在DB2 SQL中,如何仅使用FETCH FIRST(n)ROWS返回可变数量的行?

时间:2019-01-23 21:31:05

标签: db2 db2-400

如何在DB2 SQL中使用FETCH FIRST (n) ROWS ONLY返回变量(n)行数?这是针对DB2 / 400 v7r3的。

documentation for FETCH确实声明无法完成,但我无法想象另一种方式...

  

... fetch-row-count不得包含标量全选择,列引用,表引用,用户定义的函数引用或内置标量函数...

...我的意思是它必须是诸如"10"之类的常量,并且不能是变量或列名,很遗憾,这正是我想要执行的操作。

这有效:

SELECT      PREFILTER.*
FROM        PREFILTER
INNER JOIN  GT1 ON FILTERED.GTAMT=GT1.LOSTAMT
ORDER BY    GTDATE DESC
FETCH FIRST 10 ROWS ONLY

这不起作用:

SELECT      PREFILTER.*
FROM        PREFILTER
INNER JOIN  GT1 ON PREFILTER.GTAMT=GT1.LOSTAMT
ORDER BY    GTDATE DESC
FETCH FIRST (GT1.LOSTAMT) ROWS ONLY    <=== changed here

收到以下错误:

SQL Error [428H7]: [SQ20467] Expression containing LOSTAMT must calculate a constant value.

此查询旨在查找在较大的表中具有相当宽松的规范化和参照完整性(或缺乏参照完整性)的孤立或键入错误的金融交易记录。

PREFILTER是一个查询,它返回主表的一个子集,而GT1是另一个查询,它计算这些记录的一个较小且更复杂的子集。然后JOIN行,并按降序返回(n)LOSTQTY指定的行。因此,它只应返回(n)个最新记录。

请注意,我确实认识到FETCH的放置是不正确的,并且它(或它变形的任何形式)可能必须移至CTE查询之一,例如GT1

而且,在这个早期阶段,似乎很明显我得到了笛卡尔的结果,但是一旦FETCH问题解决了,那很可能会解决。

作为参考,这是项目的完整SQL:

WITH  --SET THE INITIAL ACCOUNT & DATE RANGE  
        PREFILTER    AS  (   SELECT      *
                            FROM        GLTRANT
                            WHERE       GTDATE > 20170000
                            AND         GTACCT=112068
                        ),
      --CREATE LIST OF ALL POSITIVE VALUES
        POSVALS     AS  (   SELECT      GTAMT      AS POSAMT, COUNT(GTAMT) AS POSC
                            FROM        PREFILTER 
                            WHERE       GTAMT > 0
                            GROUP BY    GTAMT
                        ),
      --CREATE LIST OF ALL NEGATIVE VALUES, WITH SIGN DROPPED
        NEGVALS     AS  (   SELECT      ABS(GTAMT) AS NEGAMT, COUNT(GTAMT) AS NEGC
                            FROM        PREFILTER 
                            WHERE       GTAMT < 0
                            GROUP BY    ABS(GTAMT)
                        ),
      --CALCULATE DISCREPANCIES BETWEEN THE TWO LISTS. SUBTRACT THE TWO AND MULTIPLY THE SIGN BY THE ABSOLUTE VALUE 
      --OF THE DIFFERENCE. THEN TO RESTORE THE SIGN, MULTIPLY THE AMOUNT BY THE SIGN OF THE DIFFERENCE. 
        FOJ         AS  (   SELECT      SIGN(COALESCE(POSC,0)-COALESCE(NEGC,0))*COALESCE(POSAMT,NEGAMT) AS LOSTAMT, 
                                        ABS (COALESCE(POSC,0)-COALESCE(NEGC,0))                         AS LOSTQTY
                            FROM        POSVALS 
                            FULL OUTER JOIN NEGVALS ON POSAMT=NEGAMT
                            WHERE       COALESCE(POSC,0)-COALESCE(NEGC,0) <> 0
                        ),
      --GET DISCREPANCIES WITH COUNT >1 
        GT1         AS  (   SELECT      *
                            FROM        FOJ
                            WHERE       LOSTQTY>1 
                        )

--SEARCH PREFILTER FOR EACH AMOUNT (LOSTAMT) IN GT1 AND RETURN THE MOST RECENT (LOSTQTY) RECORDS 
SELECT      PREFILTER.*
FROM        PREFILTER
INNER JOIN  GT1 ON PREFILTER.GTAMT=GT1.LOSTAMT
ORDER BY    GTDATE DESC
FETCH FIRST (GT1.LOSTQTY) ROWS ONLY --DOES NOT WORK

4 个答案:

答案 0 :(得分:4)

您应该可以使用window function ROW_NUMBER

WITH  --SET THE INITIAL ACCOUNT & DATE RANGE  
        PREFILTER    AS  (  
          SELECT  T.*,
                  ROW_NUMBER() OVER (ORDER BY T.GTDATE DESC) AS RN
          FROM (
                            SELECT      *
                            FROM        GLTRANT
                            WHERE       GTDATE > 20170000
                            AND         GTACCT=112068
          ) T
        ),
...
--SEARCH PREFILTER FOR EACH AMOUNT (LOSTAMT) IN GT1 AND RETURN THE MOST RECENT (LOSTQTY) RECORDS 
SELECT      PREFILTER.*
FROM        PREFILTER
INNER JOIN  GT1 ON PREFILTER.GTAMT=GT1.LOSTAMT
WHERE RN <= GT1.LOSTQTY
ORDER BY    GTDATE DESC

