在 DB2 SQL 中,如何使用 FETCH FIRST (n) ROWS ONLY.. return 可变数量的行?

In DB2 SQL, how can I return a variable number of rows with FETCH FIRST (n) ROWS ONLY..?

如何在 DB2 SQL 中 return 具有 FETCH FIRST (n) ROWS ONLY 的可变 (n) 行数?这是 DB2/400 v7r3.

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

...fetch-row-count must not contain a scalar-fullselect, a column reference, a table reference, a user-defined function reference, or a built-in scalar function...

...我的意思是它必须是常量,例如 "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.

此查询旨在查找在较大的 table 中孤立或键入不正确的金融交易记录,这些记录具有相当松散的规范化和参照完整性(或缺乏)。

PREFILTER 是一个查询,它 return 是主要 table 的一个子集,而 GT1 是另一个查询,它计算这些记录的更小和更复杂的子集.然后 JOIN 它们,以及 return (n)LOSTQTY 指定的行,按日期降序排列。所以它应该只有 return (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

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

您应该可以为此使用 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

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

好的,吸收后 Mustaccio's previous answer and banging my head on the IBM documentation all day, I was able to work this out. Much thanks to 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

参考页确实说您不能像您尝试的那样使用列引用作为行数的表达式 return。

它允许的是一个表达式。有关于它所说的不允许的规则,但这些规则不排除使用主机变量。

    FETCH FIRST :xqty ROWS ONLY