Таблица с полем идентификации: лучший запрос SQL для получения идентификаторов удаленных записей? - PullRequest
0 голосов
/ 18 января 2010

У меня есть таблица с полем идентификации. Какой лучший SQL-запрос для получения идентификаторов всех удаленных записей из этой таблицы?

Ответы [ 6 ]

2 голосов
/ 18 января 2010

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

пример того, как будет выглядеть код

create table #bla(id int)

insert #bla values(1)
insert #bla values(2)
insert #bla values(4)
insert #bla values(5)
insert #bla values(9)
insert #bla values(12)



select number from master..spt_values s
left join #bla b on s.number = b.id
where s.type='P'
and s.number < (select MAX(id) from #bla)
and  b.id is null

выход

0 3 6 7 8 10 11

См. Здесь: Как вернуть все пропущенные значения идентификаторов из таблицы в SQL Server для получения более подробной информации

2 голосов
/ 18 января 2010

Совершенно другой способ сделать это:

SELECT a.intId, b.intId
FROM MyTable a
    CROSS JOIN MyTable b
WHERE a.intId + 1 < b.intId
    AND NOT EXISTS (
        SELECT NULL FROM MyTable c
        WHERE c.intId > a.intId
            AND c.intId < b.intId
    )

, который даст пары идентификаторов, между которыми были удалены все записи.

Так что, если идентификаторы были (1, 2, 3, 6, 7, 12), он будет возвращать (3, 6) и (7, 12).

РЕДАКТИРОВАТЬ:

Это очень неэффективно, если таблица большая,Следующий метод намного лучше:

SELECT g.intStartId, MIN(t.intId) AS intEndId
FROM (
    SELECT intId AS intStartId
    FROM MyTable AS a
    WHERE NOT EXISTS (
        SELECT NULL FROM MyTable AS b
        WHERE b.intId = a.intId + 1
    )
) AS g
    CROSS JOIN MyTable AS t
WHERE t.intId > g.intStartId
GROUP BY g.intStartId

Итак, сначала мы находим идентификаторы, которые отмечают начало пропуска, а затем мы находим самый низкий идентификатор, который у нас больше, чем каждый, чтобы обозначить конец пропуска.

2 голосов
/ 18 января 2010

Вы можете использовать рекурсивный запрос:

DECLARE @MaxId int
SELECT @MaxId = SELECT IDENT_CURRENT('MyTable');

WITH Ids AS (
    SELECT 1 AS intId
    UNION ALL
    SELECT intId + 1
    FROM Ids
    WHERE intId < @MaxId
)
SELECT intId
FROM Ids AS i
WHERE NOT EXISTS (
    SELECT NULL FROM MyTable AS m
    WHERE m.intId = i.intId
)
OPTION (MAXRECURSION 0)

Хотя это будет не очень эффективно, если стол очень большой.

1 голос
/ 18 января 2010

Недостаточно запроса с упором на столбец с последовательным идентификатором. Последовательность ID может пропустить числа во время вставки, если транзакция не удалась, поэтому если у вас есть ID = (1,3), это не значит, что ID=2 было удалено, возможно, она была пропущена. Вы должны использовать что-то для перехвата удаленных записей, например, триггер или OUTPUT DELETED.* - или использовать что-то для сравнения, например, со снимком, резервной копией, таблицей истории.

1 голос
/ 18 января 2010

Один из вариантов - создать временную таблицу / встраиваемый оператор SQL, содержащий все возможные идентификаторы (некоторые параметры описаны в этой статье ), вплоть до максимального (идентификатора) вашей таблицы.

Затем вы можете оставить соединение из этого канонического списка значений с вашей таблицей и отфильтровать нулевые значения справа.

0 голосов
/ 18 января 2010

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

...