Question

Cela a été une aventure. J'ai commencé avec la requête de duplication en boucle située dans la question précédente , mais chaque boucle couvrirait la totalité des 17 millions d'enregistrements , signifie que cela prendrait des semaines (exécuter simplement * select count * sur MyTable * prend 4h30 minutes à mon serveur avec MSSQL 2005). J'ai lu des informations sur ce site et sur ce post .

Et sommes arrivés à la requête ci-dessous. La question est de savoir si le type de requête à exécuter sur 17 millions d'enregistrements est correct, quel que soit le type de performance. Si ce n’est pas le cas, qu’est-ce?

SQL QUERY:

DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in 
(SELECT RecordID
    FROM tl_acxiomimport.dbo.tblacxiomlistings
    EXCEPT
    SELECT RecordID
    FROM (
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude,           Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
    FROM tl_acxiomimport.dbo.tblacxiomlistings
    ) al WHERE Rank = 1)
Était-ce utile?

La solution

Voir le QueryPlan aiderait.

Est-ce faisable?

SELECT m.*
into #temp
FROM tl_acxiomimport.dbo.tblacxiomlistings m 
inner join (SELECT RecordID, 
                   Rank() over (Partition BY BusinessName, 
                                             latitude,  
                                             longitude,            
                                             Phone  
                                ORDER BY webaddress DESC,  
                                         caption1 DESC,  
                                         caption2 DESC ) AS Rank
              FROM tl_acxiomimport.dbo.tblacxiomlistings
           ) al on (al.RecordID = m.RecordID and al.Rank = 1)

truncate table tl_acxiomimport.dbo.tblacxiomlistings

insert into tl_acxiomimport.dbo.tblacxiomlistings
     select * from #temp

Autres conseils

Quelque chose ne va pas avec votre base de données, serveur, stockage ou une combinaison de ceux-ci. 4:30 pour un compte choisi * semble TRÈS élevé.

Exécutez un DBCC_SHOWCONTIG pour voir le degré de fragmentation de votre table. Cela pourrait entraîner une baisse importante des performances sur une table de cette taille.

De plus, pour ajouter quelque chose au commentaire de RyanKeeter, exécutez le plan d’exposition et, s’il existe des analyses de table, créez un index pour le champ PK de cette table.

Ne serait-il pas plus simple de faire:

DELETE tl_acxiomimport.dbo.tblacxiomlistings
WHERE RecordID in 
(SELECT RecordID
   FROM (
        SELECT RecordID,
            Rank() over (Partition BY BusinessName,
                                  latitude,
                                  longitude,
                                  Phone
                         ORDER BY webaddress DESC,
                                  caption1 DESC,
                                  caption2 DESC) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        )
  WHERE Rank > 1
  )

Exécuter ceci dans l'analyseur de requête:

SET SHOWPLAN_TEXT ON

Demandez ensuite à l’analyseur de requête d’exécuter votre requête. Au lieu d'exécuter la requête, SQL Server génère un plan de requête et le place dans le jeu de résultats.

Affichez-nous le plan de requête.

17 millions d’enregistrements, ce n’est rien. S'il faut 4 h 30 pour effectuer un compte sélectionné (*), il existe un problème grave, probablement lié au manque de mémoire du serveur ou à un processeur très ancien.

Pour améliorer les performances, réparez la machine. Pomper jusqu'à 2 Go. La mémoire vive est tellement bon marché de nos jours que son coût est bien inférieur à votre temps.

Est-ce que le processeur ou le disque se débat lorsque cette requête est en cours? Sinon, quelque chose bloque les appels. Dans ce cas, vous pouvez envisager de placer la base de données en mode mono-utilisateur pendant le temps nécessaire pour exécuter le nettoyage.

Vous supprimez donc tous les enregistrements qui ne sont pas classés en premier? Il serait peut-être intéressant de comparer une jointure à une première requête (ce qui pourrait également fonctionner en 2000, car le rang est 2005 et au-dessus uniquement).

Avez-vous besoin de supprimer tous les doublons en une seule opération? Je suppose que vous effectuez une sorte de tâche d’entretien, vous pourrez peut-être le faire par morceaux.

Fondamentalement, créez un curseur qui boucle tous les enregistrements (lecture incorrecte) et supprime les doublons pour chacun. Ce sera globalement beaucoup plus lent, mais chaque opération sera relativement minime. Ensuite, votre entretien devient une tâche de fond constante plutôt qu'un lot nocturne.

La suggestion ci-dessus de sélectionner d'abord une table temporaire est votre meilleur choix. Vous pouvez également utiliser quelque chose comme:

set rowcount 1000

avant d'exécuter votre suppression. Il cessera de fonctionner après la suppression des 1000 lignes. Puis exécutez-le encore et encore jusqu'à ce que vous obteniez 0 enregistrements supprimés.

si je l'obtiens correctement, votre requête est la même que

DELETE tl_acxiomimport.dbo.tblacxiomlistings
FROM
    tl_acxiomimport.dbo.tblacxiomlistings allRecords
    LEFT JOIN (   
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        WHERE Rank = 1) myExceptions
    ON allRecords.RecordID = myExceptions.RecordID
WHERE
    myExceptions.RecordID IS NULL

Je pense que cela devrait fonctionner plus vite, j'ai tendance à éviter d'utiliser "IN". clause en faveur des JOINs si possible.

Vous pouvez réellement tester la vitesse et les résultats en toute sécurité en appelant simplement SELECT * ou SELECT COUNT (*) dans la partie FROM, comme par exemple

.
SELECT *
FROM
    tl_acxiomimport.dbo.tblacxiomlistings allRecords
    LEFT JOIN (   
        SELECT RecordID, Rank() over (Partition BY BusinessName, latitude, longitude, Phone ORDER BY webaddress DESC, caption1 DESC, caption2 DESC ) AS Rank
        FROM tl_acxiomimport.dbo.tblacxiomlistings
        WHERE Rank = 1) myExceptions
    ON allRecords.RecordID = myExceptions.RecordID
WHERE
    myExceptions.RecordID IS NULL

C'est une autre raison pour laquelle je préférerais l'approche JOIN J'espère que ça aide

Cela semble bien, mais vous pouvez envisager de sélectionner vos données dans une table temporaire et de les utiliser dans votre instruction delete. J'ai constaté des gains de performances énormes en faisant cela au lieu de tout faire dans cette requête.

N'oubliez pas que lorsque vous effectuez une suppression volumineuse, il est préférable de commencer par effectuer une bonne sauvegarde (et je copie également les enregistrements supprimés dans une autre table au cas où, il faudrait que je les récupère immédiatement.)

À part l'utilisation de tronquer comme suggéré, j'ai eu la meilleure chance d'utiliser ce modèle pour supprimer de nombreuses lignes d'un tableau. Je ne me souviens plus très bien, mais je pense que l’utilisation de la transaction a permis d’empêcher le fichier journal de grandir - c’était peut-être une autre raison - je ne suis pas sûr. Et je passe généralement de la méthode de journalisation des transactions à simple avant de faire quelque chose comme ceci:

SET ROWCOUNT 5000
WHILE 1 = 1
BEGIN
    begin tran
            DELETE FROM ??? WHERE ???
            IF @@rowcount = 0
            BEGIN
               COMMIT
               BREAK
            END
    COMMIT
END
SET ROWCOUNT 0
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top