使用OUTER APPLY函数时返回的值而不是NULL

时间:2017-12-15 14:41:18

标签: sql-server tsql sql-server-2014 apply 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)

enter image description here

所以在第一个SELECT语句中,我只是OUTER APPLied函数,它返回结果。

在下一个语句中,我从函数中获取了代码并将其直接写入OUTER APPLY语句。

最后一个语句只是直接函数调用。

我无法理解为什么FIRST查询会返回值...

3 个答案:

答案 0 :(得分:11)

这是一个非常有趣的查询。您的第一个查询的行为取决于您是否使用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的查询只返回一个常量。

答案 1 :(得分:6)

我想这是陈述here的旧bug。它是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

请阅读评论:

  

可能与此处stackoverflow.com/a/32414450/73226相同的基本问题 - 马丁史密斯

就是这样!同样的问题,我提供给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)

答案 2 :(得分:0)

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

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

  

如果SELECT语句没有返回任何行,则该变量将保留该行   目前价值。如果expression是一个返回no的标量子查询   value,该变量设置为NULL。

例如

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

在任何条件下,SELECT @variable的值都不会改变,DB ENGINE将语句简化为

  

| - 常规扫描

如果使用RECOMPILE sql构建具有条件和计划更改的新计划

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

但是RECOMPILE需要编译成本,然后使用CASE进行返回

PS 如果你不改变这个功能,那么你可以绕过作业,否则为什么要申请:)

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