Как использовать рекурсивный CTE в проверочном ограничении? - PullRequest
0 голосов
/ 31 марта 2019

Я пытаюсь создать check constraint для таблицы, чтобы ParentID никогда не являлся потомком текущей записи

Например, у меня есть таблица Categories со следующими полями ID, Name, ParentID

У меня есть следующее CTE

WITH Children AS (SELECT ID AS AncestorID, ID, ParentID AS NextAncestorID FROM Categories UNION ALL SELECT Categories.ID, Children.ID, Categories.ParentID FROM Categories JOIN Children ON Categories.ID = Children.NextAncestorID) SELECT ID FROM Children where AncestorID =99

Результаты здесь верны, но когда я пытаюсь добавить это как ограничение к таблице, как это:

ALTER TABLE dbo.Categories ADD CONSTRAINT CK_Categories CHECK (ParentID NOT IN(WITH Children AS (SELECT ID AS AncestorID, ID, ParentID AS NextAncestorID FROM Categories UNION ALL SELECT Categories.ID, Children.ID, Categories.ParentID FROM Categories JOIN Children ON Categories.ID = Children.NextAncestorID) SELECT ID FROM Children where AncestorID =ID))

Я получаю следующую ошибку:

Неправильный синтаксис рядом с ключевым словом «с».Если этот оператор является общим табличным выражением, предложением xmlnamespaces или предложением контекста отслеживания изменений, предыдущий оператор должен заканчиваться точкой с запятой.

Добавление точки с запятой перед WITH не былоhelp.

Как правильно это сделать?

Спасибо!

1 Ответ

1 голос
/ 01 апреля 2019

В соответствии с документацией SQL Server по ограничениям столбцов :

ПРОВЕРЬТЕ

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

logical_expression

Является логическим выражением, используемым в ограничении CHECK, и возвращает TRUE или FALSE. Логическое_выражение, используемое с ограничениями CHECK, не может ссылаться на другую таблицу, но может ссылаться на другие столбцы в той же таблице для той же строки. Выражение не может ссылаться на псевдоним типа данных.

(Вышеприведенное было процитировано из версии документации по SQL Server 2017, но общий принцип применим и ко всем предыдущим версиям, и вы не указали, с какой версией вы работаете.)

Важной частью здесь является «не может ссылаться на другую таблицу, но может ссылаться на другие столбцы в той же таблице для той же строки » (выделение добавлено). CTE будет считаться другой таблицей.

Таким образом, вы не можете использовать сложный запрос, такой как CTE, для ограничения CHECK. Как предложил Саман, если вы хотите проверить существующие данные в других строках, и они должны быть на уровне БД, вы можете сделать это как триггер.

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

Как Сами предложил в своем комментарии, еще один вариант - UDF, но это не связано с его собственными проблемами, потенциально с производительностью и стабильностью в соответствии с ответами на этот вопрос об этом подходе в SQL Server 2008 . Это, вероятно, все еще применимо и к более поздним версиям.

Если возможно, я бы сказал, что обычно лучше перенести эту логику на прикладной уровень. Я вижу из вашего комментария, что у вас уже есть проверка на стороне клиента. Если между этим клиентом и сервером базы данных есть сервер приложений (например, в веб-приложении), я бы предложил разместить эту дополнительную проверку (на сервере приложений) вместо базы данных.

...