2009-04-09 8 views
1

J'ai une table avec quelques entrées en double. Je dois me débarrasser de tous sauf un, puis mettre à jour ce dernier. Je l'ai essayé avec une table temporaire et une déclaration while, ainsi:Comment puis-je effectuer efficacement une mise à jour massive de base de données?

CREATE TABLE #tmp_ImportedData_GenericData 
(
    Id int identity(1,1), 
    tmpCode varchar(255) NULL, 
    tmpAlpha3Code varchar(50) NULL, 
    tmpRelatedYear int NOT NULL, 
    tmpPreviousValue varchar(255) NULL, 
    tmpGrowthRate varchar(255) NULL 
) 

INSERT INTO #tmp_ImportedData_GenericData 
SELECT 
    MCS_ImportedData_GenericData.Code, 
MCS_ImportedData_GenericData.Alpha3Code, 
MCS_ImportedData_GenericData.RelatedYear, 
MCS_ImportedData_GenericData.PreviousValue, 
MCS_ImportedData_GenericData.GrowthRate 
FROM MCS_ImportedData_GenericData 
INNER JOIN 
(
    SELECT CODE, ALPHA3CODE, RELATEDYEAR, COUNT(*) AS NUMROWS 
    FROM MCS_ImportedData_GenericData AS M 
    GROUP BY M.CODE, M.ALPHA3CODE, M.RELATEDYEAR 
    HAVING count(*) > 1 
) AS M2 ON MCS_ImportedData_GenericData.CODE = M2.CODE 
    AND MCS_ImportedData_GenericData.ALPHA3CODE = M2.ALPHA3CODE 
    AND MCS_ImportedData_GenericData.RELATEDYEAR = M2.RELATEDYEAR 
WHERE 
(MCS_ImportedData_GenericData.PreviousValue <> 'INDEFINITO') 

-- SELECT * from #tmp_ImportedData_GenericData 
-- DROP TABLE #tmp_ImportedData_GenericData 

DECLARE @counter int 
DECLARE @rowsCount int 

SET @counter = 1 

SELECT @rowsCount = count(*) from #tmp_ImportedData_GenericData 
-- PRINT @rowsCount 

WHILE @counter < @rowsCount 
BEGIN 
    SELECT 
     @Code = tmpCode, 
     @Alpha3Code = tmpAlpha3Code, 
     @RelatedYear = tmpRelatedYear, 
     @OldValue = tmpPreviousValue, 
     @GrowthRate = tmpGrowthRate 
    FROM 
     #tmp_ImportedData_GenericData 
    WHERE 
     Id = @counter 

    DELETE FROM MCS_ImportedData_GenericData 
    WHERE 
     Code = @Code 
     AND Alpha3Code = @Alpha3Code 
     AND RelatedYear = @RelatedYear 
     AND PreviousValue <> 'INDEFINITO' OR PreviousValue IS NULL 

    UPDATE 
     MCS_ImportedData_GenericData 
     SET 
      PreviousValue = @OldValue, GrowthRate = @GrowthRate 
    WHERE 
     Code = @Code 
     AND Alpha3Code = @Alpha3Code 
     AND RelatedYear = @RelatedYear 
     AND MCS_ImportedData_GenericData.PreviousValue ='INDEFINITO' 

    SET @counter = @counter + 1 
END 

mais il prend trop longtemps, même s'il y a seulement 20000-30000 lignes à traiter.

Est-ce que quelqu'un a des suggestions pour améliorer les performances?

Merci d'avance!

+0

n'utilisez pas de boucle! –

+0

D'accord, alors que TSQL inclut des boucles, il n'est pas optimisé pour elles. – MatBailie

+0

S'il s'agit d'un serveur spécifique à Microsoft SQL, veuillez le marquer en tant que sqlserver. Eh bien soit cela ou je vais abandonner et juste ignorer le tag SQL. – Thomas

Répondre

3
WITH q AS (
     SELECT m.*, ROW_NUMBER() OVER (PARTITION BY CODE, ALPHA3CODE, RELATEDYEAR ORDER BY CASE WHEN PreviousValue = 'INDEFINITO' THEN 1 ELSE 0 END) 
     FROM MCS_ImportedData_GenericData m 
     WHERE PreviousValue <> 'INDEFINITO' 
     ) 
DELETE 
FROM q 
WHERE rn > 1 
1

La réponse de Quassnoi utilise SQL Server 2005+ syntaxe, donc je pensais que je l'avais mis dans ma tuppence valeur à l'aide quelque chose de plus générique ...

Tout d'abord, de supprimer tous les doublons, mais pas la " original ", vous avez besoin d'un moyen de différencier les enregistrements en double les uns des autres. (La partie ROW_NUMBER() de la réponse de Quassnoi)

Il semblerait que dans votre cas, les données source n'ont pas de colonne d'identité (vous en créez une dans la table temporaire). Si tel est le cas, il y a deux choix qui me viennent à l'esprit:
1. Ajouter la colonne d'identité aux données, puis supprimer les doublons
2. Créer un ensemble de données "dé-dupé", tout supprimer de l'original, et insérer les données de-deduped retour dans l'original

Option 1 pourrait être quelque chose comme ... (Avec le champ ID nouvellement créé)

DELETE 
    [data] 
FROM 
    MCS_ImportedData_GenericData AS [data] 
WHERE 
    id > (
     SELECT 
      MIN(id) 
     FROM 
      MCS_ImportedData_GenericData 
     WHERE 
      CODE = [data].CODE 
      AND ALPHA3CODE = [data].ALPHA3CODE 
      AND RELATEDYEAR = [data].RELATEDYEAR 
     ) 

OU ...

DELETE 
    [data] 
FROM 
    MCS_ImportedData_GenericData AS [data] 
INNER JOIN 
(
    SELECT 
     MIN(id) AS [id], 
     CODE, 
     ALPHA3CODE, 
     RELATEDYEAR 
    FROM 
     MCS_ImportedData_GenericData 
    GROUP BY 
     CODE, 
     ALPHA3CODE, 
     RELATEDYEAR 
) 
AS [original] 
    ON [original].CODE = [data].CODE 
    AND [original].ALPHA3CODE = [data].ALPHA3CODE 
    AND [original].RELATEDYEAR = [data].RELATEDYEAR 
    AND [original].id <> [data].id 
0

Je ne comprends pas assez la syntaxe utilisée pour poster une réponse exacte, mais voici une approche.

identifier les lignes que vous souhaitez conserver (par exemple. Sélectionner la valeur, ... de .. où ...)

Faites la logique de mise à jour tout en identifiant (par exemple. Sélectionner la valeur + 1 ... de .. où ...)

Insérez select dans une nouvelle table.

déposer l'original, renommer nouveau à l'original, recréer toutes les subventions/synonymes/déclencheurs/index/FKs/... (ou tronquer l'original et insérez sélection de la nouvelle)

Il est évident que cela a un grand Préty frais généraux, mais si vous voulez mettre à jour/effacer des millions de lignes, ce sera le moyen le plus rapide.