Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 План запроса: Constant Scan перед Nested Loops  [new]
PM2010
Member

Откуда:
Сообщений: 70
Добрый день,

Изучаю операторы плана запроса, и есть следующий демонстрационный запрос.

+ Запрос
USE tempdb
GO

SET NOCOUNT ON
GO

CREATE TABLE [TblTest] (
	[id] INT IDENTITY(1, 1) NOT NULL PRIMARY KEY,
	[val] uniqueidentifier NOT NULL DEFAULT NEWID()
)
GO

INSERT [TblTest] DEFAULT VALUES
GO 100000

SET STATISTICS PROFILE ON 
GO

DECLARE @i INT = CAST((RAND() * 10000) AS INT)

IF EXISTS (SELECT [id] FROM [TblTest] WHERE [id] = @i)
	PRINT N'Hello'
GO

SET STATISTICS PROFILE OFF
GO

+ Результат
Rows ExecutesStmtText StmtId NodeId Parent PhysicalOp LogicalOp Argument DefinedValues EstimateRows EstimateIO EstimateCPU AvgRowSize TotalSubtreeCost OutputList Warnings Type Parallel EstimateExecutions
1 1 IF EXISTS (SELECT [id] FROM [TblTest] WHERE [id] = @i) 1 1 0 NULL NULL NULL NULL 1 NULL NULL NULL 0.003288537 NULL NULL GeneralQuery 0 NULL
0 0 |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1004] THEN (1) ELSE (0) END)) 1 2 1 Compute Scalar Compute Scalar DEFINE:([Expr1003]=CASE WHEN [Expr1004] THEN (1) ELSE (0) END) [Expr1003]=CASE WHEN [Expr1004] THEN (1) ELSE (0) END 1 0 1E-07 11 0.003288537 [Expr1003] NULL PLAN_ROW 0 1
1 1 |--Nested Loops(Left Semi Join DEFINE:([Expr1004] = [PROBE VALUE])) 1 3 2 Nested Loops Left Semi Join DEFINE:([Expr1004] = [PROBE VALUE]) [Expr1004] = [PROBE VALUE] 1 0 4.18E-06 9 0.003288437 [Expr1004] NULL PLAN_ROW 0 1
1 1 |--Constant Scan 1 4 3 Constant Scan Constant Scan NULL NULL 1 0 1.157E-06 9 1.157E-06 NULL NULL PLAN_ROW 0 1
1 1 |--Clustered Index Seek(OBJECT:([tempdb].[dbo].[TblTest].[PK__TblTest__3213E83F03317E3D]) SEEK:([tempdb].[dbo].[TblTest].[id]=[@i]) ORDERED FORWARD) 1 5 3 Clustered Index Seek Clustered Index Seek OBJECT:([tempdb].[dbo].[TblTest].[PK__TblTest__3213E83F03317E3D]) SEEK:([tempdb].[dbo].[TblTest].[id]=[@i]) ORDERED FORWARD NULL 1 0.003125 0.0001581 9 0.0032831 NULL NULL PLAN_ROW 0 1


