了解具有许多子查询的大型复杂SQL查询的最佳方法

时间:2016-05-20 05:08:40

标签: sql db2 subquery

我刚开始在新公司的新项目中开始。

我获得了一个庞大而复杂的SQL,包含大约1000行和多个子查询,连接,总和,分组等。

此SQL用于生成报告(它没有插入或更新)。

SQL存在一些缺陷,我在公司的第一份工作是识别并纠正这些缺陷,以便报告显示正确的值(我通过访问用Cobol编写的遗留系统知道正确的值...)< / p>

如何让我更容易理解查询,以便识别缺陷?

作为一名经验丰富的Java程序员,我知道如何使用一小段代码将复杂糟糕的书面单一Java代码重构为易于理解的代码。但我不清楚如何用SQL做到这一点。

SQL看起来像这样:

SELECT columns
FROM
    (SELECT columns
    FROM
        (SELECT DISTINCT columns
              FROM table000 alias000
              INNER JOIN                                          
                      table000 alias000             
               ON column000 = table000.column000

              LEFT JOIN
                 (SELECT columns
                    FROM (
                    SELECT DISTINCT columns  
                      FROM columns        
                     WHERE conditions) AS alias000
                        GROUP BY columns ) alias000
                   ON
                    conditions
             WHERE conditions
            ) AS alias000
                 LEFT JOIN
                  (SELECT
                    columns  
                    FROM many_tables                
     WHERE many_conditions 
            ) )
        ) AS alias000
     ON condition               
       LEFT JOIN (      
    SELECT columns
    FROM            
    (SELECT
      columns
    FROM                                                   
       many_tables       
     WHERE many_conditions
            ) ) ) AS alias001  
        ,
         (SELECT
           many_columns 
         FROM                                                   
            many_tables            
           WHERE many_conditions) AS alias001
            ) AS alias001
        ON condition
    LEFT JOIN 
        (SELECT                                     
         many_columns
       FROM many_tables               
       WHERE many_conditions
          ) AS alias001
        ON condition
        ,    
         (SELECT  DISTINCT columns
           FROM table001 alias001
           INNER JOIN                                          
                 table001 alias001              
           ON condition
            LEFT JOIN
            (SELECT columns 
            FROM (                                           
         SELECT  DISTINCT columns
          FROM tables        
          WHERE conditions
         ) AS alias001
        GROUP BY                                                  
             columns ) alias001
            ON
             condition
             WHERE                                                
                 conditions
         ) AS alias001
         LEFT JOIN
         (SELECT columns
            FROM tables            
            WHERE conditions
              ) AS alias001
            ON condition
            LEFT JOIN ( 
                 SELECT columns
             FROM
             (SELECT columns
              FROM tables               
              WHERE conditions ) AS alias001
                    ,
                    (SELECT
                        columns
                 FROM                                                   
                   tables
                 WHERE conditions ) AS alias001
                ) AS alias001
                ON condition
                 LEFT JOIN 
                                (SELECT                                     
                   columns
                 FROM                                                   
                   tables
                 WHERE conditions
                     ) AS alias001
                   ON condition
    WHERE 
    condition
    ) AS alias001
    order by column001

如何让我更容易理解查询,以便识别缺陷?

5 个答案:

答案 0 :(得分:1)

从中间工作在SQL中是常见的,并且转换基于集合的sql逻辑,因为顺序逻辑可能导致性能问题。尽量避免这种情况,但我知道这样做非常诱人。

我要做的第一件事就是质疑连接语法。这是字面上现在写的方式吗?

            select
            from tb1, tb2, tb3, tb4, tb5 ...
            left join ...

那个from子句看起来应该是这样的

 From tb1
 Inner join tb2 on .....
 Inner join tb3 on .....
   ....
 Left join

答案 1 :(得分:1)

http://www-03.ibm.com/software/products/en/data-studio

IBM提供了一个基于Eclipse的分析工具,它能够为复杂查询生成Visual EXPLAIN图。它显示了如何使用索引,生成和组合了哪些内部结果集等。

实施例: SELECT * FROM EMPLOYEE, DEPARTMENT WHERE WORKDEPT=DEPTNO Visual Explain

答案 2 :(得分:1)

我每天处理这样的代码,因为我们在这里进行了大量的复杂数据报告和导出。

