将列计数转换为行计数的 SQL 查询
Posted
技术标签:
【中文标题】将列计数转换为行计数的 SQL 查询【英文标题】:SQL Query to Transpose Column Counts to Row Counts 【发布时间】:2014-09-11 22:18:13 【问题描述】:我有一个如下所示的表格,其中显示了类型的数量。我需要并且一直在尝试将数据显示为 1 列和 7 行,但是……没有成功。
__________________________________________________________________________ | col types | win2k | winxp | win2k3 | vista | win7 | win8 | win8.1 | -------------------------------------------------------------------------- | count of types | 2365 | 65655 | 422445 | 4822 | 482 | 2331 | 485323 | --------------------------------------------------------------------------
Select
count(case when col1 ~* '5.0.2195' then 1 else null end) as Win2k,
count(case when col1 ~* '5.1.2600' then 1 else null end) as WinXP,
count(case when col1 ~* '5.2.3790' then 1 else null end) as W2k3,
count(case when (col1 ~* '6.0.6000'
or col1 ~* '6.0.6001' or col1 ~* '6.0.6002')
then 1 else null end) as Vista,
count(case when (col1 ~* '6.1.7600'
or col1 ~* '6.1.7601')
then 1 else null end) as Win7,
count(case when col1 ~* '6.2.9200' then 1 else null end) as Win8,
count(case when (col1 ~* '6.3.9200'
or col1 ~* '6.3.9600')
then 1 else null end) as "Win8.1"
From col1
理想情况下应该是这样的:
___________________ | types | count | ------------------- | win2k | 2365 | | winxp | 65655 | | win2k3 | 422445 | | vista | 4822 | | win7 | 482 | | win8 | 2331 | | win8.1 | 485323 | -------------------
注意事项:
我正在使用带有 PGADMIN III 的 Postgresql 9.3 我无法创建任何自定义函数 如果有更多的列来完成这项工作并不重要【问题讨论】:
unpivot and PostgreSQL 的可能重复项 我认为 Bulat 是正确的,你想要一个与 postgresql.org/docs/9.1/static/tablefunc.html 相反的(未)数据透视表 Bulat,我在发布之前查看并尝试了该参考资料,虽然它可能看起来相似,但它具有不适用的不同表/数据结构。我的单行是聚合数据,即使不是不可能,也使得 unpivot 非常困难。 当然可以。您提出的查询是非法且令人困惑的,因此很难说什么最适合您。profile.foo
在您的查询上下文中不是合法的列名。请修复问题。您可以提供表定义和一些示例值来澄清。
发布的第一个问题...仍在学习中。
【参考方案1】:
这些类型的查询更容易以 GROUP BY 为目标,如下所示:
Select
case when profile.foo ~* '5.0.2195' then 'Win2k'
when profile.foo ~* '5.1.2600' then 'WinXP'
when profile.foo ~* '5.2.3790' then 'W2k3'
when (profile.foo ~* '6.0.6000'
or profile.foo ~* '6.0.6001'
or profile.foo ~* '6.0.6002')
then 'Vista'
when (profile.foo ~* '6.1.7600'
or profile.foo ~* '6.1.7601')
then 'Win7'
when profile.foo ~* '6.2.9200' then 'Win8'
when (profile.foo ~* '6.3.9200'
or profile.foo ~* '6.3.9600')
then 'Win8.1' ELSE 'Other' END as type,
count(*) as cnt
From profile
GROUP BY 1
如下所述,此查询适用于互斥情况,即当profile.foo
包含代表每行一个操作系统的值时
【讨论】:
在 from 语句中,我删除了列名,它运行良好。谢谢! 正如对另一个(等效和更早)问题的评论,此答案仅对互斥情况有效。使用GROUP BY 1
进行简化。
@ErwinBrandstetter:感谢您的评论。我进行了反映您的评论的编辑。顺便说一句,如果 profile.foo
中有多个值,则可以使用 UNION ALL
执行计数
UNION ALL
,是的,但由于多次顺序扫描,成本要高得多。【参考方案2】:
不使用条件聚合,只需使用CASE
适当地填充Type
,然后在Type
上分组:
;with cte AS (Select case when profile.foo ~* '5.0.2195' then 'Win2k'
when profile.foo ~* '5.1.2600' then 'WinXP'
when profile.foo ~* '5.2.3790' then 'W2k3'
when profile.foo ~* '6.0.6000' or profile.foo ~* '6.0.6001' or profile.foo ~* '6.0.6002' then 'Vista'
when (profile.foo ~* '6.1.7600' or profile.foo ~* '6.1.7601') then 'Win7'
when profile.foo ~* '6.2.9200' then 'Win8'
when (profile.foo ~* '6.3.9200' or profile.foo ~* '6.3.9600') then 'Win8.1'
end as Type
From profile.foo)
SELECT Type,COUNT(*) AS ct
FROM cte
GROUP BY Type
不是 100% 使用 postgresql 语法,但逻辑是兼容的。
【讨论】:
CASE
语句每行只生成 1 个匹配项,而在原始查询中,每行可以匹配多个表达式。 (除了 Q 中的非法列名。)可能不是作者的意图,但仍可能产生不同的结果。
我运行这个并得到:错误:模式“配置文件”不存在第 9 行:来自 profile.flavor) ^ ********** 错误 ******* *** 错误:架构“配置文件”不存在 SQL 状态:3F000 字符:914
@ErwinBrandstetter 原始查询中CASE
语句之间的条件都没有重叠,因此计数将等同于我在这里所做的,就像行而不是列一样,除非语法问题,当然。
@BooneStars 我不知道是什么导致了这个错误,我对 postgresql 语法不是很熟悉。
这是一个逻辑错误。条件不必重叠。单个字符串可以匹配任意数量的正则表达式,特别是因为它们没有锚定。考虑值“5.0.2195 5.1.2600”。我现在放弃这个。有太多的漂浮物无法加起来。【参考方案3】:
我喜欢为此使用 Postgres 特定的并行 unnest()
:
SELECT unnest('win2k,winxp,win2k3,vista,win7,win8,win8.1'::text[]) AS type
,unnest(ARRAY[
count(some_column ~ '5.0.2195' OR NULL)
,count(some_column ~ '5.1.2600' OR NULL)
.. the rest from your query above ...
]) AS ct
FROM profile.foo
两个数组中值的顺序和数量必须匹配。 包含更多详细信息的相关答案(请务必阅读!):
Parallel unnest() and sort order in PostgreSQL
Is there something like a zip() function in PostgreSQL that combines two arrays?
替代计数技术(可选)
count()
只计算非空值..
(TRUE OR NULL) IS TRUE
(FALSE OR NULL) IS NULL
(NULL OR NULL) IS NULL
瞧。仅计算 TRUE
。 More details in this answer on dba.SE.
旁白:在表达式中使用~
instead of ~*
,因为这些字符串文字中没有区分大小写的字母。但我怀疑你根本不需要正则表达式匹配。
另外,列名profile.foo
没有意义,因为唯一的表名为foo
,而不是profile
【讨论】:
以上是关于将列计数转换为行计数的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章