Значение возвращается вместо NULL при использовании функции с OUTER APPLY

Я получаю странные результаты при использовании встроенной функции. Вот код:

IF EXISTS (
SELECT * FROM sys.objects AS o WHERE name = 'vendor_relation_users'
) DROP FUNCTION dbo.vendor_relation_users;
GO
CREATE FUNCTION [dbo].[vendor_relation_users]
(
    @user_name CHAR(12)
)
RETURNS TABLE
AS
    RETURN (SELECT @user_name AS user_name WHERE @user_name NOT LIKE '06%');
GO

DECLARE @u CHAR(12) = '066BDLER'
SELECT a.user_name, is_v.user_name 
FROM (SELECT @u AS user_name) a
OUTER APPLY [dbo].[vendor_relation_users](@u) AS is_v

SELECT a.user_name, is_v.user_name 
FROM (SELECT @u AS user_name) a
OUTER APPLY (SELECT @u AS user_name WHERE @u NOT LIKE '06%') AS is_v


SELECT * FROM [dbo].[vendor_relation_users](@u)

введите описание изображения здесь

Итак, в первом операторе SELECT я просто OUTER APPLied, и он возвращает результат.

В следующем утверждении я взял код из функции и поместил его прямо в инструкцию OUTER APPLY.

И последнее утверждение - это просто вызов прямой функции.

Я не могу понять, почему запрос FIRST возвращает значение...

Ответы

Ответ 1

Это очень интересный запрос. Поведение вашего первого запроса зависит от того, используете ли вы OPTION (RECOMPILE) или нет.

Как вы указываете, это:

DECLARE @u CHAR(12) = '066BDLER'
SELECT a.user_name, is_v.user_name 
FROM (SELECT @u AS user_name) a
OUTER APPLY [dbo].[vendor_relation_users](@u) AS is_v

возвращает это:

user_name       user_name
066BDLER        066BDLER

но если вы добавите OPTION (RECOMPILE) следующим образом:

SELECT a.user_name, is_v.user_name 
FROM (SELECT @u AS user_name) a
OUTER APPLY [dbo].[vendor_relation_users](@u) AS is_v
OPTION (RECOMPILE)   

вы правильно поняли:

user_name       user_name
066BDLER        NULL

Я подозреваю, что это связано с ошибкой в ​​том, как оптимизатор запросов замыкает эти встроенные функции из-за оценки мощности. Если вы посмотрите на план запроса для двух запросов, вы увидите, что тот, у которого нет OPTION RECOMPILE, просто возвращает константу.

Ответ 2

Я думаю, что это старый пример, описанный здесь. И это Closed as Won't Fix

Пожалуйста, используйте эту функцию, например:

SELECT a.user_name, is_v.user_name 
FROM (SELECT @u AS user_name) a
OUTER APPLY (
    SELECT *
    FROM [dbo].[vendor_relation_users](a.user_name) 
    ) AS is_v

ОБНОВЛЕНИЕ # 1

Просто прочитайте комментарии:

Возможно, будет такая же основная проблема, как здесь fooobar.com/questions/335856/... - Martin Smith

Вот и все! Такая же проблема, та же ссылка, которую я предоставил сайту MS Connect.

ОБНОВЛЕНИЕ # 2

Вместо

RETURN (SELECT @user_name AS user_name WHERE @user_name NOT LIKE '06%');

Вам нужно использовать:

RETURN (SELECT CASE WHEN @user_name LIKE '06%' THEN NULL ELSE @user_name END)

Ответ 3

CREATE FUNCTION [dbo].[vendor_relation_users]
(
    @user_name CHAR(12)
)
RETURNS TABLE
AS
  RETURN (SELECT CASE WHEN @user_name NOT LIKE '06%' THEN  @user_name  ELSE NULL END as [user_name]);  
GO

Ok, https://docs.microsoft.com/en-us/sql/t-sql/language-elements/select-local-variable-transact-sql

Если оператор SELECT не возвращает строки, переменная сохраняет Текущее значение. Если выражение является скалярным подзапросом, который не возвращает значение, значение переменной равно NULL.

Например

DECLARE @u CHAR(12) = '066BDLER'
SELECT  @u  = 'AAAA' WHERE 1 <> 1
SELECT @u

При любых условиях значение SELECT @variable не изменится, и DB ENGINE упростит утверждение

| --Сохранная проверка

Если использовать RECOMPILE sql build новый план с условием и изменением плана

        |--Compute Scalar(DEFINE:([Expr1001]='066BDLER    '))
             |--Filter(WHERE:(STARTUP EXPR(NOT '066BDLER    ' like '06%')))
                  |--Constant Scan

Но RECOMPILE требует затрат на компиляцию, затем используйте CASE для возврата

пс Если вы не измените функцию, вы можете обойти назначение, иначе почему бы вы никогда не применяли:)

DECLARE @u CHAR(12) = '066BDLER'
SELECT a.user_name, is_v.user_name 
FROM (SELECT @u AS user_name) a
OUTER APPLY [dbo].[vendor_relation_users](a.user_name) AS is_v