SQL-запрос с самостоятельным соединением - PullRequest
0 голосов
/ 05 августа 2011

Учитывая таблицу (TableA), которая содержит следующие данные:

Id    Date    Status    RecordId
1    01/06/11    2      REC001
2    01/06/11    2      REC002
3    01/06/11    2      REC003
4    01/07/11    1      REC001

Как я могу вернуть все записи со статусом 2, кроме записей с заданным RecordId, где за статусом 2 следует символзапись 1 на более позднюю дату (и больше нет записей с состоянием 2.

Так, например, запрос должен вернуть REC002 и REC003, так как REC001 имел статус 2 в прошлом, но этобыл заменен идентификатором записи 4 со статусом 1 на более позднюю дату. Если в какой-то более поздний момент времени для REC001 была добавлена ​​еще одна запись со статусом 2, то она снова должна присутствовать в наборе результатов (при условииболее поздние записи со статусом 1).

Моя слабая попытка возиться с этим:

DECLARE @TableA TABLE
(
    Id INT,
    Dt DATETIME,
    Stat INT,
    RecId VARCHAR(6)
)

INSERT INTO @TableA 

SELECT   1,    DATEADD(day, -5, current_timestamp),  2,   'REC001'
UNION
SELECT   2,    DATEADD(day, -4, current_timestamp),  2,   'REC002'
UNION
SELECT   3,    DATEADD(day, -3, current_timestamp),  2,   'REC003'
UNION
SELECT   4,    DATEADD(day, -2, current_timestamp),  1,   'REC001'

   SELECT * 
     FROM @TableA t1
LEFT JOIN @TableA t2 ON t1.RecId = t2.RecId 
    WHERE t1.Stat = 2 
      AND (t1.Dt >= t2.Dt 
      AND t2.Stat != 1)

Это работает, но возвращает значения, где t1.Id = t2.То есть, я знаю, что могу исключить это с помощью предложения where, но если я добавлю еще несколько записей в таблицу, это снова не удастся. Например;

INSERT INTO @TableA 
SELECT   1,    DATEADD(day, -15, current_timestamp),  2,   'REC004'
UNION
SELECT   2,    DATEADD(day, -14, current_timestamp),  2,   'REC002'
UNION
SELECT   3,    DATEADD(day, -13, current_timestamp),  1,   'REC003'
UNION
SELECT   4,    DATEADD(day, -12, current_timestamp),  1,   'REC001'
UNION
SELECT   11,    DATEADD(day, -5, current_timestamp),  2,   'REC004'
UNION
SELECT   21,    DATEADD(day, -4, current_timestamp),  2,   'REC002'
UNION
SELECT   31,    DATEADD(day, -3, current_timestamp),  1,   'REC003'
UNION
SELECT   41,    DATEADD(day, -2, current_timestamp),  1,   'REC001'

Любые идеи приветствуются.

РЕДАКТИРОВАТЬ: Я попробовал два ответа, и хотя ни один из них не дал мне именно то, что мне нужно, они, безусловно, указали мне в правильном направлении.Используя приведенные ответы, я пришел к следующему, которое, кажется, выполняет то, что мне нужно:

;WITH lastSuccess(recid, dt) AS (
    select recid, max(dt) from @tableA
    where stat = 1
    group by recid
),
lastFailure(recid, dt) AS (
    select recid, max(dt) from @tableA
    where stat = 2
    group by recid
)
select a.* from @tablea a
-- Limit results to those that include a failure
INNER JOIN lastFailure lf ON lf.recid = a.recid AND lf.dt = a.dt
-- If the recid also has a success, show this along with it's latest success date
LEFT JOIN lastSuccess ls ON ls.recid = lf.recid 
-- Limit records to where last failure is > last success or where there is no last success.
WHERE (lf.dt > ls.dt OR ls.dt IS NULL)

Единственный недостаток, который я могу видеть здесь, - это наличие двух записей с одинаковой отметкой времени, тогда этов результате установить дважды.Например, если идентификатор 21 был повторен как 22 тогда, он появится дважды.Это не настоящая проблема, так как в действительности метка времени всегда будет уникальной.

Ответы [ 2 ]

1 голос
/ 05 августа 2011
WITH ranked AS (
  SELECT
    *,
    rn = ROW_NUMBER() OVER (PARTITION BY RecId ORDER BY Dt DESC)
  FROM TableA
)
SELECT
  r1.Id,
  r1.Dt,
  r1.Stat,
  r1.RecId
FROM ranked r1
  INNER JOIN ranked r2 ON r1.RecId = r2.RecId AND r2.rn = 1
WHERE r1.Stat = 2

ОБНОВЛЕНИЕ после обновления вопроса

WITH ranked AS (
  SELECT
    *,
    rn = ROW_NUMBER() OVER (PARTITION BY RecId ORDER BY Dt DESC)
  FROM TableA
)
SELECT
  Id,
  Dt,
  Stat,
  RecId
FROM ranked
WHERE Stat = 2 AND rn = 1
1 голос
/ 05 августа 2011

Это то, что вам нужно?

declare @tableB table (recid varchar(6), dt datetime)
insert @tableB
select recid, max(dt) from @tableA
where stat = 1
group by recid

Это создаст вам таблицу со всеми "1" и их максимальной датой.

select * from @tableA a
left join @tableB b on a.recid = b.recid
where a.stat = 2 and a.dt > isnull(b.dt, '1753-01-01')

Этопокажет все записи из A, кроме тех, где есть более поздние B.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...