【问题标题】:SQL multiple rows as columns (optimizing)SQL 多行作为列(优化)
【发布时间】:2023-03-23 23:27:01
【问题描述】:

我有一个 SQL 查询,它给出了正确的结果,但执行速度太慢了。

查询对以下三个表进行操作:

  • customers 包含大量客户数据,例如姓名、地址、电话 等等。为了简化表格,我只使用名称。

  • customdatas 包含某些自定义(非客户)数据。 (这 表是在软件中创建的,这就是复数形式错误的原因 对于这个表)

  • customercustomdatarels 将自定义数据与客户相关联。

客户

Id                                  Name            (many more columns)
-----------------------------------------------------------------------
8053c6f4c5c5c631054ddb13d9186117    MyCustomer      ...
2efd2aa5711ddfade1f829b12dd88cf3    CheeseFactory   ...

自定义数据

id                                  key
-------------------------------------------------
22deb172c1af6e8e245634a751871564    favoritsport
86eea84d296df9309ad6ff36fd7f856e    favoritcheese

customercustomdatarels(客户和自定义数据之间的关系 - 具有相应的值)

customer                            customdata                          value
-------------------------------------------------------------------------------------
8053c6f4c5c5c631054ddb13d9186117    22deb172c1af6e8e245634a751871564    cycling
8053c6f4c5c5c631054ddb13d9186117    86eea84d296df9309ad6ff36fd7f856e    cheddar
2efd2aa5711ddfade1f829b12dd88cf3    22deb172c1af6e8e245634a751871564    football
2efd2aa5711ddfade1f829b12dd88cf3    86eea84d296df9309ad6ff36fd7f856e    mouldy

我想要的是一个基本上由customers 中的所有数据组成的表,其中包含可变数量的额外列,对应于customercustomdatarels 中指定的自定义数据。 这些列应该在某个地方定义,因此我创建了下表,它定义了这些额外的列并将它们映射到customdata 表中的一个键:

test_customkeymapping

colkey  customkey
---------------------
1       favoritsport
2       favoritcheese

结果应该是:

Name            ExtraColumn_1   ExtraColumn_2
---------------------------------------------
CheeseFactory   football        mouldy
MyCustomer      cycling         cheddar

(因此,ExtraColumn_1 是客户最喜欢的运动的同义词,ExtraColumn_2 是客户最喜欢的奶酪的同义词。)

这个结果是通过执行以下查询来实现的:

SET @sql = NULL;

SELECT
    GROUP_CONCAT(DISTINCT
        CONCAT('MAX(CASE
                WHEN ckm.colkey = ', colkey, ' THEN
                    (SELECT value FROM customercustomdatarels ccdr2
                     LEFT JOIN customdatas cd2
                       ON cd2.id = ccdr2.customdata
                     WHERE cd2.key = ckm.customkey AND c.Id = ccdr2.customer)
                END) AS ', CONCAT('`ExtraColumn_', colkey, '`'))
    ) INTO @sql
FROM test_customkeymapping;

SET @sql = CONCAT('SELECT c.Name, ', @sql, ' 
                   FROM customers c
                   LEFT JOIN customercustomdatarels ccdr
                     ON c.Id = ccdr.customer
                   LEFT JOIN customdatas cd
                     ON cd.Id = ccdr.customdata
                   LEFT JOIN test_customkeymapping ckm 
                     ON cd.key = ckm.customkey
                   GROUP BY c.Id');

PREPARE stmt FROM @sql;
EXECUTE stmt;

这行得通。但是太慢了(对于 7000 名客户,它需要大约 10 秒)。 查询受到此问题中的解决方案的极大影响: MySQL Join Multiple Rows as Columns

如何优化这个查询?

【问题讨论】:

  • 想要这样的东西似乎是初学者的错误。通常您应该简单地接受列数是静态的。如果您想要动态数量的列,则必须动态生成查询,这对性能来说确实很糟糕。
  • 很难(如果不是不可能的话)想出一个有用的合理场景。您似乎正在尝试在数据库中做一些您应该在应用程序中做的事情。

标签: mysql sql query-optimization


【解决方案1】:

我不明白您为什么在 group_concat() 语句中使用子查询。这不会生成您真正想要运行的代码吗?

SET @sql = NULL;

SELECT
    GROUP_CONCAT(DISTINCT
        CONCAT('MAX(CASE WHEN ckm.colkey = ', colkey, ' THEN ccd.value END) AS ',  
               CONCAT('ExtraColumn_', colkey, ''))
    ) INTO @sql
FROM test_customkeymapping;

SET @sql = CONCAT('SELECT c.Name, ', @sql, ' 
                   FROM customers c
                   LEFT JOIN customercustomdatarels ccdr
                     ON c.Id = ccdr.customer
                   LEFT JOIN customdatas cd
                     ON cd.Id = ccdr.customdata
                   LEFT JOIN test_customkeymapping ckm 
                     ON cd.key = ckm.customkey
                   GROUP BY c.Id');

PREPARE stmt FROM @sql;
EXECUTE stmt;

注意:这是未经测试的,但想法是相同的。为您的工作使用主 from 语句中的值,而不是一些额外的、不必要的子查询中的值。

【讨论】:

  • 谢谢。这无疑简化了查询。我不知道为什么当时我看不到子查询是不必要的。然而,执行时间几乎相同。
  • @CleanUp 。 . . join 键上有索引吗?您的数据也可能足够大,以至于group by 需要时间。在您的情况下,这将是由于列宽而不是行数。
  • 有很多数据,但关键问题是您指出的某些键缺少一些索引。我认为它会表现得足够好——至少现在是这样。非常感谢。
猜你喜欢
  • 1970-01-01
  • 2021-03-20
  • 1970-01-01
  • 2023-03-04
  • 2023-03-14
  • 2018-10-30
  • 2020-10-30
  • 2014-11-11
  • 1970-01-01
相关资源
最近更新 更多