Как SQL Удалить, используя подзапрос

15

Следующий код был добавлен одним из наших разработчиков для удаления дублирующихся записей из таблицы:

DELETE  SubQuery

FROM
(
    SELECT  ID
            ,FK1
            ,FK2
            ,CreatedDateTime
            ,ROW_NUMBER() OVER(PARTITION BY FK1, FK2 ORDER BY CreatedDateTime) AS RowNumber

    FROM    Table
)
AS SubQuery

WHERE   RowNumber > 1

При рассмотрении кода я предполагал, что он не будет работать, однако тестирование в нашей тестовой среде (SQL 2014) показывает, что это работает!

Как SQL знает, как разрешить подзапрос и удалить записи table?

Greg
источник

Ответы:

14

То, subqueryчто у вас есть в вашем коде, называется производной таблицей . Это не базовая таблица, а таблица, которая «живет» во время выполнения запроса. Подобно представлениям (которые также называются просматриваемыми таблицами ) - и в последних версиях CTE, что является еще одним, четвертым способом «определения» таблицы внутри запроса - они во многом похожи на таблицу. Вы можете selectиз них, вы можете использовать их в fromили joinк другим таблицам (базы или нет!).

В некоторых СУБД (не все СУБД реализовали это одинаково) эти таблицы / представления являются обновляемыми . И «обновляемый» означает, что мы также можем update, insertв или deleteиз них.

Хотя есть ограничения, и это ожидается. Представьте, если это subqueryбыло объединение 2 (или 17 таблиц). Что бы deleteтогда значило? (из каких таблиц следует удалять строки?) Обновляемые представления - это очень сложный вопрос . Недавно вышла книга (2012), полностью посвященная этой теме, написанная Крисом Дейтом, известным специалистом по теории отношений: « Обновление представлений и теория отношений» .

Когда производная таблица (или представление) представляет собой очень простой запрос, например, он имеет только одну базовую таблицу (возможно, ограниченную символом a WHERE) и no GROUP BY, тогда каждая строка производной таблицы соответствует одной строке в базовой базовой таблице, поэтому легко * обновить, вставить или удалить из этого.

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

Для SQL Server, вы можете прочитать в пункте обновляемых представлений в MSDN: CREATE VIEW.

Обновляемые виды

Вы можете изменить данные базовой базовой таблицы через представление, если выполняются следующие условия:

  • Любые модификации, в том числе UPDATE, INSERTи DELETEутверждения, должны ссылаться на столбцы только из одной базовой таблицы.

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

  • Агрегатная функция: AVG, COUNT, SUM, MIN, MAX, GROUPING, STDEV, STDEVP, VAR, и VARP.

  • Вычисление. Столбец не может быть вычислен из выражения, которое использует другие столбцы. Столбцы, которые сформированы с использованием набора операторов UNION, UNION ALL, CROSSJOIN, EXCEPT, и INTERSECT количества к вычислению и также не обновляемые.

  • На изменяемые столбцы не распространяются ни слова GROUP BY, HAVINGни DISTINCTпредложения.

  • TOPне используется нигде в select_statement представления вместе с WITH CHECK OPTIONпредложением.

Предыдущие ограничения применяются к любым подзапросам в FROMпредложении представления так же, как они применяются к самому представлению. Как правило, компонент Database Engine должен иметь возможность однозначно отслеживать изменения из определения представления в одной базовой таблице.


На самом деле deleteэто проще, менее сложно, чем update. SQL Server нужны только первичные ключи или какой-либо другой способ определить, какие строки базовой таблицы следует удалить. Поскольку updateесть дополнительное (довольно очевидное) ограничение, что мы не можем обновить вычисляемый столбец. Вы можете попытаться изменить свой запрос, чтобы сделать обновление. Обновление CreatedDateTime, вероятно, будет работать нормально, но попытка обновить вычисляемый RowNumberстолбец вызовет ошибку. И insertэто еще более сложно, так как мы должны были бы предоставить значения для всех столбцов базовой таблицы, которые не имеют DEFAULTограничений.

ypercubeᵀᴹ
источник
4

Это легко увидеть, когда вы посмотрите на план запроса. В вашем случае план просто содержит дополнительный оператор Segment and Sequence Project для обработки номера строки. Этот тип операции работает только тогда, когда SQL Server действительно может разрешить базовую таблицу.

Удаление из подзапросов и CTE полностью поддерживается и очень эффективно, особенно для удаления дубликатов. Я также вспоминаю, как использовал его в старых версиях SQL Server.

Больше в моем старом блоге .

Даниэль Хутмахер
источник