如何在 SQL 中将列分组为单独的行
Posted
技术标签:
【中文标题】如何在 SQL 中将列分组为单独的行【英文标题】:How to group columns into separate rows in SQL 【发布时间】:2017-10-01 18:30:57 【问题描述】:我没有深厚的 SQL 背景,最近遇到了一个用 JUST SQL 似乎很难解决的 SQL 问题。
我有一张桌子
```
IMEI | DATE | A_1 | A_2 | A_3 | B_1 | B_2 | B_3
2132 | 09/21| 2 | 4 | 4 | 5 | 2 | 4
4535 | 09/22| 2 | 2 | 4 | 5 | 2 | 3
9023 | 09/21| 2 | 1 | 5 | 7 | 2 | 2
```
如何以某种方式对 A_1
、A_2
等的值进行分组,以便我可以实现此表。基本上,我想将表格中的某些列分组,并将它们放入不同的行中。
IMEI | DATE | MODULE | val_1 | val_2 | val_3
2132 | 09/21| A | 2 | 4 | 4
2132 | 09/21| B | 5 | 2 | 4
...
目标是在命名空间A
、B
等下具有值,以便将一行分隔为新表中的不同行。
另外,关于我可以在哪里改进我的 SQL 的任何建议。我应该保留哪些书籍作为参考或任何其他我应该使用的资源?
谢谢!
【问题讨论】:
"A_1、A_2 等的值" - 这是关键问题,等是什么意思。例如可以是 ZZZ_898 吗? 你使用的是 mysql 还是 bigquery? 任何时候你发现自己有枚举列,警钟应该开始响起etc
只是意味着,列是命名空间。例如A_n, B_n, C_n .. N_n
【参考方案1】:
您可以使用UNION
:
SELECT IMEI, DATE, 'A' AS MODULE, A_1 AS val_1, A_2 AS val_2, A_3 AS val_3
FROM myTable
UNION ALL
SELECT IMEI, DATE, 'B', B_1, B_2, B_3
FROM myTable
在sqlfiddle 上查看。
但实际上,您应该将数据存储在上述查询创建的表单中,然后在需要时使用JOIN
创建原始格式。
【讨论】:
感谢您的想法。我想,我不应该一开始就列举我的专栏。但这个 UNION 有效。【参考方案2】:我喜欢玩这样的数据和问题! 以下可以被认为是过度设计,但我认为当您事先不知道列名但有您描述的模式时,它仍然是一个选择,或者它可能对学习有用,因为看起来您正在寻找改进您的SQL(基于这个问题的标签,我假设您的意思是 BigQuery SQL)
#standardSQL
WITH parsed AS (
SELECT IMEI, DATE,
REGEXP_REPLACE(SPLIT(row, ':')[OFFSET(0)], r'^"|"$', '') key,
REGEXP_REPLACE(SPLIT(row, ':')[OFFSET(1)], r'^"|"$', '') value
FROM `yourTable` t,
UNNEST(SPLIT(REGEXP_REPLACE(to_json_string(t), r'[]', ''))) row
),
grouped AS (
SELECT
IMEI, DATE,
REGEXP_EXTRACT(key, r'(.*)_') MODULE,
ARRAY_AGG(value ORDER BY CAST(REGEXP_EXTRACT(key, r'_(.*)') AS INT64)) AS vals
FROM parsed
WHERE key NOT IN ('IMEI', 'DATE')
GROUP BY IMEI, DATE, MODULE
)
SELECT IMEI, DATE, MODULE,
vals[SAFE_OFFSET(0)] AS val_1,
vals[SAFE_OFFSET(1)] AS val_2,
vals[SAFE_OFFSET(2)] AS val_3,
vals[SAFE_OFFSET(3)] AS val_4
FROM grouped
-- ORDER BY IMEI, DATE, MODULE
您可以测试/使用问题中的虚拟数据
#standardSQL
WITH `yourTable` AS (
SELECT 2132 IMEI, '09/21' DATE, 2 A_1, 4 A_2, 4 A_3, 5 B_1, 2 B_2, 4 B_3 UNION ALL
SELECT 4535, '09/22', 2, 2 ,4, 5, 2, 3 UNION ALL
SELECT 9023, '09/21', 2, 1 ,5, 7, 2, 2
),
parsed AS (
SELECT IMEI, DATE,
REGEXP_REPLACE(SPLIT(row, ':')[OFFSET(0)], r'^"|"$', '') key,
REGEXP_REPLACE(SPLIT(row, ':')[OFFSET(1)], r'^"|"$', '') value
FROM `yourTable` t,
UNNEST(SPLIT(REGEXP_REPLACE(to_json_string(t), r'[]', ''))) row
),
grouped AS (
SELECT
IMEI, DATE,
REGEXP_EXTRACT(key, r'(.*)_') MODULE,
ARRAY_AGG(value ORDER BY CAST(REGEXP_EXTRACT(key, r'_(.*)') AS INT64)) AS vals
FROM parsed
WHERE key NOT IN ('IMEI', 'DATE')
GROUP BY IMEI, DATE, MODULE
)
SELECT IMEI, DATE, MODULE,
vals[SAFE_OFFSET(0)] AS val_1,
vals[SAFE_OFFSET(1)] AS val_2,
vals[SAFE_OFFSET(2)] AS val_3,
vals[SAFE_OFFSET(3)] AS val_4
FROM grouped
ORDER BY IMEI, DATE, MODULE
输出如下
Row IMEI DATE MODULE val_1 val_2 val_3 val_4
1 2132 09/21 A 2 4 4 null
2 2132 09/21 B 5 2 4 null
3 4535 09/22 A 2 2 4 null
4 4535 09/22 B 5 2 3 null
5 9023 09/21 A 2 1 5 null
6 9023 09/21 B 7 2 2 null
【讨论】:
以上是关于如何在 SQL 中将列分组为单独的行的主要内容,如果未能解决你的问题,请参考以下文章