这适用于通过DBeaver SQL客户端访问的IBM AS400 DB2 SQL服务器vr61m0。我有什么对我来说是通过手动编写任何SQL而创建的最复杂的查询,并且它的一部分存在重复问题。具体来说,有一个子查询以不同的方式多次使用,我希望减轻这种重复,因此它只出现一次。
我不确定使用什么术语,因此很难在Google上搜索。我尝试过像“子查询”,“衍生表”等等,但没有运气。我收到太多不相关的搜索结果。对我来说这个问题似乎很明显是一个范围问题:各种子查询环境都看不到对方。
到目前为止......如果SQL查询在FROM子句中有子查询,并且其中一个被多次使用,但在某些情况下,它嵌套到更深的子查询中以应用聚合函数,那么更深的子查询可以引用更浅的子查询避免重复.. ??
这是我用工作数据创建的一个工作示例。这是一个非常简洁的例子,它基于我正在使用的真实查询和数据。重复的子查询在SQL中注明并带有注释:
数据表ORDERS
:
ORDDATE ORDNUM ORDACCT ORDLOAD
20180901 1 ABC99 101
20180901 2 XYZ00 102
20180901 3 ZZZ12 103
数据表LOADS
:
LOADDATE LOADNUM LOADDRIV LOADHLP1 LOADHLP2 RATEDRIV RATEHLP1 RATEHLP2
20180901 101 57 1
20180901 102 60 71 1 2
20180901 103 58 81 85 1 3 3
此SQL有效,但所提到的子查询重复两次:
SELECT ORDDATE, ORDNUM, ORDACCT, ORDLOAD, TYPECODE AS LOADTYPE, EMPID, RATE
FROM CERTODB.ORDERS AS ORDERS
-- THE SUBQUERY BELOW IS THE FIRST DUPLICATION
INNER JOIN ( SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
) AS LOADEMPS ON ORDERS.ORDDATE = LOADEMPS.LOADDATE
AND ORDERS.ORDLOAD = LOADEMPS.LOADNUM
INNER JOIN ( SELECT LOADDATE, LOADNUM, COUNT(LOADROLE) AS TYPECODE
-- THE SUBQUERY BELOW IS THE SECOND DUPLICATION
FROM ( SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
) AS LOADEMPS
GROUP BY LOADDATE, LOADNUM
) AS LOADTYPE ON ORDERS.ORDDATE = LOADTYPE.LOADDATE
AND ORDERS.ORDLOAD = LOADTYPE.LOADNUM
ORDER BY ORDDATE, ORDNUM, ORDLOAD, LOADROLE
这里尝试在主查询中稍后引用第一个子查询,但它不起作用。它产生错误:“SQL错误[42704]:[SQL0204]找不到CERTODB类型* FILE中的LOADEMPS。”如上所述,它似乎是一个范围问题:子查询环境无法看到对方。
有没有办法在一个SQL查询中完成这项工作或类似的工作,所以复制不存在......?例如,没有额外的视图,存储过程或其他对象,但都在一个大的SQL语句中完成..?
SELECT ORDDATE, ORDNUM, ORDACCT, ORDLOAD, TYPECODE AS LOADTYPE, EMPID, RATE
FROM CERTODB.ORDERS AS ORDERS
INNER JOIN ( SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
) AS LOADEMPS ON ORDERS.ORDDATE = LOADEMPS.LOADDATE
AND ORDERS.ORDLOAD = LOADEMPS.LOADNUM
INNER JOIN ( SELECT LOADDATE, LOADNUM, COUNT(LOADROLE) AS TYPECODE
-- BELOW AN ATTEMPT IS MADE TO ACCESS THE PREVIOUS CALL OF THE SUBQUERY 'LOADEMPS', THUS REMOVING THE DUPLICATE
FROM LOADEMPS
GROUP BY LOADDATE, LOADNUM
) AS LOADTYPE ON ORDERS.ORDDATE = LOADTYPE.LOADDATE
AND ORDERS.ORDLOAD = LOADTYPE.LOADNUM
ORDER BY ORDDATE, ORDNUM, ORDLOAD, LOADROLE
我相信LATERAL
关键字会有所帮助
SELECT ORDDATE, ORDNUM, ORDACCT, ORDLOAD, TYPECODE AS LOADTYPE, EMPID, RATE
FROM CERTODB.ORDERS AS ORDERS
INNER JOIN ( SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
) AS LOADEMPS ON ORDERS.ORDDATE = LOADEMPS.LOADDATE
AND ORDERS.ORDLOAD = LOADEMPS.LOADNUM
INNER JOIN LATERAL ( SELECT LOADDATE, LOADNUM, COUNT(LOADROLE) AS TYPECODE
-- BELOW AN ATTEMPT IS MADE TO ACCESS THE PREVIOUS CALL OF THE SUBQUERY 'LOADEMPS', THUS REMOVING THE DUPLICATE
FROM LOADEMPS
GROUP BY LOADDATE, LOADNUM
) AS LOADTYPE ON ORDERS.ORDDATE = LOADTYPE.LOADDATE
AND ORDERS.ORDLOAD = LOADTYPE.LOADNUM
ORDER BY ORDDATE, ORDNUM, ORDLOAD, LOADROLE
但正如评论中所提到的,我会使用几个CTE
with loademps as (SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
)
, loadtypes as ( SELECT LOADDATE, LOADNUM, COUNT(LOADROLE) AS TYPECODE
-- BELOW AN ATTEMPT IS MADE TO ACCESS THE PREVIOUS CALL OF THE SUBQUERY 'LOADEMPS', THUS REMOVING THE DUPLICATE
FROM LOADEMPS
GROUP BY LOADDATE, LOADNUM
)
SELECT ORDDATE, ORDNUM, ORDACCT, ORDLOAD, TYPECODE AS LOADTYPE, EMPID, RATE
FROM CERTODB.ORDERS AS ORDERS
INNER JOIN LOADEMPS ON ORDERS.ORDDATE = LOADEMPS.LOADDATE
AND ORDERS.ORDLOAD = LOADEMPS.LOADNUM
INNER JOIN LOADTYPE ON ORDERS.ORDDATE = LOADTYPE.LOADDATE
AND ORDERS.ORDLOAD = LOADTYPE.LOADNUM
ORDER BY ORDDATE, ORDNUM, ORDLOAD, LOADROLE
基于对我的OP的回应,似乎我需要一个公用表表达式(CTE),又名WITH子句:https://modern-sql.com/feature/with
下面是重新设计的SQL,需要删除重复:
WITH LOADEMPS AS ( SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
)
SELECT ORDDATE, ORDNUM, ORDACCT, ORDLOAD, TYPECODE AS LOADTYPE, EMPID, RATE
FROM CERTODB.ORDERS AS ORDERS
INNER JOIN LOADEMPS ON ORDERS.ORDDATE = LOADEMPS.LOADDATE
AND ORDERS.ORDLOAD = LOADEMPS.LOADNUM
INNER JOIN ( SELECT LOADDATE, LOADNUM, COUNT(LOADROLE) AS TYPECODE
FROM LOADEMPS
GROUP BY LOADDATE, LOADNUM
) AS LOADTYPE ON ORDERS.ORDDATE = LOADTYPE.LOADDATE
AND ORDERS.ORDLOAD = LOADTYPE.LOADNUM
ORDER BY ORDDATE, ORDNUM, ORDLOAD, LOADROLE
为了繁荣,您还可以将这些加载到全局临时表中。
在CTE
或传统临时表上使用全局临时表的优点是全局表可以在存储过程中初始化,然后在嵌套在外部表中的另一个存储过程中引用。
它还允许您使用超快速和宽松的SELECT ... INTO
语法,而无需提前定义表。
SELECT *
INTO #Global_Temp
FROM (
SELECT LOADDATE, LOADNUM, 1 AS LOADROLE, LOADDRIV AS EMPID, RATEDRIV AS RATE FROM CERTODB.LOADS WHERE LOADDRIV>0
UNION SELECT LOADDATE, LOADNUM, 2 AS LOADROLE, LOADHLP1 AS EMPID, RATEHLP1 AS RATE FROM CERTODB.LOADS WHERE RATEHLP1>0
UNION SELECT LOADDATE, LOADNUM, 3 AS LOADROLE, LOADHLP2 AS EMPID, RATEHLP2 AS RATE FROM CERTODB.LOADS WHERE RATEHLP2>0
);
SELECT ORDDATE, ORDNUM, ORDACCT, ORDLOAD, TYPECODE AS LOADTYPE, EMPID, RATE
FROM CERTODB.ORDERS AS ORDERS
-- THE SUBQUERY BELOW IS THE FIRST DUPLICATION
INNER JOIN ( SELECT LOADDATE, LOADROLE, EMPID, RATE FROM #Global_Temp
) AS LOADEMPS ON ORDERS.ORDDATE = LOADEMPS.LOADDATE
AND ORDERS.ORDLOAD = LOADEMPS.LOADNUM
INNER JOIN ( SELECT LOADDATE, LOADNUM, COUNT(LOADROLE) AS TYPECODE
-- THE SUBQUERY BELOW IS THE SECOND DUPLICATION
FROM ( SELECT LOADDATE, LOADROLE, EMPID, RATE FROM #Global_Temp
) AS LOADEMPS
GROUP BY LOADDATE, LOADNUM
) AS LOADTYPE ON ORDERS.ORDDATE = LOADTYPE.LOADDATE
AND ORDERS.ORDLOAD = LOADTYPE.LOADNUM
ORDER BY ORDDATE, ORDNUM, ORDLOAD, LOADROLE;
DROP TABLE #Global_Temp;