在 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
如何在 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