是否可以提高具有不同连接和多个连接的查询的性能?

Is it possible to improve the performance of query with distinct and multiple joins?

有以下查询:

SELECT DISTINCT ID, ACCOUNT,
   CASE
       WHEN p.GeneralLevel = '1' THEN '1'
       WHEN p.Level3 IS NULL THEN '2'
       WHEN p.Level4 IS NULL THEN '3'
       WHEN p.Level5 IS NULL THEN '4'
       WHEN p.Level6 IS NULL THEN '5'
       WHEN p.Level7 IS NULL THEN '6'
       WHEN p.Level8 IS NULL THEN '7'
       ELSE '8'
   END AS LEVEL,
   CASE
       WHEN c.codeValueDescription IS NULL THEN p.Level2
       ELSE c.codeValueDescription
   END AS L2_CODE,
   CASE
       WHEN d.codeValueDescription IS NULL THEN p.Level3
       ELSE d.codeValueDescription
   END AS L3_CODE,
   CASE
       WHEN j.codeValueDescription IS NULL THEN p.Level4
       ELSE j.codeValueDescription
   END AS L4_CODE,
   CASE
       WHEN f.codeValueDescription IS NULL THEN p.Level5
       ELSE f.codeValueDescription
   END AS L5_CODE,
   CASE
       WHEN g.codeValueDescription IS NULL THEN p.Level6
       ELSE g.codeValueDescription
   END AS L6_CODE,
   CASE
       WHEN h.codeValueDescription IS NULL THEN p.Level7
       ELSE h.codeValueDescription
   END AS L7_CODE,
   p.Level8
   FROM generic p
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '2') c ON p.Level2 = c.codeValue
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '3') d ON p.Level3 = d.codeValue
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '4') j ON p.Level4 = j.codeValue
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '5') f ON p.Level5 = f.codeValue
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '3') g ON p.Level6 = g.codeValue //yes, code is 3 again
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '3') h ON p.Level7 = h.codeValue //and yes, again code 3 here

table 'generic' 的一些列(排除日期和其他对我们不重要的列):

ID INTEGER NOT NULL,
ACCOUNT VARCHAR(50) NOT NULL,
GeneralLevel1 VARCHAR(50),
Level2 VARCHAR(50),
Level3 VARCHAR(50),
Level4 VARCHAR(50),
Level5 VARCHAR(50),
Level6 VARCHAR(50),
Level7 VARCHAR(50),
Level8 VARCHAR(50)

简单数据:

ID,ACCOUNT_ID,LEVEL_1,LEVEL_2,...LEVEL_8
id1,ACCOUNT_ID1,GENERAL,null,...null
id1,ACCOUNT_ID2,GENERAL,A,...null
id1,ACCOUNT_ID2,GENERAL,B,...null
id2,ACCOUNT_ID1,GENERAL,null,...null
id2,ACCOUNT_ID2,GENERAL,A,...null
id2,ACCOUNT_ID3,GENERAL,B,...H

当前查询运行超过1s,通常returns在100到1000条记录之间,我想提高这个查询的性能。我们的想法是摆脱这些 LEFT JOINS 并以某种方式重写此查询以提高性能。

也许有办法改进此查询以更快地获取数据?我希望我在这里提供了足够的信息。数据库是自定义的,NO_SQL 引擎盖下的巨人,但我们数据库桥的语法与 MySQL 非常相似。不幸的是,我无法提供此查询的执行计划,因为它在服务器端处理,然后生成一些我无法访问的 SQL。

您正在从 codes table 中进行 key/value 查找。您的查询包含多个 LEFT JOIN 模式。

   FROM generic p
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '2') c ON p.Level2 = c.codeValue
   LEFT JOIN
     (SELECT codeValue, codeValueDescription
      FROM codes
      WHERE code = '3') d ON p.Level3 = d.codeValue

可以重构这些 LEFT JOIN 以消除子查询。这种重构可能会更清楚地向 SQL 系统表明您的意图。结果看起来像这样。

   FROM generic p
   LEFT JOIN codes c ON  p.Level2 = c.codeValue AND c.code = '2'
   LEFT JOIN codes d ON  p.Level3 = d.codeValue AND d.code = '3'

如果您的 SQL 系统允许索引,那么在您的 codes table 上使用这样的覆盖索引将有助于加快您的 key/value 查找。

ALTER TABLE codes ADD INDEX (codeValue, code, codeValueDescription)

您的 SELECT 子句包含很多此类内容:

   CASE
       WHEN c.codeValueDescription IS NULL THEN p.Level2
       ELSE c.codeValueDescription
   END AS L2_CODE,
   CASE
       WHEN d.codeValueDescription IS NULL THEN p.Level3
       ELSE d.codeValueDescription
   END AS L3_CODE

它可能没有多大帮助,但这可以通过重写为

来简化
   COALESCE(c.codeValueDescription, p.Level2) AS L2_CODE,
   COALESCE(d.codeValueDescription, p.Level3) AS L3_CODE

如果删除 DISTINCT 限定符会怎样?这可能需要一些处理时间。如果您的 generic.ID 列是主键,那么 DISTINCT 对您一点好处都没有:这些列值不会重复。 (大多数现代 SQL 查询规划器检测到这种情况并跳过重复数据删除步骤,但我们不知道您的查询规划器有多现代。)

您的查询不包含整体 WHERE 子句,因此它必须处理 generic table 中的每一行。而且,如果 table 很大,您的结果集也会很大。我相信您知道,扫描整个 tables 需要时间和资源。

话虽这么说,通过 SQL 网桥进行这样的查询,每行一毫秒并不是确凿证据的可怕表现。你可能不得不忍受它。另一种方法可能是将代码应用于您的应用程序中的数据:吞噬整个 codes table 然后编写一些应用程序逻辑来执行您的 CASE / WHEN / THEN 或 COALESCE 工作。换句话说,将 LEFT JOIN 操作移至您的应用程序。如果您的 SQL 网桥能够快速处理非常简单的 SELECT * FROM generic 单个 table 查询,这将有很大帮助。