如何在 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_1A_2 等的值进行分组,以便我可以实现此表。基本上,我想将表格中的某些列分组,并将它们放入不同的行中。

IMEI | DATE | MODULE | val_1 | val_2 | val_3
2132 | 09/21| A      |  2    | 4     | 4   
2132 | 09/21| B      |  5    | 2     | 4   
...

目标是在命名空间AB 等下具有值,以便将一行分隔为新表中的不同行。

另外,关于我可以在哪里改进我的 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 中将列分组为单独的行的主要内容,如果未能解决你的问题,请参考以下文章

如何将单独列中冒号前后的单词拆分为sql中的行

sql - 如何使用单独的逗号对列进行分组

如何在ClickHouse中将具有相同列值的mysql行分组为一行?

在 SQL 中将分隔的行拆分为列

我们如何在 SQL 中将连续数据分组为单个日期跨度?

SQL查询如何对增量键RANK中的相同结果进行单独分组