嵌套的SQL子查询是否可以被同一Select语句中的其他子查询访问..?

问题描述 投票:1回答:3

这适用于通过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
sql db2 db2-400
3个回答
1
投票

我相信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

0
投票

基于对我的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

0
投票

为了繁荣,您还可以将这些加载到全局临时表中。

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;
© www.soinside.com 2019 - 2024. All rights reserved.