它按PREFILTER的降序为GTDATE中的每一行分配连续的数字,以后可以用来限制结果集。

答案 1 :(得分:1)

我不确定您是否可以在单个语句中完成所需的操作。但是,您应该能够将可以执行此操作的存储过程或SQL函数放在一起。在存储过程中,将查询放在一起,然后用光标将其打开。然后,游标可以获取所需的行数,将其放入结果集中,然后从过程中返回该结果集。 IBM关于此的最新文档位于:https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/sqlp/rbafyresultsets.htm

答案 2 :(得分:0)

好吧,在听完Mustaccio的先前答案并全神贯注于IBM文档之后,我得以解决。非常感谢Mustaccio为我指出正确的方向。

两个重要位:ROW_NUMBER()的位置必须进一步向下移动;它在原地不起作用。另外,它还需要添加PARTITION BY参数,以便从每组新值开始重新编号。所以它像1-2-1-2-1-2-3而不是1-2-3-4-5-6-7。这对于WHERE RN <= LOSTQTY的工作至关重要。

这是完整的结果:

WITH  --SET THE INITIAL ACCOUNT & DATE RANGE  
        PREFILTER   AS  (   SELECT  GTCOMP, GTACCT, GTDATE, GTSRCE, GTREF#, GTENT#, GTAMT, GTDESC, "GTPO#", 
                                    "GTCHK#", "GTINV#", GTCKAC, GT1099, GTXXX1, GTAFLG, GTVEND, "GTBAT#"
                            FROM        F_CERTOB.GLTRANT
                            WHERE       GTDATE > 20180000
                            AND         GTACCT=112068
                        ),
      --CREATE LIST OF ALL POSITIVE VALUES
        POSVALS     AS  (   SELECT      GTAMT      AS POSAMT, COUNT(GTAMT) AS POSC
                            FROM        PREFILTER 
                            WHERE       GTAMT > 0
                            GROUP BY    GTAMT
                        ),
      --CREATE LIST OF ALL NEGATIVE VALUES, WITH SIGN DROPPED
        NEGVALS     AS  (   SELECT      ABS(GTAMT) AS NEGAMT, COUNT(GTAMT) AS NEGC
                            FROM        PREFILTER 
                            WHERE       GTAMT < 0
                            GROUP BY    ABS(GTAMT)
                        ),
      --CALCULATE DISCREPANCIES BETWEEN THE TWO LISTS. SUBTRACT THE "DE-SIGNED" NEGATIVE FROM THE POSITIVE AND 
      --MULTIPLY THE SIGN BY THE ABSOLUTE VALUE OF THE DIFFERENCE. THEN TO RESTORE THE SIGN, MULTIPLY THE AMOUNT 
      --BY THE SIGN OF THE DIFFERENCE. THIS IS A FULL OUTER JOIN, SO NULLS ARE A GIVEN, AND COALESCE() IS USED 
      --TO FILL IN THE HOLES.  
        FOJ         AS  (   SELECT      SIGN(COALESCE(POSC,0)-COALESCE(NEGC,0))*COALESCE(POSAMT,NEGAMT) AS LOSTAMT, 
                                        ABS (COALESCE(POSC,0)-COALESCE(NEGC,0))                         AS LOSTQTY
                            FROM        POSVALS 
                            FULL OUTER JOIN NEGVALS ON POSAMT=NEGAMT
                            WHERE       COALESCE(POSC,0)-COALESCE(NEGC,0) <> 0
                        ),
      --THIS IS AN EXTRA KNOB TO CONTROL THE NUMBER OF RESULTS DURING DEVELOPMENT. IF SET TO >1, IT WILL SHOW
      --ONLY THE DATA THAT WOULD RETURN TWO OR MORE ROWS. USEFUL WHEN 99% OF THE DATA WOULD BE ONE ROW. THIS
      --WAS NEEDED TO DEVELEOP & TEST "ROW_NUMBER() OVER (PARTITION BY GTAMT ORDER BY GTDATE DESC) AS RN" AND
      --IF THE NUMBERING WAS RESTARTING PROPERLY FOR EACH GROUP OF VALUES.
        GT          AS  (   SELECT      *
                            FROM        FOJ
                            WHERE       LOSTQTY>0
                        ),
      --RETRIEVE THE ITEMS, RANK THEM BY AMT & DATE. USE PARTITON-BY TO RESTART ROW NUMBERING FOR EACH GROUP 
      --OF AMOUNTS. USE ORDER-BY TO NUMBER BY DATE IN DESCENDING ORDER. 
        GROUPED     AS  (   SELECT      PREFILTER.*, LOSTQTY, 
                                        ROW_NUMBER() OVER (PARTITION BY GTAMT ORDER BY GTDATE DESC) AS RN
                            FROM        PREFILTER
                            INNER JOIN  GT ON GTAMT=LOSTAMT
                        ),
      --NARROW IT DOWN TO ONLY THE TOP (n) ITEMS
        RECENT      AS  (   SELECT      *
                            FROM        GROUPED
                            WHERE       RN <= LOSTQTY
                        )

SELECT      *
FROM        RECENT
ORDER BY    GTAMT

答案 3 :(得分:0)

引用页面确实说您不能像尝试那样使用列引用作为要返回的行数的表达。

它允许的是一个表达式。有一些规则禁止它说什么,但是这些规则并不排除使用主机变量。

    FETCH FIRST :xqty ROWS ONLY