我刚开始在新公司的新项目中开始。
我获得了一个庞大而复杂的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
如何让我更容易理解查询,以便识别缺陷?
答案 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
答案 2 :(得分:1)
我每天处理这样的代码,因为我们在这里进行了大量的复杂数据报告和导出。
第一步是了解您正在做的事情的意义。如果您不理解其含义,则无法评估您是否获得了正确的结果。因此,要准确了解您要完成的工作,并查看是否可以在用户界面中看到您应该看到的结果。将某些内容进行比较确实很有帮助,这样您就可以在查询时看到添加新内容如何更改结果。如果您的查询使用单个字母或其他对派生表别名无意义的东西,那么当您弄清楚派生表应该执行的含义时,请将别名替换为更有意义的内容,例如Employees而不是A.将使下一个使用它的人更容易在以后解码它。
然后你做的是从最里面的派生表开始(如果你愿意,可以使用子查询,但是当它被用作表时,术语派生表更准确)。首先弄清楚它应该做什么。例如,可能是所有员工的绩效评估都不尽如人意。
运行它并根据您正在做的事情的含义检查结果以查看它们是否正确。例如,如果您正在寻找不满意的评估并且您拥有10,000名员工,那么5617对于该数据块来说似乎是一个合理的结果集?寻找重复记录。如果同一个人在那里三次,那么你可能会遇到一个问题,你加入一对多,当你只想要一个人时,你会有很多人回来。这可以通过使用聚合函数和分组依据或将另一个派生表放入以替换问题联接来解决。
一旦你将最里面的部分清除了,然后开始检查其他其他派生表的结果,重新添加代码并检查结果,直到找到不应该有的记录丢失的位置(嘿我有137名员工)在这个阶段,现在我只有116.是什么造成的?)请记住,这只是一个线索,看看为什么会发生这种情况。当基本结果发生变化时,有时候会构建一个复杂的查询,有时候它们应该没有,这就是为什么理解数据的含义是至关重要的。
一般要注意的一些事项:
如果你有左联接,请留意他们得到的地方 通过放置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:
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.