Как я понимаю, Constant Scan создает пустую строку, Clustered Index Seek отбирает нужную нам строку, а затем происходит левое полусоединение, где слева - пустая строка от Constant Scan, а справа - строка от Clustered Index Seek.
Дальше я совсем ничего не понимаю.
  • По какому условию происходит соединение, если слева пустая строка?
  • Что является результатом такого соединения?
  • Кто такой [PROBE VALUE], что записывается в столбец [Expr1004], и как он получается?

    Версия SQL Server 2008 R2.
  • 19 фев 14, 10:38    [15587650]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Glory
    Member

    Откуда:
    Сообщений: 104751
    PM2010
    Constant Scan создает пустую строку,

    Почему пустую, если Rows=1 ?

    PM2010
    Дальше я совсем ничего не понимаю.

    Вы же смотрите план IF EXISTS, а не просто запроса
    19 фев 14, 10:59    [15587772]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Glory
    Почему пустую, если Rows=1 ?

    Одну пустую строку. Без столбцов, наверное.

    Glory
    Вы же смотрите план IF EXISTS, а не просто запроса

    Не понимаю, что тогда принципиально меняется, если рассматривать план в таком ключе.
    19 фев 14, 11:06    [15587837]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Glory
    Member

    Откуда:
    Сообщений: 104751
    PM2010
    Не понимаю, что тогда принципиально меняется, если рассматривать план в таком ключе.

    В том, что EXISTS всегда продуцирует результат.
    Т.е. результатом EXISTS является не результат запроса внутри.
    А результат, построенный на основе результата запроса внутри.
    19 фев 14, 11:10    [15587885]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Glory
    В том, что EXISTS всегда продуцирует результат.

    Пусть продуцирует, вопрос-то состоит в том, что именно происходит, чтобы этот результат получить: какие операции, что это операции делают, в каком порядке, куда и чего записывается.
    19 фев 14, 11:13    [15587912]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Glory
    Member

    Откуда:
    Сообщений: 104751
    PM2010
    вопрос-то состоит в том, что именно происходит

    Выполняется запрос. На основе результата этого запроса _вычисляется_ возвращаемый результат.
    Выполнение запроса вы видите в 4ой строке плана.
    А вычисление результата - в остальных.
    19 фев 14, 11:18    [15587956]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    К сожалению, ответ в стиле "Лампочка светит потому что электричество" не подходит к конкретным вопросам из первого поста.
    19 фев 14, 11:20    [15587977]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Glory
    Member

    Откуда:
    Сообщений: 104751
    PM2010
    К сожалению, ответ в стиле "Лампочка светит потому что электричество"

    А вы лампочку от выключателя отличаете ?
    19 фев 14, 11:30    [15588088]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Glory, могу попытаться.
    19 фев 14, 11:41    [15588200]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Glory
    Member

    Откуда:
    Сообщений: 104751
    PM2010
    могу попытаться.

    Ну так попытайтесь понять, что EXISTS ждет от запроса одну любую запись, чтобы сгенерировать выходной результат TRUE
    И если не дождется, то генерирует результат FALSE
    19 фев 14, 11:43    [15588214]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Glory,

    Так а как он получает эту одну любую запись? К чему там Constant Scan, Nested Loops и Probe Value? Почему не посмотреть напрямую, вернул ли нам Clustered Index Seek чего-нибудь или нет?
    19 фев 14, 11:46    [15588256]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    SomewhereSomehow
    Member

    Откуда: Moscow
    Сообщений: 2480
    Блог
    PM2010,

    Такая форма плана обусловлена особенностями языкового элемента IF, который вы видите в плане как самую левую иконку "COND WITH QUERY", его дочерний оператор (в данном случае Compute Scalar) должен обязательно вернуть одну строку со значением, либо 1, либо 0. Чтобы этот языковой элемент мог выполнить одну или другую ветку кода.

    Если встанете на Compute Scalar, вы там увидите: [Expr1004] = Scalar Operator(CASE WHEN [Expr1005] THEN (1) ELSE (0) END).

    Далее в игру вступает EXISTS, эта конструкция проверяет наличие и обычно (хотя и не всегда) реализуется через полу-соединение (Semi Join). Почему просто не выполнить поиск по индексу, без соединения с непонятно чем? Ответ дан выше, одна строка обязательно должна вернуться верхнему оператору, иначе ему нечего будет проверять.

    Посмотрите план запроса (план по форме, такой же, как и ваш, только отличается корневым языковым элементом):
    with const(a) as (select 1)
    select case when EXISTS (SELECT [id] FROM [TblTest] WHERE [id] = @i) then 1 else 0 end
    from const;
    


    Мы же не имеем права ничего не возвращать, если у нас нет совпадения в CASE? Поэтому, сервер сначала сканирует константу, в данном случае одну строку, как и в вашем случае – одна строка, после этого, выполняет левое полу соединение (Left Semi Join). Но это пока еще не гарантирует, что одна строка обязательно вернется. Т.к. обычно, когда мы пишем exists, то мы хотим оставить только те строки, которые существуют, исключив лишние.

    В данном случае, если ничего найдено не будет, лишнюю строку исключать не надо (помним, что она должна вернуться в любом случае)! Поэтому используется особый вид Semi Join-а с Probe column.

    Что такое Probe column? Сравните планы двух запросов, и их операторы Nested Loops:

    with const(a) as (select 1)
    select case when EXISTS (SELECT [id] FROM [TblTest] WHERE [id] = @i) then 1 else 0 end
    from const;
    
    with const(a) as (select 1)
    select * from const
    where EXISTS (SELECT [id] FROM [TblTest] WHERE [id] = @i);
    


    Картинка с другого сайта.

    Видно, хотя по смыслу запросы разные, в одном случае мы всегда должны возвращать строки внешней таблицы, в другом фильтровать, планы имеют схожую форму. Отлиичия как раз в столбце Probe Column. Это особый вид полу-соединения, который всегда возвращает строки внешней таблицы, но при этом в колонке Probe указывает, было ли для этой строки совпадение со строкой внутренней таблицы.

    Вот в принципе и все.
    19 фев 14, 11:49    [15588293]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    SomewhereSomehow,

    Большое спасибо за такое обстоятельное объяснение. Для закрепления и чтобы удостовериться, что я понял всё правильно, повторю рассуждения в обратном порядке.

    1. Constant Scan сканирует константу (под константой здесь понимается как раз переменная @i).
    2. Clustered Index Seek выбирает запрошенные строки.
    3. Left Semi Join соединяет результаты шагов 1 и 2. Left Semi Join возвращает каждую строку из верхнего входа, для которой есть соответствующая строка в нижнем входе. Предиката соединения нет, поэтому каждая строка будет считаться подходящей. Слева у нас одна строка из шага 1. Она и будет результатом соединения, если в таблице строки с нужным [id] существуют. Если не существуют, то не вернется ничего. Но не вернуть ничего мы не имеем права, поэтому у нас создается PROBE COLUMN, который является флажком, было ли что-нибудь соединено или нет.
    4. Compute Scalar проверяет флажок из PROBE COLUMN. В итоге мы узнаем, EXISTS ли у нас что-либо.

    Надеюсь, мои рассуждения верны?
    22 фев 14, 00:37    [15609140]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    SomewhereSomehow
    Member

    Откуда: Moscow
    Сообщений: 2480
    Блог
    PM2010,

    Проб колонка создтся всегда (когда применимо правило), она же и проверяется дальше.
    В целом, да, вы все верно поняли!
    22 фев 14, 00:41    [15609154]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Сам себя поправлю: на шаге 1 вполне может создаться пустая строка (без столбцов), так как предиката соединения нет и любая строка подойдёт, необязательно с @i. А выяснить, случилось ли соединение или нет, поможет PROBE COLUMN.
    22 фев 14, 00:43    [15609161]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    SomewhereSomehow
    Member

    Откуда: Moscow
    Сообщений: 2480
    Блог
    PM2010,

    Без столбцов или нет - все это неважно. Главное есть строка. Вы все правильно поняли.
    22 фев 14, 00:46    [15609172]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Follow up в тему.
    + Запрос
    USE tempdb
    go
    
    CREATE TABLE Student(ID Integer , Gender CHAR(1), ID_Gender INT)   
    GO
    ALTER TABLE Student ADD CONSTRAINT CK_Student_Gender CHECK(Gender IN('M','F'))   
    GO   
    CREATE TABLE Gender(ID Integer PRIMARY KEY, Gender CHAR(1))   
    GO   
    INSERT INTO Gender(ID, Gender) VALUES(1, 'F')  
    INSERT INTO Gender(ID, Gender) VALUES(2, 'M')  
    INSERT INTO Gender(ID, Gender) VALUES(3, 'N')  
    GO
    ALTER TABLE Student ADD CONSTRAINT fk_Student FOREIGN KEY (ID_Gender) REFERENCES Gender(ID)  
    GO
    
    SET STATISTICS PROFILE ON
    GO
    INSERT INTO Student(ID, ID_Gender, Gender) 
    VALUES(5, (SELECT ID_Gender FROM Student), 'F')
    GO
    SET STATISTICS PROFILE OFF
    
    + План

    RowsExecutesStmtTextStmtIdNodeIdParentPhysicalOpLogicalOpArgumentDefinedValuesEstimateRowsEstimateIOEstimateCPUAvgRowSizeTotalSubtreeCostOutputListWarningsTypeParallelEstimateExecutions
    11INSERT INTO Student(ID. ID_Gender. Gender) VALUES(5. (SELECT ID_Gender FROM Student). 'F')110NULLNULLNULLNULL1NULLNULLNULL0.01657986NULLNULLINSERT0NULL
    11 |--Assert(WHERE:(CASE WHEN NOT [Pass1018] AND [Expr1017] IS NULL THEN (0) ELSE NULL END))131AssertAssertWHERE:(CASE WHEN NOT [Pass1018] AND [Expr1017] IS NULL THEN (0) ELSE NULL END)NULL107.8E-0790.01657986NULLNULLPLAN_ROW01
    11 |--Nested Loops(Left Semi Join. PASSTHRU:([Student].[ID_Gender] IS NULL). OUTER REFERENCES:([Student].[ID_Gender]). DEFINE:([Expr1017] = [PROBE VALUE]))143Nested LoopsLeft Semi JoinPASSTHRU:([Student].[ID_Gender] IS NULL). OUTER REFERENCES:([Student].[ID_Gender]). DEFINE:([Expr1017] = [PROBE VALUE])[Expr1017] = [PROBE VALUE]. [Pass1018] = [PASSTHRU VALUE]104.18E-0690.01657908[Expr1017]. [Pass1018]NULLPLAN_ROW01
    11 |--Assert(WHERE:([Expr1019]))154AssertAssertWHERE:([Expr1019])NULL102.8E-07110.0132916[Student].[ID_Gender]NULLPLAN_ROW01
    00 | |--Compute Scalar(DEFINE:([Expr1019]=CASE WHEN [Student].[Gender]<>'F' AND [Student].[Gender]<>'M' THEN (0) ELSE NULL END))165Compute ScalarCompute ScalarDEFINE:([Expr1019]=CASE WHEN [Student].[Gender]<>'F' AND [Student].[Gender]<>'M' THEN (0) ELSE NULL END)[Expr1019]=CASE WHEN [Student].[Gender]<>'F' AND [Student].[Gender]<>'M' THEN (0) ELSE NULL END101E-07150.01329132[Student].[ID_Gender]. [Expr1019]NULLPLAN_ROW01
    11 | |--Table Insert(OBJECT:([Student]). SET:([Student].[ID] = [Expr1004].[Student].[ID_Gender] = [Expr1010].[Student].[Gender] = [Expr1011]))176Table InsertInsertOBJECT:([Student]). SET:([Student].[ID] = [Expr1004].[Student].[ID_Gender] = [Expr1010].[Student].[Gender] = [Expr1011])NULL10.011E-06120.01329122[Student].[Gender]. [Student].[ID_Gender]NULLPLAN_ROW01
    11 | |--Top(TOP EXPRESSION:((1)))187TopTopTOP EXPRESSION:((1))NULL101E-07160.003290217[Expr1004]. [Expr1010]. [Expr1011]NULLPLAN_ROW01
    00 | |--Compute Scalar(DEFINE:([Expr1004]=(5). [Expr1010]=[Expr1016]. [Expr1011]='F'))198Compute ScalarCompute ScalarDEFINE:([Expr1004]=(5). [Expr1010]=[Expr1016]. [Expr1011]='F')[Expr1004]=(5). [Expr1010]=[Expr1016]. [Expr1011]='F'101E-07160.003290117[Expr1004]. [Expr1010]. [Expr1011]NULLPLAN_ROW01
    11 | |--Nested Loops(Left Outer Join)1109Nested LoopsLeft Outer JoinNULLNULL104.18E-06110.003290017[Expr1016]NULLPLAN_ROW01
    11 | |--Constant Scan11110Constant ScanConstant ScanNULLNULL101.157E-0691.157E-06NULLNULLPLAN_ROW01
    11 | |--Assert(WHERE:(CASE WHEN [Expr1015]>(1) THEN (0) ELSE NULL END))11210AssertAssertWHERE:(CASE WHEN [Expr1015]>(1) THEN (0) ELSE NULL END)NULL104.8E-07110.00328468[Expr1016]NULLPLAN_ROW01
    11 | |--Stream Aggregate(DEFINE:([Expr1015]=Count(*). [Expr1016]=ANY([Student].[ID_Gender])))11312Stream AggregateAggregateNULL[Expr1015]=Count(*). [Expr1016]=ANY([Student].[ID_Gender])101.1E-06190.0032842[Expr1015]. [Expr1016]NULLPLAN_ROW01
    01 | |--Table Scan(OBJECT:([Student]))11413Table ScanTable ScanOBJECT:([Student])[Student].[ID_Gender]10.0031250.0001581110.0032831[Student].[ID_Gender]NULLPLAN_ROW01
    00 |--Clustered Index Seek(OBJECT:([Gender].[PK__Gender__3214EC270519C6AF]). SEEK:([Gender].[ID]=[Student].[ID_Gender]) ORDERED FORWARD)1314Clustered Index SeekClustered Index SeekOBJECT:([Gender].[PK__Gender__3214EC270519C6AF]). SEEK:([Gender].[ID]=[Student].[ID_Gender]) ORDERED FORWARD. FORCEDINDEXNULL10.0031250.000158190.0032831NULLNULLPLAN_ROW01



    1. Table Scan возвращает нам все строки таблицы Student.
    2. Stream Aggregate формирует одну строку со столбцами [Expr1015]=(кол-во строк) и [Expr1016]=(какое-нибудь значение ID_Gender).
    3. Assert проверяет, что Table Scan вернул 0 или 1 строку, так как в INSERT может вернуться только 1 значение или NULL.
    4. Constant Scan создает пустую строку.
    5. Происходит Left Outer Join пустой строки и результата Stream Aggregate, соединение без предиката, то есть строка с результатом Stream Aggregate будет передана дальше без изменений.
    6. Compute Scalar формирует строку для передачи в INSERT.
    7. Сформированная в пункте 6 строка передается через TOP(1) без изменений.
    8. Происходит вставка строки в таблицу Student.
    9. Compute Scalar и Assert проверяют ограничение CHECK.
    10. Clustered Index Seek просматривает таблицу Gender в поисках строки, на которую мы ссылаемся в свежевставленной строке.
    11. Left Semi Join и последующий Assert уже знакомым нам способом проверяют ссылочную целостность.
    12. Если все проверки прошли, то успех.

    Что тут непонятно:
    1) зачем лишние телодвижения в виде Left Outer Join с пустой строкой в качестве внешней таблицы;
    2) почему после Left Outer Join столбец [Expr1015] пропал без вести;
    3) опять же, зачем лишний TOP(1).
    22 фев 14, 17:08    [15611192]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Mnior
    Member

    Откуда: Кишинёв
    Сообщений: 6724
    PM2010
    3) опять же, зачем лишний TOP(1).
    Кореллированный подзапрос логически не имеет права возвращать более одной сроки. Т.к. горе программист написал откровенно бесмысленный запрос, скулю ничего не остаётся как перестраховываться и городить кучу всяких проверок.
    SELECT (SELECT 1 AS ColName UNION ALL SELECT 2)
    
    Msg 512, Level 16, State 1, Line 1
    Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
    PM2010
    2) почему после Left Outer Join столбец [Expr1015] пропал без вести;
    Никуда он не пропал. После агрегации значение [Expr1015] проверяется в Assert под описанием "Used to verify that a specified condition exists" которое прерывает запрос в случае не выполнения правил.
    PM2010
    1) зачем лишние телодвижения в виде Left Outer Join с пустой строкой в качестве внешней таблицы;
    Сначало выполняется "Constant Scan", который возвращает/определяет значения { ID = 5, Gender = 'F' }. Затем через оператор Left Outer Join считывается Table Scan. Т.к. строка может и не вернуться то LEFT, а Loop т.к. строк мало (не более одной).

    Оператор Top возможно бессмысленен, возможно именно перестраховка для обхода бага, возможно недоделали оптимизатор для идиотических запросов. На самом деле багов много, просто часто они не мешают так жить чтобы резко их исправлять.
    Скорее он стоит в неправильном месте. Я бы его положил до агрегации. Если таблица бы имело *лярд записей - то это было очень не оптимально и чревато. Я бы возвращал не более 2х строк для агрегации, чтобы понять что уже хамба.

    Если кто-то опишет сей баг на буржуйском - выставлю на connect.microsoft.com/SQLServer/
    Проголосуем, но они не станут это исправлять - и правильно, нефег хренью заниматься.

    PS: Всегда не понимал зачем смотреть текстовый план запроса, когда графический выдаёт море полезной информации и выглядит более читабельным.
    23 фев 14, 01:07    [15613168]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    PM2010
    Member

    Откуда:
    Сообщений: 70
    Понятно, что запрос глупый, ибо писался запрос ради запроса из желания посмотреть что будет в плане. :-)

    Mnior
    Никуда он не пропал. После агрегации значение [Expr1015] проверяется в Assert под описанием "Used to verify that a specified condition exists" которое прерывает запрос в случае не выполнения правил.
    План гласит обратное. :-( [Expr1015] и [Expr1016] рождаются в Stream Aggregate, потом Assert проверяет [Expr1015] и, если всё нормально, согласно документации должен передать строчку дальше, в Nested Loops. Но после Nested Loops, как гласит столбец OutputList текстового плана, [Expr1015] больше нигде не числится и был как будто отброшен. При этом, никаких явных пометок, что [Expr1015] следует отбросить, вроде не содержится. Выходит, SQL Server может неявно отбрасывать столбцы, если считает, что они ему больше не понадобятся, чтобы не таскать за собой через весь план?

    Mnior
    Сначало выполняется "Constant Scan", который возвращает/определяет значения { ID = 5, Gender = 'F' }.
    Опять же, в OutputList у оператора Constant Scan никаких столбцов нет, то есть строка пустая. А значения { ID = 5, Gender = 'F' }, согласно плану же, определяются в операторе Compute Scalar, причем в виде литерала, а не опосредованно, через другие столбцы. Наверное, это очередной не мешающий жить баг? К слову, наблюдал похожие планы запросов, в которых Constant Scan возвращает вполне осмысленные столбцы и передает их в Left Outer Join. Возможно, просто работает какая-то обкатанная схема и я столкнулся с ее частным случаем в виде пустой строки от Constant Scan?

    Mnior
    Всегда не понимал зачем смотреть текстовый план запроса, когда графический выдаёт море полезной информации и выглядит более читабельным.
    Да, графический план удобен, но он и интерактивен, то есть содержательные вещи выдает во всплывающих подсказках, чего добиться при размещении на форуме просто скриншота нельзя. Разве что скриншотить каждый оператор с подсказкой отдельно... Проще поместить текстовый план, чтобы сразу было всё видно.
    24 фев 14, 00:30    [15616733]     Ответить | Цитировать Сообщить модератору
     Re: План запроса: Constant Scan перед Nested Loops  [new]
    Mnior
    Member

    Откуда: Кишинёв
    Сообщений: 6724
    PM2010
    [Expr1015] и [Expr1016] рождаются в Stream Aggregate, потом Assert проверяет [Expr1015] и, если всё нормально, согласно документации должен передать строчку дальше, в Nested Loops. Но после Nested Loops, как гласит столбец OutputList текстового плана, [Expr1015] больше нигде не числится и был как будто отброшен.
    Оператор Assert инструкции Showplan
    Оператор Assert инструкции Showplan
    Оператор Assert проверяет условие. Например, этот оператор проверяет целостность ссылок или гарантирует, что скалярный вложенный запрос возвращает одну строку. Для каждой входной строки оператор Assert вычисляет выражение в столбце Аргумент плана выполнения. Если значением этого выражения является NULL, строка передается через оператор Assert и выполнение запроса продолжается. При значении этого выражения, отличном от NULL, будет выдана соответствующая ошибка.
    Смотрим внимательно на аргумент этого оператора и видим что там этот пресловутый [Expr1015] (количество строк) и используется. Ибо только ради этой проверки он и высчитывается. Никуда дальше это значение передавать не нужно.
    PM2010
    При этом, никаких явных пометок, что [Expr1015] следует отбросить, вроде не содержится.
    Так и должно быть написано "[Expr1015] отбрасываем"?! Откуда вы это взяли? Зачем это писать?
    Это дублирование никому не нужно. Вы же не пишете в коде когда переменную надо "отбрасывать".
    PM2010
    Выходит, SQL Server может неявно отбрасывать столбцы, если считает, что они ему больше не понадобятся, чтобы не таскать за собой через весь план?
    1. Естественно! Зачем ему таскать?
    2. Вы не поняли - это не столбцы - это внутренние логические переменные.
    Вообще, это довольно интересный вопрос, как связаны результирующие столбцы и эти "переменные". Сказать по графическому плану порой не получится (возможно можно выковырять из его XML-я).

    Не все команды вообще имеют столбцы (и даже строк). Для SELECT нужно смотреть на последний оператор, он содержит все нужные данные но ни 1к1. К примеру если столбцы логически одинаковые по значению, то в Output List будет меньше значений. Есть много системных или вспомогательных переменных - главное решить поставленную задачу, и показать все логические значения.
    PM2010
    Опять же, в OutputList у оператора Constant Scan никаких столбцов нет ...
    Оператор Constant Scan инструкции Showplan
    Оператор Constant Scan инструкции Showplan
    Оператор Constant Scan вводит в запрос одну или несколько константных строк. Оператор Compute Scalar часто используется после выполнения оператора Constant Scan. Он добавляет столбцы в строки, полученные в результате выполнения оператора Constant Scan.
    Никакого бага нет. Да и банально - зачем высчитывать значения заранее, если далее вообще всё может вывалиться в ошибку?! Представьте что у вас один из "столбцов" занимает 2 гига.
    На самом деле нюансов прост океан, и не сразу понимаешь из-за каких явлений было так всё организованно.

    Давайте мы не будем сюда выкладывать весь BOL, а вы сами всё прочитаете. Ок?
    PM2010
    Да, графический план удобен, но он и интерактивен ... Проще поместить текстовый план, чтобы сразу было всё видно.
    Cюда можно выложить и скриншот, который передаст 90% удобоваримой информации (структура плана) и сам XML файл графического плана (правой кнопкой на плане - Сохранить/показать XML плана), для деталей.

    Лучше спросите у SomewhereSomehow, как он делает такие хорошие иллюстрации одной картинкой. (есть спец прога)
    24 фев 14, 04:55    [15616964]     Ответить | Цитировать Сообщить модератору
    Все форумы / Microsoft SQL Server Ответить