Выберите последние и вторые последние строки даты для пользователя - PullRequest
0 голосов
/ 08 марта 2019

У меня следующий запрос, чтобы выбрать строки, в которых поле LAST_UPDATE_DATE получает записи, у которых значение даты больше или равно последним 7 дням, что прекрасно работает.

   SELECT 'NEW ROW' AS 'ROW_TYPE', A.EMPLID, B.FIRST_NAME, B.LAST_NAME,
    A.BANK_CD, A.ACCOUNT_NUM, ACCOUNT_TYPE, PRIORITY, A.LAST_UPDATE_DATE
   FROM PS_DIRECT_DEPOSIT D
    INNER JOIN PS_DIR_DEP_DISTRIB A ON A.EMPLID = D.EMPLID AND A.EFFDT = D.EFFDT
    INNER JOIN PS_EMPLOYEES B ON B.EMPLID = A.EMPLID
   WHERE 
    B.EMPL_STATUS NOT IN ('T','R','D')
    AND ((A.DEPOSIT_TYPE = 'P' AND A.AMOUNT_PCT = 100)
          OR A.PRIORITY = 999
          OR A.DEPOSIT_TYPE = 'B')
    AND A.EFFDT = (SELECT MAX(A1.EFFDT)
                   FROM PS_DIR_DEP_DISTRIB A1
                   WHERE A1.EMPLID = A.EMPLID
                    AND A1.EFFDT <= GETDATE())
    AND D.EFF_STATUS = 'A'
    AND D.EFFDT = (SELECT MAX(D1.EFFDT)
                   FROM PS_DIRECT_DEPOSIT D1
                   WHERE D1.EMPLID = D.EMPLID
                    AND D1.EFFDT <= GETDATE())
    AND A.LAST_UPDATE_DATE >= GETDATE() - 7

Что я хотел бы добавить к этому, так это добавить предыдущую (2-ю MAX) строку для каждого EMPLID, чтобы я мог вывести «старую» строку (которая была до последнего обновления последней строки, отвечающей критериям) вместе с новой строкой, которую я уже выводил в запросе.

ROW_TYPE      EMPLID    FIRST_NAME   LAST_NAME      BANK_CD     ACCOUNT_NUM     ACCOUNT_TYPE    PRIORITY    LAST_UPDATE_DATE
NEW ROW       12345     JOHN         SMITH          123548999   45234879        C               999         2019-03-06 00:00:00.000
OLD ROW       12345     JOHN         SMITH          214080046   92178616        C               999         2018-10-24 00:00:00.000
NEW ROW       56399     CHARLES      MASTER         785816167   84314314        C               999         2019-03-07 00:00:00.000   
OLD ROW       56399     CHARLES      MASTER         345761227   547352          C               999         2017-05-16 00:00:00.000

Таким образом, EMPLID будет упорядочено по NEW ROW, после чего будет OLD ROW, как показано выше. В этом примере «NEW ROW» получает запись за последние 7 дней, как указано LAST_UPDATE_DATE.

Я хотел бы получить отзыв о том, как изменить запрос, чтобы я также мог получить «старую» строку (которая является максимальной строкой, которая меньше, чем строка «NEW», полученная выше).

1 Ответ

0 голосов
/ 11 марта 2019

Это был медленный день для преступления в Готэме, поэтому я обернулся.Может сработать.

Это вряд ли сработает сразу после установки, но оно должно помочь вам начать.

Ваш столбец LAST_UPDATE_DATE находится на таблице PS_DIR_DEP_DISTRIB, поэтому мыначну там.Во-первых, вы хотите идентифицировать все записи, которые были обновлены за последние 7 дней, потому что это единственные, которые вас интересуют. На протяжении всего этого я предполагаю, и я, вероятно, ошибаюсь, что естественный ключ длятаблица состоит из EMPLID, BANK_CD и ACCOUNT_NUM.Вы захотите добавить фактический естественный ключ для этих столбцов в нескольких местах.Тем не менее, ограничитель даты выглядит примерно так:

  SELECT
    EMPLID
   ,BANK_CD
   ,ACCOUNT_NUM
  FROM
    PS_DIR_DEP_DISTRIB AS limit
  WHERE
    limit.LAST_UPDATE_DATE >= DATEADD(DAY, -7, CAST(GETDATE() AS DATE))
    AND 
    limit.LAST_UPDATE_DATE <= CAST(GETDATE() AS DATE)

Теперь мы будем использовать это как коррелированный подзапрос в предложении WHERE EXISTS, которое мы сопоставим с базовой таблицей для ограничениясами записи с естественными значениями ключа, которые были обновлены на прошлой неделе.Я изменил список SELECT на SELECT 1, что является типичным словосочетанием для коррелированного подпрограммы, так как он прекращает поиск совпадения, когда находит один (1), и фактически не возвращает никаких значений вообще.

Кроме того, поскольку мы все равно фильтруем этот набор записей, я переместил все остальные фильтры предложений WHERE для этой таблицы в этот (скоро будет) подзапрос.

Наконец, вSELECT часть, я добавил DENSE_RANK, чтобы принудительно упорядочить записи.Позже мы используем значение DENSE_RANK, чтобы отфильтровать только первые ( N ) записи, представляющие интерес.

