Comment SQL Delete peut-il utiliser une sous-requête

15

Le code suivant a été ajouté par l'un de nos développeurs pour supprimer les enregistrements en double de la table:

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

Lors de l'examen du code, j'ai supposé qu'il ne fonctionnerait pas, mais le tester dans notre environnement de test (SQL 2014) montre que c'est le cas!

Comment SQL sait-il comment résoudre la sous-requête et supprimer les enregistrements table?

Greg
la source

Réponses:

14

Le que subqueryvous avez dans votre code est appelé une table dérivée . Ce n'est pas une table de base mais une table qui "vit" pendant la durée d'exécution de la requête. Comme les vues (qui sont également appelées tables vues ) - et dans les versions récentes CTE qui est une autre, 4ème façon de "définir" une table à l'intérieur d'une requête - elles sont similaires à une table à bien des égards. Vous pouvez à selectpartir d'eux, vous pouvez les utiliser dans fromou pour joineux à d'autres tables (de base ou non!).

Dans certains SGBD (tous les SGBD ne l'ont pas implémenté de la même manière), ces tables / vues peuvent être mises à jour . Et «actualisable» signifie que nous pouvons aussi update, insertvers ou deletedepuis eux.

Il existe cependant des restrictions et cela est prévu. Imaginez si le subqueryétait une jointure de 2 (ou 17 tables). Que deletesignifierait alors? (de quelles tables les lignes doivent-elles être supprimées?) Les vues pouvant être mises à jour sont très compliquées . Il y a un livre récent (2012), entièrement sur ce sujet, écrit par Chris Date, expert bien connu en théorie relationnelle: View Updating and Relational Theory .

Lorsque la table dérivée (ou la vue) est une requête très simple, comme elle n'a qu'une seule table de base (éventuellement restreinte par a WHERE) et non GROUP BY, chaque ligne de la table dérivée correspond à une ligne de la table de base sous-jacente, il est donc facile * à mettre à jour, insérer ou supprimer de cela.

Lorsque le code à l'intérieur de la sous-requête est plus complexe, cela dépend si les lignes de la table / vue dérivée peuvent être tracées / résolues en lignes de l'une des tables de base sous-jacentes.

Pour SQL Server, vous pouvez en savoir plus dans les vues actualisables paragraphe MSDN: CREATE VIEW.

Vues actualisables

Vous pouvez modifier les données d'une table de base sous-jacente via une vue, tant que les conditions suivantes sont remplies:

  • Toute modification, y compris UPDATE, INSERTet des DELETEdéclarations, des colonnes de référence doit partir d' une seule table de base.

  • Les colonnes en cours de modification dans la vue doivent référencer directement les données sous-jacentes dans les colonnes du tableau. Les colonnes ne peuvent pas être dérivées d'une autre manière, par exemple par le biais des éléments suivants:

  • Une fonction d' agrégation: AVG, COUNT, SUM, MIN, MAX, GROUPING, STDEV, STDEVP, VAR, et VARP.

  • Un calcul. La colonne ne peut pas être calculée à partir d'une expression qui utilise d'autres colonnes. Les colonnes qui sont formées en utilisant les opérateurs d'ensemble UNION, UNION ALL, CROSSJOIN, EXCEPT, et INTERSECT montant à un calcul et ne sont pas non modifiable.

  • Les colonnes modifiées ne sont pas affectées par GROUP BY, HAVINGou DISTINCTclauses.

  • TOPn'est utilisé nulle part dans l'instruction select_statement de la vue avec la WITH CHECK OPTIONclause.

Les restrictions précédentes s'appliquent à toutes les sous-requêtes de la FROMclause de la vue, tout comme elles s'appliquent à la vue elle-même. En règle générale, le moteur de base de données doit pouvoir tracer sans ambiguïté les modifications de la définition de la vue vers une table de base.


En fait, deletec'est plus facile, moins complexe que update. SQL Server n'a besoin que des clés primaires ou d'une autre manière pour identifier les lignes de la table de base à supprimer. Pour update, il existe une restriction supplémentaire (plutôt évidente) selon laquelle nous ne pouvons pas mettre à jour une colonne calculée. Vous pouvez essayer de modifier votre requête pour effectuer une mise à jour. La mise à jour de CreatedDateTimefonctionnera probablement très bien, mais essayer de mettre à jour la RowNumbercolonne calculée soulèvera une erreur. Et insertc'est encore plus complexe, car il faudrait fournir des valeurs pour toutes les colonnes de la table de base qui n'ont pas de DEFAULTcontrainte.

ypercubeᵀᴹ
la source
4

Il est facile de voir lorsque vous regardez le plan de requête. Dans votre cas, le plan contient simplement un opérateur de projet de segment et de séquence supplémentaire pour gérer le numéro de ligne. Ce type d'opération ne fonctionne que lorsque SQL Server peut réellement résoudre la table sous-jacente.

La suppression des sous-requêtes et des CTE est entièrement prise en charge et très efficace, en particulier pour supprimer les doublons. Je me souviens aussi de l'utiliser sur des versions plus anciennes de SQL Server.

Plus dans un ancien blog de la mienne.

Daniel Hutmacher
la source