第一步是了解您正在做的事情的意义。如果您不理解其含义,则无法评估您是否获得了正确的结果。因此,要准确了解您要完成的工作,并查看是否可以在用户界面中看到您应该看到的结果。将某些内容进行比较确实很有帮助,这样您就可以在查询时看到添加新内容如何更改结果。如果您的查询使用单个字母或其他对派生表别名无意义的东西,那么当您弄清楚派生表应该执行的含义时,请将别名替换为更有意义的内容,例如Employees而不是A.将使下一个使用它的人更容易在以后解码它。

然后你做的是从最里面的派生表开始(如果你愿意,可以使用子查询,但是当它被用作表时,术语派生表更准确)。首先弄清楚它应该做什么。例如,可能是所有员工的绩效评估都不尽如人意。

运行它并根据您正在做的事情的含义检查结果以查看它们是否正确。例如,如果您正在寻找不满意的评估并且您拥有10,000名员工,那么5617对于该数据块来说似乎是一个合理的结果集?寻找重复记录。如果同一个人在那里三次,那么你可能会遇到一个问题,你加入一对多,当你只想要一个人时,你会有很多人回来。这可以通过使用聚合函数和分组依据或将另一个派生表放入以替换问题联接来解决。

一旦你将最里面的部分清除了,然后开始检查其他其他派生表的结果,重新添加代码并检查结果,直到找到不应该有的记录丢失的位置(嘿我有137名员工)在这个阶段,现在我只有116.是什么造成的?)请记住,这只是一个线索,看看为什么会发生这种情况。当基本结果发生变化时,有时候会构建一个复杂的查询,有时候它们应该没有,这就是为什么理解数据的含义是至关重要的。

一般要注意的一些事项:

  • 如何处理空值会影响结果
  • 混合implict和explict连接可能会导致某些不正确的结果 数据库。
  • 无论如何,您应该始终用所有隐式连接替换 明确的。这使代码更清晰,更不可能拥有 错误。
  • 如果您有隐式连接,请查找意外的交叉连接。他们是 即使在简短的查询中也很容易引入,在复杂的查询中,它们 更有可能这就是为什么隐式连接永远不应该是 使用。
  • 如果你有左联接,请留意他们得到的地方 通过放置where子句意外地转换为内连接 左连接表(除了id是否为null)。所以这 结构是一个问题:

       FROM table1 t1
       LEFT JOIN Table2 t2 ON t1.t1id = T2.t1id
       WHERE t2.somefield = 'test'
    

    应该是

       FROM table1 t1
       LEFT JOIN Table2 t2 ON t1.t1id = T2.t1id
          AND t2.somefield = 'test'
    

答案 3 :(得分:1)

解决方案是使用COMMON TABLE EXPRESSIONS简化查询。

这使我能够将大而复杂的SQL查询分解为许多小而易于理解的查询。

COMMON TABLE EXPRESSIONS:

  • 可用于分解复杂查询,尤其是复杂的连接和子查询
  • 是一种封装查询定义的方法。
  • 仅在下一个查询运行之前保留。
  • 正确使用可以提高代码质量/可维护性和速度。
  • 可用于在同一语句中多次引用结果表(消除SQL中的重复)。
  • 当不需要一般使用视图时,可以替代视图;也就是说,您不必将定义存储在元数据中。

实施例

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

我的新SQL看起来像这样:

------------------------------------------
--COMMON TABLE EXPRESSION 001--
------------------------------------------
WITH alias001 (column001, column002) AS (
    SELECT column005, column006
    FROM table001
    WHERE condition001
    GROUP by column008
)

--------------------------------------------
--COMMON TABLE EXPRESSION 002 --
--------------------------------------------
, alias002 (column009) as (
    select distinct column009 from table002
)

--------------------------------------------
--COMMON TABLE EXPRESSION 003 --
--------------------------------------------
, alias003 (column1, column2, column3) as (
    SELECT '1' AS column1, '1' as column2, 'name001' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '1' AS column1, '1.1' as column2, 'name002' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '1' AS column1, '1.2' as column2, 'name003' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '2' AS column1, '2' as column2, 'name004' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '2' AS column1, '2.1' as column2, 'name005' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '2' AS column1, '2.2' as column2, 'name006' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '3' AS column1, '3' as column2, 'name007' AS column3 FROM SYSIBM.SYSDUMMY1
    UNION ALL
    SELECT '3' AS column1, '3.1' as column2, 'name008' AS column3 FROM SYSIBM.SYSDUMMY1
)
--------------------------------------------
--COMMON TABLE EXPRESSION 004 --
--------------------------------------------
, alias004 (column1) as (
    select distinct column1 from table003
)

------------------------------------------------------
--COMMON TABLE EXPRESSION 005 --
------------------------------------------------------
, alias005 (column1, column2) as (
    select column1, column2 from alias002, alias004
)

