В DB2 SQL, как я могу вернуть переменное число строк только с FETCH FIRST (n) ROWS ..? - PullRequest
0 голосов
/ 24 января 2019

Как я могу вернуть переменное (n) количество строк с FETCH FIRST (n) ROWS ONLY в DB2 SQL ..? Это для DB2 / 400 v7r3.

В документации для 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 голосов
/ 25 января 2019

Справочная страница действительно говорит о том, что вы не можете использовать ссылку на столбец, как вы пытались это сделать, в качестве выражения числа возвращаемых строк.

То, что она допускает, является выражением.Существуют правила относительно того, что в нем сказано, что это запрещено, но эти правила не исключают использование переменной хоста.

    FETCH FIRST :xqty ROWS ONLY
0 голосов
/ 24 января 2019

Вы должны иметь возможность использовать оконную функцию 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, который впоследствии можно использовать для ограничения набора результатов.

0 голосов
/ 25 января 2019

Хорошо, после того, как я усвоил предыдущий ответ Мустаччо и постучал по документации IBM весь день, я смог разобраться с этим.Большое спасибо Мустаччо за то, что он указал мне правильное направление.

Два важных бита: расположение 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
0 голосов
/ 24 января 2019

Я не уверен, что вы можете делать то, что вы хотите в одном утверждении.Однако вы должны иметь возможность собрать хранимую процедуру или функцию SQL, которая может сделать это вместо этого.В хранимой процедуре соберите запрос, а затем откройте его курсором.Затем курсор может извлечь желаемое количество строк, поместить его в набор результатов и вернуть этот набор результатов из процедуры.Текущая документация от IBM по этому вопросу находится здесь: https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/sqlp/rbafyresultsets.htm

...