Так что нам остается следующее:

SELECT
  EMPLID
 ,BANK_CD
 ,ACCOUNT_NUM
 --,ACCOUNT_TYPE --Might belong here. Can't tell without table alias in original SELECT
 ,PRIORITY
 ,EFFDT
 ,LAST_UPDATE_DATE
 ,DEPOSIT_TYPE
 ,AMOUNT_PCT
 ,DENSE_RANK() OVER (PARTITION BY --Add actual natural key columns here...
                       EMPLID
                     ORDER BY
                       LAST_UPDATE_DATE DESC
                    ) AS RowNum
FROM
  PS_DIR_DEP_DISTRIB AS sdist
WHERE
  EXISTS
    (
      -- Get the set of records that were last updated in the last 7 days.
      -- Correlate to the outer query so it only returns records related to this subset.
      -- This uses a correlated subquery. A JOIN will work, too. Try both, pick the faster one.
      -- Something like this, using the actual natural key columns in the WHERE
      SELECT
        1
      FROM
        PS_DIR_DEP_DISTRIB AS limit
      WHERE
        --The first two define the date range.
        limit.LAST_UPDATE_DATE >= DATEADD(DAY, -7, CAST(GETDATE() AS DATE))
        AND limit.LAST_UPDATE_DATE <= CAST(GETDATE() AS DATE)
        AND
        --And these are the correlations to the outer query.
        limit.EMPLID = sdist.EMPLID
        AND limit.BANK_CD = sdist.BANK_CD
        AND limit.ACCOUNT_NUM = sdist.ACCOUNT_NUM
    )
  AND
  (
    dist.DEPOSIT_TYPE = 'P'
    AND dist.AMOUNT_PCT = 100
  )
  OR dist.PRIORITY = 999
  OR dist.DEPOSIT_TYPE = 'B'

Замените исходный запрос INNER JOIN на PS_DIR_DEP_DISTRIB этим запросом.В списке SELECT первое жестко закодированное значение теперь зависит от значения RowNum, так что теперь это выражение CASE.В предложении WHERE все даты определяются подзапросом, поэтому они пропали, некоторые из них были свернуты в подзапрос, и мы добавляем WHERE dist.RowNum <= 2, чтобы вернуть 2 верхние записи.

(я также заменил все псевдонимы таблицы, чтобы я мог отслеживать то, на что я смотрел.)

SELECT
  CASE dist.RowNum
    WHEN 1 THEN 'NEW ROW'
    ELSE 'OLD ROW' 
  END AS ROW_TYPE
 ,dist.EMPLID
 ,emp.FIRST_NAME
 ,emp.LAST_NAME
 ,dist.BANK_CD
 ,dist.ACCOUNT_NUM
 ,ACCOUNT_TYPE
 ,dist.PRIORITY
 ,dist.LAST_UPDATE_DATE
FROM
  PS_DIRECT_DEPOSIT AS dd
INNER JOIN
  (
    SELECT
      EMPLID
     ,BANK_CD
     ,ACCOUNT_NUM
     --,ACCOUNT_TYPE --Might belong here. Can't tell without table alias in original SELECT
     ,PRIORITY
     ,EFFDT
     ,LAST_UPDATE_DATE
     ,DEPOSIT_TYPE
     ,AMOUNT_PCT
     ,DENSE_RANK() OVER (PARTITION BY --Add actual natural key columns here...
                           EMPLID
                         ORDER BY
                           LAST_UPDATE_DATE DESC
                        ) AS RowNum
    FROM
      PS_DIR_DEP_DISTRIB AS sdist
    WHERE
      EXISTS
        (
          -- Get the set of records that were last updated in the last 7 days.
          -- Correlate to the outer query so it only returns records related to this subset.
          -- This uses a correlated subquery. A JOIN will work, too. Try both, pick the faster one.
          -- Something like this, using the actual natural key columns in the WHERE
          SELECT
            1
          FROM
            PS_DIR_DEP_DISTRIB AS limit
          WHERE
            --The first two define the date range.
            limit.LAST_UPDATE_DATE >= DATEADD(DAY, -7, CAST(GETDATE() AS DATE))
            AND limit.LAST_UPDATE_DATE <= CAST(GETDATE() AS DATE)
            AND
            --And these are the correlations to the outer query.
            limit.EMPLID = sdist.EMPLID
            AND limit.BANK_CD = sdist.BANK_CD
            AND limit.ACCOUNT_NUM = sdist.ACCOUNT_NUM
        )
      AND
      (
        dist.DEPOSIT_TYPE = 'P'
        AND dist.AMOUNT_PCT = 100
      )
      OR dist.PRIORITY = 999
      OR dist.DEPOSIT_TYPE = 'B'
  ) AS dist
    ON
    dist.EMPLID = dd.EMPLID
      AND dist.EFFDT = dd.EFFDT
INNER JOIN
  PS_EMPLOYEES AS emp
    ON
    emp.EMPLID = dist.EMPLID
WHERE
  dist.RowNum <= 2
  AND
  emp.EMPL_STATUS NOT IN ('T', 'R', 'D')
  AND 
  dd.EFF_STATUS = 'A';
...