Как я могу эффективно сделать масштабное обновление базы данных? - PullRequest
1 голос
/ 09 апреля 2009

У меня есть таблица с некоторыми повторяющимися записями. Я должен отказаться от всех, кроме одного, а затем обновить этот последний. Я попытался с временной таблицей и оператором while, таким образом:

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

но это занимает слишком много времени, даже если обрабатывается только 20000 - 30000 строк.

У кого-нибудь есть предложения по улучшению производительности?

Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 09 апреля 2009
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 голос
/ 09 апреля 2009

В ответе Quassnoi используется синтаксис SQL Server 2005+, поэтому я подумал, что я бы хотел использовать что-то более общее ...

Во-первых, чтобы удалить все дубликаты, кроме «оригинала», вам нужен способ отличить дубликаты записей друг от друга. (ROW_NUMBER () часть ответа Quassnoi)

Может показаться, что в вашем случае исходные данные не имеют столбца идентификаторов (вы создаете его во временной таблице). Если это так, мне на ум приходят два варианта:
1. Добавьте идентификационный столбец к данным, затем удалите дубликаты
2. Создайте «дедуплицированный» набор данных, удалите все из оригинала и вставьте дедуплицированные данные обратно в оригинал

Вариант 1 может быть что-то вроде ... (С вновь созданным полем ID)

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
        )

OR ...

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 голосов
/ 07 июля 2009

Я не очень хорошо понимаю используемый синтаксис, чтобы опубликовать точный ответ, но вот подход.

Определите строки, которые вы хотите сохранить (например, выберите значение, ... из .. где ...)

Выполнять логику обновления при идентификации (например, выбрать значение + 1 ... из ... где ...)

Вставить выбор в новую таблицу.

Удаление оригинала, переименование нового в оригинал, воссоздание всех грантов / синонимов / триггеров / индексов / FKs / ... (или усечение оригинала и вставка из нового выбора)

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

...