Рубрики
sql Без рубрики

ASSERT

Оператор ASSERT используется для проверки определённых условий. Это оператор будет выполняться для каждой строки из набора данных.

Типичный пример, когда можно встретить этот оператор — проверка условия CHECK. Например, для столба можно ограничить список возможных значений. Тогда при вставке данных ASSERT будет проверять для каждой строки значение, переданное в столбец. К сожалению в реальной работе эти возможности SQL используют реже, чем следует. Поэтому мы рассмотрим выдуманный и сильно упрощённый пример.

DROP TABLE IF EXISTS traffic_light;
CREATE TABLE traffic_light
(
    id INT IDENTITY(1, 1),
    color CHAR(1),
    CONSTRAINT CHECK_COLOR CHECK (color IN ( 'G', 'R' ))
);
INSERT INTO dbo.traffic_light (color) VALUES ('Y');

В этой таблице мы разрешаем цвету условного светофора быть или зелёным, или красным. При вставке данных в эту таблицу SQL Server генерирует следующий план:

Если разобраться с тем, что именно проверяет ASSERT, то мы увидим, что он возвращает 0, если в столбец color передаётся значение отличное от G или R и NULL в противном случае. Ошибка будет сгенерирована, есть ASSERT вернёт значение отличное от NULL.

Помимо проверки CHECK-ограничений ASSERT используется и при проверке ограничений внешних ключей. Попробуем чуть усложнить наш пример, добавив ещё одну таблицу с потенциально возможными цветами светофора и связав две таблицы между собой.

DROP TABLE IF EXISTS possible_light;
CREATE TABLE possible_light
(
    color CHAR(1) PRIMARY KEY
);
INSERT INTO dbo.possible_light (color) VALUES ('R'),('Y'),('G');
ALTER TABLE dbo.traffic_light
ADD CONSTRAINT fk_light
    FOREIGN KEY (color)
    REFERENCES dbo.possible_light (color);

Теперь при вставке данных в первую таблицу план усложнится:

Если идти справа налево, то первый ASSERT как и раньше проверит CHECK ограничение, а второй ASSERT проверит корректность связи по ключам.

В предикате второго оператора ASSERT мы видим непонятное условие Expr1007, но всё станет на свои места, если отобразить план запроса в виде текста:

  |--Assert(WHERE:(CASE WHEN NOT [Pass1008] AND [Expr1007] IS NULL THEN (0) ELSE NULL END))
       |--Nested Loops(Left Semi Join, PASSTHRU:([lessons].[dbo].[traffic_light].[color] IS NULL), OUTER REFERENCES:([lessons].[dbo].[traffic_light].[color]), DEFINE:([Expr1007] = [PROBE VALUE]))
            |--Assert(WHERE:(CASE WHEN [lessons].[dbo].[traffic_light].[color]<>'R' AND [lessons].[dbo].[traffic_light].[color]<>'G' THEN (0) ELSE NULL END))
            |    |--Table Insert(OBJECT:([lessons].[dbo].[traffic_light]), SET:([lessons].[dbo].[traffic_light].[color] = [Expr1004],[lessons].[dbo].[traffic_light].[id] = [Expr1003]))
            |         |--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(char(1),[@1],0)))
            |              |--Compute Scalar(DEFINE:([Expr1003]=getidentity((1525580473),(6),NULL)))
            |                   |--Constant Scan
            |--Clustered Index Seek(OBJECT:([lessons].[dbo].[possible_light].[PK__possible__900DC6E8209DDE1B]), SEEK:([lessons].[dbo].[possible_light].[color]=[lessons].[dbo].[traffic_light].[color]) ORDERED FORWARD)

По строке DEFINE:([Expr1007] = [PROBE VALUE]) становится понятно, что это выражение — просто результат объединения таблиц. Этим выражением ASSERT проверяет, что вставляемое значение действительно есть в таблице possible_light.

Наконец, рассмотрим ещё один случай, где оператор ASSERT может встречаться в реальной работе. Речь идёт о подзапросах. Во многих ситуациях встречается «скалярный» подзапрос. Т.е. такой подзапрос, который возвращает строго одно значение. В качестве тестового запроса рассмотрим следующий код:

SELECT * FROM dbo.traffic_light tl
WHERE (tl.color>
(SELECT pl.color FROM dbo.possible_light pl WHERE (1=1)and(pl.color>'A'))
)

Практического смысла он не несёт, но позволяет продемонстрировать ситуацию, когда мы сравниваем скалярное значение с результатом подзапроса. План получился не очень сложный:

Самый интересный блок тут это часть где данные из possible_light агрегируются, чтобы подсчитать количество строк, а потом полученное значение передаётся в ASSERT. Думаю, вы уже сами догадались, что Expr1004 это результат агрегации

Stream Aggregate(DEFINE:([Expr1004]=Count(*)...

Вот такой большой пост у меня получился о простейшем, но очень важном операторе.

Добавить комментарий