2010-09-22 5 views
0

je les tableaux suivants:Essayer de résoudre requête SQL avec deux tables

Entrée
EntryID - int
EntryDate - datetime

Heure
EntryID - int
inhour - datetime
OutHour - datetime

Pour chaque registre dans la table d'entrée, il devrait y avoir au moins un (peut-être beaucoup) les registres sur la table heure, comme ceci:

Entrée
EntryID: 8
EntryDate: 9/9/2010 12:31:25

Heure
EntryID: 8
inhour: 9/9/2010 12:31:25
OutHour: 9/9/2010 18:21:19

Maintenant, cette information est stockée sur 2 bases de données égales, une sur la machine locale et une sur un serveur. J'essaye d'écrire une requête qui effacera toutes les informations qui ont déjà été passées au serveur sous la condition que les registres qui n'ont pas OutHour (null) ne seront pas supprimés.

j'ai écrit la requête suivante:

DELETE from [dbo].[Entry] 
WHERE [dbo].[Entry].[EntryID] IN (SELECT [EntryID] 
            FROM [LINKEDSERVER].[MYDATABASE].[dbo].[Entry]) 
    AND [dbo].[Entry].[EntryID] IN (SELECT [EntryID] 
            FROM [dbo].[Hour] 
            WHERE [OutHour] IS NOT NULL) 

DELETE from [dbo].[Hour] 
WHERE [dbo].[Hour].[InHour] IN (SELECT [InHour] 
            FROM [LINKEDSERVER].[MYDATABASE].[dbo].[Hour]) 
    AND [dbo].[Hour].[OutHour] IS NOT NULL 

AFAIK, cette requête vérifie d'abord dans la table d'entrée et supprimera tous les registres qui sont déjà sur le serveur et ne disposent pas d'un registre d'heure correspondant qui a une valeur nulle OutHour. Cependant aujourd'hui j'ai découvert qu'un enregistrement d'entrée a été supprimé mais l'heure correspondante n'était pas (il avait une OutHour nulle).

Qu'est-ce que je fais mal? Toute aide est appréciée.

Merci!

Répondre

0

Ma toute première suggestion est de mettre une relation de clé étrangère entre les deux sur EntryID. Cela empêchera toute suppression de la table Entry sans supprimer d'abord toutes les instances de la table Hour. Deuxièmement, avec une clé étrangère en place, vous devez le faire de l'enfant au parent (alias, commencer au bas de la hiérarchie). Cela signifie que je le ferais d'abord:

delete from dbo.Hour where OutHour is not null 
delete e 
from dbo.Entry e 
left outer join dbo.Hour h 
on e.entryid=h.entryid 
where h.entryid is null 
1

Ce qui ne va pas, c'est que votre deuxième requête utilise uniquement InHour, sans faire référence à l'EntryID. De plus, votre première requête a ses conditions complètement indépendantes les unes des autres, ce qui ne pose pas de problème si vos contraintes de table Hour sont correctes (la première colonne ne peut jamais être nulle quand la seconde n'est pas nulle).

Dans les bases de données relationnelles, il est préférable de prendre l'habitude de penser en termes de JOINs plutôt qu'en IN(). Utiliser IN() peut souvent renvoyer les mêmes résultats qu'un JOIN (avec quelques différences dans la gestion NULL) et obtient souvent même le même plan d'exécution, mais c'est # 1 une façon "détendue" de penser au problème qui ne prête pas lui-même bien à l'espace mental nécessaire pour écrire des requêtes complexes et # 2 ne peut pas comparer plusieurs valeurs à la fois, il ne peut faire qu'une seule comparaison (au moins dans SQL Server, car certains autres SGBD peuvent le faire). Permettez-moi de réécrire vos requêtes en tant que JOINs et peut-être que cela vous aidera à voir ce qui ne va pas.

DELETE E 
FROM 
    dbo.Entry E 
    INNER JOIN LINKEDSERVER.MYDATABASE.dbo.Entry L ON E.EntryID = L.EntryID 
    INNER JOIN Hour H ON E.EntryID = H.EntryID 
WHERE 
    H.OutHour IS NOT NULL 

DELETE H 
FROM 
    dbo.Hour H 
    INNER JOIN LINKEDSERVER.MYDATABASE.dbo.Hour L ON H.InHour L.InHour 
WHERE 
    H.OutHour IS NOT NULL 

Je vous recommande de mettre une cascade supprimer la contrainte de clé étrangère sur la table d'heure de sorte que lorsque vous supprimez de la table d'entrée, les lignes Hour enfants disparaissent tous. Il y a encore des problèmes ici car vous pourriez avoir plusieurs lignes d'heure par EntryID et sémantiquement vous pouvez finir par essayer de supprimer la même ligne sur le serveur lié plusieurs fois. De plus, sachez que les grosses connexions sur les serveurs liés peuvent connaître de très mauvaises performances car le moteur de recherche décide parfois de tirer d'énormes ensembles de lignes sur le lien, même des tables entières. Vous pouvez atténuer cela en faisant des choses par lots, peut-être en faisant d'abord un select dans une table temporaire basée sur un JOIN à travers le lien, puis en supprimant les lignes correspondantes en petits lots de 100, 1000 ou 5000 (test pour trouver le bon Taille). Enfin, si vous trouvez que vos requêtes entraînent inutilement d'énormes ensembles de données sur le lien (déterminez ceci en exécutant Query Profiler sur la machine à correspondance distante pour voir quelles requêtes sont soumises), puis utilisez stratégiquement CROSS APPLY peut aider en forçant le traitement rangée-par-rangée, qui dans le cas de serveurs liés peut être une amélioration de performance énorme, malgré la façon contre-intuitive qui est comparée à la recommandation standard et forte de ne jamais faire rangée par rangée dans bases de données relationnelles. Pensez-y comme forçant une "recherche de signet extensible" plutôt qu'un "scan de table extensible" et vous aurez une idée de pourquoi cela peut être une grande aide.

Questions connexes