SQL: УДАЛИТЬ данные из самоссылающейся таблицы в определенном порядке - PullRequest
5 голосов
/ 05 марта 2011

У меня есть такая таблица:

groupId guid PK
parentId guid
name
left int
right int

Существует внешний ключ от parentId до groupId (это таблица с самоссылкой).

слева и справа MPTT левый / правый значения для поддержания иерархии.Здесь важно отметить, что чем больше левое значение, тем глубже вложенный элемент (другими словами: для любого данного элемента его левое значение всегда будет больше, чем левое значение всех родителей).


Я пытаюсь написать быстрый оператор DELETE, чтобы удалить все, кроме самой верхней группы (которая всегда имеет пустое значение GUID), например:

DELETE FROM [group] WHERE [groupId] <> '00000000-0000-0000-0000-000000000000'`

К сожалению, это не 'Это работает, так как обычно есть подгруппы ниже любой данной группы, которые предотвращают удаление.Если вы выполняете этот запрос DELETE снова и снова, в конечном итоге он удалит все, но это, очевидно, не очень хорошее решение.

То, что я хочу, эквивалентно:

DELETE FROM [group] WHERE [groupId] <> '00000000-0000-0000-0000-000000000000' 
ORDER BY [left] DESC

Конечно, это не разрешенный синтаксис, но эффективно, он должен сначала удалить элементы с самыми большими левыми значениями, чтобы обеспечитьниже нет групп, которые будут препятствовать удалению из-за ограничений FK.

Я также попытался:

delete from [group] where groupid in (
  select top 1000000 * from [group] 
  where groupid <> '00000000-0000-0000-0000-000000000000' 
  ORDER BY [left] desc
)

, который является допустимым синтаксисом (вы можете использовать ORDER BY, только если вы также используете TOP), но на самом деле это не вызывает УДАЛЕНИЕ в порядке:возвращенные строки, так что это все еще не работает.

Возможно ли это сделать, не прибегая к курсору для удаления строк одна за другой?

Ответы [ 5 ]

5 голосов
/ 05 марта 2011

Было бы интересно узнать, какую версию SQL Server вы используете, потому что в SQL Server 2005 и 2008 план запроса показывает, что все удаления в кластеризованном индексе завершены до выполнения проверок ссылочной целостности. Проверка ограничений является отложенным!

Удаленные значения groupId записываются в Eager Spool после оператора delete. Затем оператор последовательности в корне плана воспроизводит вторую ветвь, которая считывает все строки из буфера и проверяет другие строки, которые ссылаются на удаленные строки, используя полусоединение. Последней операцией в плане является Assert, которая выдаст ошибку, если ссылочная целостность была нарушена.

Сценарий воспроизведения, используя данные, показанные в статье MPTT, на которую ссылается оригинальный вопрос:


USE     tempdb
;
IF      OBJECT_ID(N'dbo.Test', N'U')
        IS NOT NULL
        DROP TABLE dbo.Test
;
CREATE  TABLE dbo.Test
        (
        groupId     UNIQUEIDENTIFIER NOT NULL PRIMARY KEY NONCLUSTERED,
        parentId    UNIQUEIDENTIFIER REFERENCES dbo.Test(groupId) NULL,
        name        NVARCHAR(50) NOT NULL,
        lft         INTEGER NOT NULL,
        rgt         INTEGER NOT NULL,
        )
;
CREATE  NONCLUSTERED INDEX nc1 ON dbo.Test (parentId)
;
INSERT  dbo.Test (groupId, parentId, name, lft, rgt)
VALUES  ({guid '00000000-0000-0000-0000-000000000000'}, NULL, N'Food', 1, 18),
        ({guid '99163693-B535-47A1-9F5B-E5F235CEB8E2'}, {guid '00000000-0000-0000-0000-000000000000'}, N'Fruit', 2, 11),
        ({guid '6A3FFED1-0230-4909-A7D4-DD9CBA674A1D'}, {guid '99163693-B535-47A1-9F5B-E5F235CEB8E2'}, N'Red', 3, 6),
        ({guid '2E7A25D8-8555-4FEC-A0D3-8B8E60501F6A'}, {guid '6A3FFED1-0230-4909-A7D4-DD9CBA674A1D'}, N'Cherry', 4, 5),
        ({guid '20613684-1081-4A42-AEAE-F21250D56703'}, {guid '99163693-B535-47A1-9F5B-E5F235CEB8E2'}, N'Yellow', 7, 10),
        ({guid 'B6D5A4AB-1A10-4069-842D-AAE09E73B3CD'}, {guid '20613684-1081-4A42-AEAE-F21250D56703'}, N'Banana', 8, 9),
        ({guid '174FF0C3-BAE0-4023-8819-EC89E080834C'}, {guid '00000000-0000-0000-0000-000000000000'}, N'Meat', 12, 17),
        ({guid '3CA5356E-343B-461C-8602-B9ECAEA1304D'}, {guid '174FF0C3-BAE0-4023-8819-EC89E080834C'}, N'Beef', 13, 14),
        ({guid '79664130-ACDF-48CE-A33D-7473153819E8'}, {guid '174FF0C3-BAE0-4023-8819-EC89E080834C'}, N'Pork', 15, 16)
;
DELETE  dbo.Test
WHERE   groupId > {guid '00000000-0000-0000-0000-000000000000'}
;
2 голосов
/ 05 марта 2011

Попробуйте

ALTER TABLE [group] NOCHECK CONSTRAINT ALL
go
DELETE FROM [group] WHERE [groupId] <> '00000000-0000-0000-0000-000000000000'
go
ALTER TABLE [group] CHECK CONSTRAINT ALL
go

Если таблица большая, это может быть хорошим вариантом.

select * into #temptable from [group] WHERE [groupId] = '00000000-0000-0000-0000-000000000000'
go
truncate table [group]
go
select * into [group] from #temptable
go
drop table #temptable
go
0 голосов
/ 01 апреля 2011

Вы можете написать:

declare @idgroup guid;
set @idgroup = 'whatever';

WITH n AS
(
    SELECT groupId, parentId
        FROM Table WHERE groupId=@idgroup
    UNION ALL
    SELECT e.groupId, e.parentId
        FROM Table e JOIN n ON n.groupId=e.parentId
)
--Unlike some other systems, we don’t have to worry about the order the records are deleted, 
--since all SQL Server referential constraints are deferred till the end of the query.
DELETE FROM Table
WHERE groupId IN (SELECT groupId FROM n)

Это удалит определенную группу и всех ее потомков, не беспокоясь об ограничении FK и порядке

Также есть действительно хорошая статья о ссылочной целостностии каскадные действия здесь

0 голосов
/ 05 марта 2011

Не могли бы вы попробовать отключить проверку ограничений:

CHECK / NOCHECK CONSTRAINT
0 голосов
/ 05 марта 2011

Можете ли вы удалить FK, сделать удаление, а затем положить FK обратно, когда вы закончите?Кинда чувствует себя обманщиком, но она может выполнить свою работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...