按列的值排列列

时间:2016-04-12 22:15:58

标签: sql postgresql dynamic-sql crosstab

我有一堆包含一些布尔列的表。我想按照每个人的真实值计算这些列。

我找到了一种使用以下方法计算列中真值的数量的方法:

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;

但这种方法有两个问题:

  1. 我必须手动输入列的名称
  2. 然后我必须转换结果并按值排序
  3. 有没有办法对整个操作进行一次查询?

2 个答案:

答案 0 :(得分:1)

这实际上不是交叉表作业(或者#34; pivot"在其他RDBMS中),而是反向操作,如果你愿意,还可以使用反交叉表。一种优雅的技术是VALUES联接中的LATERAL表达式。

基本查询可以如下所示,其中包含:

  
      
  1. 然后我必须转换结果并按值排序
  2.   
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

这是一个简单的部分。你的其他要求更难:

  
      
  1. 我必须手动输入列的名称
  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;

虽然你仍然需要输入结果,但这确实会回避转置。