Удаление старых записей при сохранении минимального количества записей на одного родителя - PullRequest
1 голос
/ 10 февраля 2012

Мой вопрос аналогичен выражению sql для удаления записей старше XXX, если количество строк превышает YY , но этот вопрос касается только одного родителя, я хочу удалить записи для всех родителейза один раз.

Рассмотрим следующую таблицу:

CREATE TABLE Children
(
    ChildId int NOT NULL,
    ChildCreated datetime NOT NULL,
    ParentId int NOT NULL
) 

Это могут быть любые отношения родитель-потомок, поэтому имена являются общими.

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

Я пробовал некоторые утверждения с вложенными SELECTsи GROUP BYs, которые дали мне некоторые результаты, но ни один не дал мне правильный набор результатов.

Поскольку я использую SQL Server, я пришел к следующему решению, которое прекрасно работает:

WITH CTE AS
(
  SELECT ROW_NUMBER() OVER (Partition BY ParentId ORDER BY ChildCreated DESC) 
  As RowNo, ChildCreated FROM Children
)

DELETE FROM CTE WHERE RowNo > 10
AND RevisionCreated < DATEADD(MONTH,-1,GetDate())

Общее табличное выражение группирует все дочерние элементы для каждого родителя вместе и добавляет непрерывный номер строки на основе номерана создание заказа.Самый новый дочерний элемент для каждого родителя имеет номер строки 1, десятый самый новый имеет 10. Таким образом, я могу просто удалить все записи с номером строки больше 10, если они также старше месяца.

МойВопрос в том, что если я должен сделать то же самое в системе, где CTE не поддерживаются.Каково решение ANSI SQL-92 для этой проблемы?

Ответы [ 2 ]

2 голосов
/ 10 февраля 2012

Основываясь на других ответах и ​​относительной простоте моего запроса, я думаю, что, возможно, слишком упрощаю проблему, но я предполагаю, так как parentID не обнуляется, что он не ссылается на childID, и в этом случае это может быть достигнуто просто как показано ниже

DELETE  Children
FROM    Children a
WHERE   ChildCreated < DATEADD(MONTH, -1, GETDATE())
AND     (   SELECT  COUNT(*)    -- NUMBER OF NEWER CHILDREN WITH THE SAME PARENT
            FROM    Children b
            WHERE   a.ParentID = b.ParentID
            AND (   a.ChildCreated < b.ChildCreated
                OR  (a.ChildCreated = b.ChildCreated AND a.ChildID > b.ChildID)
                )
        ) >= 10

Хотя этот точный SQL может нуждаться в настройке в зависимости от СУБД, я не знаю ни одной СУБД, где этот принципал не может быть применен.

2 голосов
/ 10 февраля 2012

Скорее всего, снижение производительности, но следующий оператор

  • добавляет rownumber, перезапускаясь для каждой группы, используя подвыбор для каждой записи из числа дочерних элементов той же группы.
  • возвращает таблицу в памяти, содержащую ChildId с указанным номером.
  • JOIN вернуться к исходной таблице на ChildId
  • добавляет WHEREпредложение, в котором вы можете фильтровать данные по rownumber и / или дополнительным столбцам из Children.
  • использует результат в выражении DELETE FROM.

Оператор SQL

DELETE FROM Children
FROM    Children c
        INNER JOIN (
          SELECT  ChildId
                  , ( SELECT COUNT(*) + 1
                      FROM   Children rn
                      WHERE  rn.ChildCreated < Children.ChildCreated
                             AND rn.ChildId = Children.ParentId
                      ) AS rn           
          FROM    Children
        ) rn ON rn.ChildId = c.ChildId          
WHERE   rn.rn > 10
        AND ChildCreated < DATEADD(MONTH,-1,GetDate())
...