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

Dmi*_*sev 16 t-sql sql-server apply sql-server-2014 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)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

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

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

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

Ste*_*ord 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
Run Code Online (Sandbox Code Playgroud)

返回:

user_name       user_name
066BDLER        066BDLER
Run Code Online (Sandbox Code Playgroud)

但如果你这样添加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)   
Run Code Online (Sandbox Code Playgroud)

你正确得到这个:

user_name       user_name
066BDLER        NULL
Run Code Online (Sandbox Code Playgroud)

我怀疑这是由于基数估计导致查询优化器如何使这些内联函数短路的错误.如果查看两个查询的查询计划,您将看到没有OPTION RECOMPILE的查询只返回一个常量.


gof*_*fr1 6

我想这是这里描述的旧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
Run Code Online (Sandbox Code Playgroud)

更新#1

只需阅读评论:

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

这就对了!同样的问题,我提供给MS Connect网站的链接相同.

更新#2

代替:

RETURN (SELECT @user_name AS user_name WHERE @user_name NOT LIKE '06%');
Run Code Online (Sandbox Code Playgroud)

你需要使用:

RETURN (SELECT CASE WHEN @user_name LIKE '06%' THEN NULL ELSE @user_name END)
Run Code Online (Sandbox Code Playgroud)