【问题标题】:Rank columns by their count of values按值的计数对列进行排名
【发布时间】:2016-08-03 18:14:11
【问题描述】:

我有一张带有一堆布尔列的表。我想根据每个列的真实值计数对这些列进行排名。

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

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

有没有办法做一个查询的整个操作?

【问题讨论】:

  • 可能crosstab 会处理它。请提供sqlfiddle.com 样本数据和所需的结果集
  • @lad2025 和crosstab 您还必须手动输入列的名称。除非您使用动态语句,这与 OP 呈现的方式基本相同(就结果而言)
  • 编辑您的问题并提供您想要的结果
  • 那么你有答案了吗?

标签: sql postgresql dynamic-sql crosstab


【解决方案1】:

这实际上不是交叉表作业(或其他 RDBMS 中的“枢轴”),而是反向操作,如果您愿意的话,是一个反交叉表。一种优雅的技术是在LATERAL 连接中使用VALUES 表达式。

基本查询可能如下所示,它负责:

  1. 然后我必须转置结果并按值排序
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. 我必须手动输入列的名称

此函数获取您的表名并从系统目录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

适用于任何表,以按true 值的计数对所有boolean 列进行排名。

要了解函数参数,请阅读以下内容:

更多解释的相关答案:

【讨论】:

  • LATERAL and VALUES :) 我一直想知道为什么人们要模拟 SELECT * 的行为
【解决方案2】:

如果我理解正确的话,你可以用巨人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;

虽然您仍然需要输入结果,但这确实避开了换位。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-15
    • 2011-11-12
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    相关资源
    最近更新 更多