Удаление нечетких строк - PullRequest
7 голосов
/ 02 августа 2011

У меня есть таблица с уникальным некластеризованным индексом, и 4 столбца перечислены в этом индексе.Я хочу обновить большое количество строк в таблице.Если я это сделаю, они больше не будут различаться, поэтому обновление завершится неудачно из-за индекса.

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

SELECT t.itemid, t.fieldid, t.version, updated
FROM dbo.VersionedFields w
inner JOIN
(
    SELECT itemid, fieldid, version, COUNT(*) AS QTY
    FROM dbo.VersionedFields
    GROUP BY itemid, fieldid, version
    HAVING COUNT(*) > 1
) t 
on w.itemid = t.itemid and w.fieldid = t.fieldid and w.version = t.version

Выбор во внутреннем объединении возвращает правильное количество записей, которые мы хотим удалить, но группирует их, так что фактически их вдвое больше.

После объединения отображаются все записи, но я хочу удалить только самые старые?

Как это можно сделать?

Ответы [ 4 ]

11 голосов
/ 02 августа 2011

Если вы говорите SQL (язык структурированных запросов), но на самом деле имеете в виду SQL Server (система баз данных Microsoft), а если вы используете SQL Server 2005 или более новую версию, вы можете использовать CTE (CommonТаблица Expression) для этой цели.

С помощью этого CTE вы можете разделить ваши данные по некоторым критериям - например, ItemId (или комбинация столбцов) - и иметь номер SQL Server для всех ваших строк, начиная с 1для каждого из этих разделов, упорядоченных по некоторым другим критериям - то есть, вероятно, version (или некоторый другой столбец).

Так что попробуйте что-то вроде этого:

;WITH PartitionedData AS
(
    SELECT 
       itemid, fieldid, version, 
       ROW_NUMBER() OVER(PARTITION BY ItemId ORDER BY version DESC) AS 'RowNum'
    FROM dbo.VersionedFields
)
DELETE FROM PartitionedData
WHERE RowNum > 1

По сути, выразделение ваших данных по некоторым критериям и нумерация каждого раздела, начиная с 1 для каждого нового раздела, упорядоченного по некоторым другим критериям (например, Дата или версия).

Таким образом, для каждого «раздела» данных «самый новый»запись имеет RowNum = 1, и любые другие, принадлежащие к одному и тому же разделу (с помощью одинаковых значений partitino), будут иметь последовательно пронумерованные значения от 2 до любого числа r.В этом разделе есть записи.

Если вы хотите сохранить только самую новую запись - удалите что-нибудь с RowNum больше 1, и все готово!

4 голосов
/ 02 августа 2011

В SQL Server 2005 и выше:

WITH    q AS
        (
        SELECT  *,
                ROW_NUMBER() OVER (PARTITION BY itemid, fieldid, version ORDER BY updated DESC) AS rn
        FROM    versionedFields
        )
DELETE
FROM    q
WHERE   rn > 1
0 голосов
/ 02 августа 2011

Вам, вероятно, нужно взглянуть на этот ответ переполнения стека (удалить ранее повторяющиеся строки).

По существу, метод использует группировку (или опционально, управление окнами), чтобы найти минимальное значение идентификаторагруппы для того, чтобы удалить его.Может быть более точным удалить строки, в которых значение <> max (идентификатор строки).

Итак:

  1. Удалить уникальный индекс
  2. Загрузить данные
  3. Удалите данные, используя механизм группировки (в идеале, в транзакции, чтобы можно было выполнить откат в случае ошибки), а затем зафиксировать
  4. Создать индекс заново.

Обратите внимание, чтопересоздание индекса на большой таблице может занять много времени.

0 голосов
/ 02 августа 2011

Попробуйте что-то вроде:

DELETE FROM dbo.VersionedFields w WHERE w.version < (SELECT MAX(version) FROM dbo.VersionedFields)

Конечно, вы хотите ограничить MAX (версию) только версиями поля, которое вы хотите удалить.

...