我有一堆包含一些布尔列的表。我想按照每个人的真实值计算这些列。
我找到了一种使用以下方法计算列中真值的数量的方法:
SELECT count(CASE WHEN col1 THEN 1 ELSE null END) as col1,
count(CASE WHEN col2 THEN 1 ELSE null END) as col2
....
FROM my_table;
但这种方法有两个问题:
有没有办法对整个操作进行一次查询?
答案 0 :(得分:1)
这实际上不是交叉表作业(或者#34; pivot"在其他RDBMS中),而是反向操作,如果你愿意,还可以使用反交叉表。一种优雅的技术是VALUES
联接中的LATERAL
表达式。
基本查询可以如下所示,其中包含:
- 然后我必须转换结果并按值排序
醇>
SELECT c.col, c.ct
FROM (
SELECT count(col1 OR NULL) AS col1
, count(col2 OR NULL) AS col2
-- etc.
FROM tbl
) t
, LATERAL (
VALUES ('col1', col1)
, ('col2', col2)
-- etc.
) c(col, ct)
ORDER BY 2
这是一个简单的部分。你的其他要求更难:
- 我必须手动输入列的名称
醇>
此函数获取您的表名并从系统目录pg_attribute
中检索元数据。它是上述查询的动态实现,可以安全地防止SQL注入:
CREATE OR REPLACE FUNCTION f_true_ct(_tbl regclass)
RETURNS TABLE (col text, ct bigint) AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT format('
SELECT c.col, c.ct
FROM (SELECT %s FROM tbl) t
, LATERAL (VALUES %s) c(col, ct)
ORDER BY 2 DESC'
, string_agg (format('count(%1$I OR NULL) AS %1$I', attname), ', ')
, string_agg (format('(%1$L, %1$I)', attname), ', ')
)
FROM pg_attribute
WHERE attrelid = _tbl -- valid, visible, legal table name
AND attnum >= 1 -- exclude tableoid & friends
AND NOT attisdropped -- exclude dropped columns
AND atttypid = 'bool'::regtype -- only character types
);
END
$func$ LANGUAGE plpgsql;
呼叫:
SELECT * FROM f_true_ct('tbl'); -- table name optionally schema-qualified
结果:
col | ct
------+---
col1 | 3
col3 | 2
col2 | 1
适用于 任何 表,按boolean
个值计算所有true
列。
要理解函数参数,请阅读:
相关答案以及更多解释:
答案 1 :(得分:0)
如果我理解正确,您可以使用巨型union all
:
select c.*
from ((select 'col1' as which, sum(case when col1 then 1 else 0 end) as cnt from t
) union all
(select 'col2' as which, sum(case when col2 then 1 else 0 end) as cnt from t
) union all
. . .
) c
order by cnt desc;
虽然你仍然需要输入结果,但这确实会回避转置。