SQL:将宽表转换为窄表

Posted

技术标签:

【中文标题】SQL:将宽表转换为窄表【英文标题】:SQL: Convert a Wide Table to Narrow Table 【发布时间】:2020-08-31 07:56:51 【问题描述】:

我有一个表格,其中每一行都有一些布尔字段,如下所示:

user_id          | USD | EUR | CAD | ....
               1 |   1 |   1 |   0 | ....
               2 |   0 |   1 |   1 | ....

我需要把它转换成这样的:

user_id          | currency
               1 | USD
               1 | EUR
               2 | EUR
               2 | CAD

我设法用大量的 UNION(每种货币一个)构造了一个丑陋的 SQL,但我觉得这不是最好的方法。帮助任何人?

附: 此查询将在 AWS Athena 上执行,因此我担心成本,我希望为此提供最优化的查询。

【问题讨论】:

您使用的是哪种 DBMS 产品? “SQL”只是所有关系数据库都使用的一种查询语言,而不是特定数据库产品的名称。请为您正在使用的数据库产品添加tag。 Why should I tag my DBMS 如果您的数据库支持它,这似乎是一个 UNPIVOT 操作。 我不认为这样的查询很难看。它很长,但谁在乎呢? UNION ALL 在这里是合适的,并导致一个可读的查询,尽管查询很长。为什么不使用它? 【参考方案1】:

如果您的数据库支持横向连接和values() 行构造函数,那么您可以这样做:

select x.user_id, x.currency
from mytable t
cross join lateral (values(user_id, 'USD', usd), (user_id, 'EUR', eur), (user_id, 'CAD', cad)) x(user_id, currency, val)
where x.val= 1

一些数据库使用cross apply而不是cross join lateral实现横向连接。

更便携的方法是union all。这效率较低,因为它需要多次表扫描:

select user_id, 'USD'currency from mytable where usd = 1
union all select user_id, 'EUR' from mytable where eur = 1
union all select user_id, 'CAD' from mytable where cad = 1

【讨论】:

那应该是select user_id, 'USD' as currency from mytable where usd = 1 ...(即'USD'作为一个字符串)。第一个查询需要类似的修复。

以上是关于SQL:将宽表转换为窄表的主要内容,如果未能解决你的问题,请参考以下文章

R语言tidyr包spread()函数实战详解:数据裂变从窄表到宽表

宽表和窄表的建设该如何选择?

杂记宽表/窄表—事实表/维度表—数仓分层(ODS/CDM/ADS)—增量表/全量表——电信行业常见指标

如何将宽数据帧转换为长数据帧

将宽格式转换为长格式,然后嵌套列

将宽数据帧转换为具有特定条件并添加新列的长数据帧