16

使用内联函数时,我得到了奇怪的结果。这是代码:

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 语句中。

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

我不明白为什么第一个查询返回值...

4

3 回答 3

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 的查询计划只返回一个常量。

于 2017-12-15T15:31:58.620 回答
6

我想这是这里描述的旧错误。它是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相同的基本问题– 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)
于 2017-12-21T06:22:16.020 回答
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 语句没有返回任何行,则变量保留其当前值。如果表达式是不返回值的标量子查询,则变量设置为 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
于 2017-12-20T09:00:04.573 回答