------------------------------------------------------
--COMMON TABLE EXPRESSION 006 --
------------------------------------------------------
, alias006 (column1, column2, column3, column4) as (
    SELECT column1, column2, column3, sum(column0) as column4
    FROM table004
    LEFT JOIN table005 ON column01 = column02
    group by column1, column2, column3
)

------------------------------------------------------
--COMMON TABLE EXPRESSION 007 --
------------------------------------------------------
, alias007 (column1, column2, column3, column4) as (
    SELECT column1, column2, column3, sum(column0) as column4
    FROM table006
    LEFT JOIN table007 ON column01 = column02
    group by column1, column2, column3
)

------------------------------------------------------
--COMMON TABLE EXPRESSION 008 --
------------------------------------------------------
, alias008 (column1, column2, column3, column4) as (
    select column1, column2, column3, column4 from alias007 where column5 = 123
)

----------------------------------------------------------
--COMMON TABLE EXPRESSION 009 --
----------------------------------------------------------
, alias009 (column1, column2, column3, column4) as (
    select column1, column2, 
    CASE WHEN column3 IS NOT NULL THEN column3 ELSE 0 END as column3, 
    CASE WHEN column4 IS NOT NULL THEN column4 ELSE 0 END as column4
    from table007
)

----------------------------------------------------------
--COMMON TABLE EXPRESSION 010 --
----------------------------------------------------------
, alias010 (column1, column2, column3) as (
    select column1, sum(column4), sum(column5) 
    from alias009 
    where column6 < 2005 
    group by column1
)

--------------------------------------------
--             MAIN QUERY            --
--------------------------------------------

select j.column1, n.column2, column3, column4, column5, column6, 
column3 + column5 AS column7,
column4 + column6 AS column8
from alias010 j
left join alias006 m ON (m.column1 = j.column1)
left join alias008 n ON (n.column1 = j.column1)

答案 4 :(得分:0)

编辑:我对这个答案投了反对意见,可能是因为他们认为我建议你如何构建最终查询。我应该澄清,这纯粹是为了试图了解正在发生的事情。一旦理解了子查询以及它们如何链接在一起,您就可以使用该知识对查询进行必要的更改并以有效的方式重建它。

我使用了中间临时表的技术来解决复杂的查询问题。它们将逻辑分解为更小的块,如果原始查询需要很长时间,它们也很有用。您可以测试如何组合这些中间表,而无需重新运行整个查询的开销。有时我会使用临时视图而不是临时表,因为查询优化器可以继续使用基表上的索引。一旦完成,临时视图就会被删除。

我会从最里面的子查询开始,然后一直到外面。 你正在寻找在略微不同的伪装下出现几次的子查询,并且还要给它们一个简洁的描述 - 它们的目的是什么?

例如,替换

        from (
            select x1.y1, x1.y2, x1.y3 ...
            from tb1, tb2, tb3, tb4, tb5 ...
            left join ...
            where ...
            group by ...
            ) as a1

from daniel_view1 as a1

其中daniel_view1

create view daniel_view as
            select x1.y1, x1.y2, x1.y3 ...
            from tb1, tb2, tb3, tb4, tb5 ...
            left join ...
            where ...
            group by ...

这将使它看起来更清洁。然后比较视图。可以合并吗?您不一定最终会将结果保留在最终产品中,但它们将有助于查看更广泛的模式而不会详细淹没。

或者,您可以将子查询插入临时表

insert #daniel_work1 
select x1.y1, x1.y2, x1.y3 ...
from tb1, tb2, tb3, tb4, tb5 ...
left join ...
where ...
group by ...

然后用

替换子查询
select ... from #daniel_work1 as a1

您可以做的另一件事是看看是否可以将其分解为连续步骤。 如果你看到

select ... from ...
union all
select ... from ...

这可能会成为

insert #steps
select 'step1', ...#1...
insert #steps
select 'step2', ...#2...

union比较棘手,因为set union会删除重复的行(所有列都与另一行相同的行)。

通过将中间结果存储在临时表中,您可以在展开时查看查询内部,并重放困难的步骤。我将'step_id'作为所有调试临时表的第一列,因此如果它分阶段填充,那么您将看到哪些数据适用于哪个阶段。

有一些技巧可以提供有关正在发生的事情的线索。如果您看到一个表连接到这样的表:

select ... from mytable t1 inner join mytable t2 on t2.id < t.id

它通常意味着他们想要表格与自身的交叉产品,但没有重复。你会得到钥匙1&amp; 2但不是2&amp; 1.