【发布时间】:2021-10-27 07:32:09
【问题描述】:
我想从下表数据中计算“是”和“否”,而不考虑列。
Oracle 数据库表:
预期输出:
它来自哪一列并不重要。我只想计算是和否的总数。 我尝试使用 pivot 但找不到如何将其用于多个列的方法。
请帮忙。
【问题讨论】:
-
我想我找到了一种更通用的方法。查看我的更新答案。
我想从下表数据中计算“是”和“否”,而不考虑列。
Oracle 数据库表:
预期输出:
它来自哪一列并不重要。我只想计算是和否的总数。 我尝试使用 pivot 但找不到如何将其用于多个列的方法。
请帮忙。
【问题讨论】:
如果它必须是动态的,那么......它必须是动态的,这意味着PL/SQL 是一个选项来做你想做的事。这是一个例子:
循环遍历名称作为参数传递的表中的所有列并计算作为第二个参数传递的值的数量的函数:
SQL> create or replace function f_yes_no
2 (par_table_name in varchar2, par_yes_no in varchar2)
3 return number
4 is
5 l_cnt number;
6 retval number := 0;
7 begin
8 for cur_r in (select column_name from user_tab_columns
9 where table_name = dbms_assert.sql_object_name(par_table_name)
10 and data_type like '%CHAR%'
11 )
12 loop
13 execute immediate
14 'select count(*) from ' || dbms_assert.sql_object_name(par_table_name) ||
15 ' where ' || cur_r.column_name || ' = ' ||
16 dbms_assert.enquote_literal(par_yes_no)
17 into l_cnt;
18 retval := retval + l_cnt;
19 end loop;
20 return retval;
21 end;
22 /
Function created.
SQL>
测试:样本表:
对于示例表:
SQL> select * from test;
ZING PLING COL3 BOING
----- ----- ----- -----
Yes No Yes No
Yes No No No
No No Yes No
SQL> select f_yes_no('TEST', 'Yes') cnt_yes,
2 f_yes_no('TEST', 'No' ) cnt_no
3 from dual;
CNT_YES CNT_NO
---------- ----------
4 8
SQL>
它有什么好处?您可以将该函数重用于另一个表中的其他字符串,例如Scott 的EMP 表中有多少个MANAGER?
SQL> select ename, job from emp order by job;
ENAME JOB
---------- ---------
SCOTT ANALYST
FORD ANALYST
MILLER CLERK
JAMES CLERK
SMITH CLERK
ADAMS CLERK
BLAKE MANAGER
JONES MANAGER
CLARK MANAGER
KING PRESIDENT
TURNER SALESMAN
MARTIN SALESMAN
WARD SALESMAN
ALLEN SALESMAN
14 rows selected.
SQL> select f_yes_no('EMP', 'MANAGER') cnt_mgr from dual;
CNT_MGR
----------
3
SQL>
【讨论】:
UNPIVOT 子句?
SELECT
SUM(DECODE(v, 'Yes', 1)) "Yes",
SUM(DECODE(v, 'No', 1)) "No"
FROM t
UNPIVOT (
v FOR value_type IN (c1, c2, c3, c4, c5)
);
更通用的方式:
SELECT
SUM(DECODE(yes_no, 'Yes', 1)) "Yes",
SUM(DECODE(yes_no, 'No', 1)) "No"
FROM (
SELECT
x.yes_no.EXTRACT(c.column_name || '/text()').getStringVal() yes_no
FROM
XMLTABLE ('/ROWSET/ROW'
PASSING XMLTYPE(dbms_xmlgen.getxml('SELECT * FROM t'))
COLUMNS
yes_no XMLTYPE PATH '*[text() = ("Yes", "No")]') x
CROSS JOIN user_tab_columns c
WHERE
c.table_name = 'T' AND
column_name LIKE 'C%'
) t;
【讨论】:
一种方法是使用横向连接:
select sum(case when col = 'Yes' then 1 else 0 end) as num_yes,
sum(case when col = 'No' then 1 else 0 end) as num_no
from t cross join lateral
(select t.column1 as col from dual union all
select t.column2 from dual union all
select t.column3 from dual union all
select t.column4 from dual union all
select t.column5 from dual
) t
【讨论】: