Sql server 优化SQL函数

Sql server 优化SQL函数,sql-server,optimization,Sql Server,Optimization,我正在尝试优化或完全重写此查询。目前运行大约需要1500毫秒。我知道distinct的效率和工会一样低。但我正努力弄清楚到底该从这里走到哪里 我认为第一个select语句可能不需要返回的输出 [Key | User_ID,(User_ID)] 注;程序和程序场景都使用聚集索引。如果需要,我可以提供执行计划的屏幕截图 ALTER FUNCTION [dbo].[Fn_Get_Del_User_ID] (@_CompKey INT) RETURNS VARCHAR(8000) AS BEGI

我正在尝试优化或完全重写此查询。目前运行大约需要1500毫秒。我知道distinct的效率和工会一样低。但我正努力弄清楚到底该从这里走到哪里

我认为第一个select语句可能不需要返回的输出

[Key | User_ID,(User_ID)]
注;程序和程序场景都使用聚集索引。如果需要,我可以提供执行计划的屏幕截图

ALTER FUNCTION [dbo].[Fn_Get_Del_User_ID] (@_CompKey INT)

RETURNS VARCHAR(8000)

AS

BEGIN

    DECLARE @UseID AS VARCHAR(8000);

    SET @UseID = '';

    SELECT @UseID = @UseID + ', ' + x.User_ID
    FROM 
        (SELECT DISTINCT (UPPER(p.User_ID)) as User_ID FROM  [dbo].[Program] AS p WITH (NOLOCK) 
        WHERE p.CompKey = @_CompKey
        UNION
        SELECT DISTINCT (UPPER(ps.User_ID)) as User_ID FROM  [dbo].[Program] AS p WITH (NOLOCK) 
        LEFT OUTER JOIN [dbo].[Program_Scenario] AS ps WITH (NOLOCK) ON p.ProgKey = ps.ProgKey
        WHERE p.CompKey = @_CompKey
        AND ps.User_ID IS NOT NULL) x 

    RETURN Substring(@UserIDs, 3, 8000);
END

我在黑暗中拍摄。我猜您最后发布的代码仍然是一个标量函数。它也没有您原始查询的所有逻辑。同样,由于没有表定义或示例数据发布,因此这是一个瞎猜

这可能就是它作为内联表值函数的外观

ALTER FUNCTION [dbo].[Fn_Get_Del_User_ID] 
(
    @_CompKey INT
) RETURNS TABLE AS RETURN
    select MyResult = STUFF(
    (
        SELECT distinct UPPER(p.User_ID) as User_ID 
        FROM dbo.Program AS p 
        WHERE p.CompKey = @_CompKey
        group by p.User_ID

        UNION

        SELECT distinct UPPER(ps.User_ID) as User_ID 
        FROM dbo.Program AS p 
        LEFT OUTER JOIN dbo.Program_Scenario AS ps ON p.ProgKey = ps.ProgKey
        WHERE p.CompKey = @_CompKey
            AND ps.User_ID IS NOT NULL
        for xml path ('')
    ), 1, 1, '') 
    from dbo.Program

我在黑暗中拍摄。我猜您最后发布的代码仍然是一个标量函数。它也没有您原始查询的所有逻辑。同样,由于没有表定义或示例数据发布,因此这是一个瞎猜

这可能就是它作为内联表值函数的外观

ALTER FUNCTION [dbo].[Fn_Get_Del_User_ID] 
(
    @_CompKey INT
) RETURNS TABLE AS RETURN
    select MyResult = STUFF(
    (
        SELECT distinct UPPER(p.User_ID) as User_ID 
        FROM dbo.Program AS p 
        WHERE p.CompKey = @_CompKey
        group by p.User_ID

        UNION

        SELECT distinct UPPER(ps.User_ID) as User_ID 
        FROM dbo.Program AS p 
        LEFT OUTER JOIN dbo.Program_Scenario AS ps ON p.ProgKey = ps.ProgKey
        WHERE p.CompKey = @_CompKey
            AND ps.User_ID IS NOT NULL
        for xml path ('')
    ), 1, 1, '') 
    from dbo.Program

在这个查询中发生了两件事 1.在[Program]表中查找与指定的CompKey(@_CompKey)匹配的行 2.在[Program_Scenario]表中查找与上述(1)中的行具有相同ProgKey的行

最后,来自这两组行的非空用户标识被连接到一个标量中

SELECT UPPER(p.User_ID) as User_ID 
FROM 
   [dbo].[Program] AS p WITH (NOLOCK) 
WHERE
   p.CompKey = @_CompKey
UNION
SELECT UPPER(ps.User_ID) as User_ID 
FROM  
   [dbo].[Program] AS p WITH (NOLOCK) 
   INNER JOIN [dbo].[Program_Scenario] AS ps WITH (NOLOCK) ON p.ProgKey = ps.ProgKey
WHERE 
    p.CompKey = @_CompKey
    AND ps.User_ID IS NOT NULL
为了使步骤1更有效,您需要在CompKey列(集群或非集群)上建立索引 为了使步骤2更有效,您需要在连接键上创建一个索引,该索引是Program_Scenario表上的ProgKey(这可能是一个非聚集索引,因为我无法想象ProgKey是PK)。很可能,SQL会采用循环连接策略——即,对于[Program]中找到的与CompKey条件匹配的每一行,它需要使用相同的ProgKey查找[Program_Scenario]中的对应行。不过这只是一个猜测,因为没有足够的关于数据基数和分布的信息

确保存在上述两个索引。 另外,正如其他人所注意到的,第二个左外部联接有点混乱,因为内部联接是处理它的正确方法

根据我的解释,查询的内部部分可以这样重写。此外,这也是在定位字符串连接部分之前最好运行和优化的查询。DISTINCT将被删除,因为它与UNION一起是自动的。尝试这个版本的查询以及上面的索引,如果它提供了必要的提升,那么就包括字符串连接或xml填充方法来返回标量

SELECT UPPER(p.User_ID) as User_ID 
FROM 
   [dbo].[Program] AS p WITH (NOLOCK) 
WHERE
   p.CompKey = @_CompKey
UNION
SELECT UPPER(ps.User_ID) as User_ID 
FROM  
   [dbo].[Program] AS p WITH (NOLOCK) 
   INNER JOIN [dbo].[Program_Scenario] AS ps WITH (NOLOCK) ON p.ProgKey = ps.ProgKey
WHERE 
    p.CompKey = @_CompKey
    AND ps.User_ID IS NOT NULL

在这个查询中发生了两件事 1.在[Program]表中查找与指定的CompKey(@_CompKey)匹配的行 2.在[Program_Scenario]表中查找与上述(1)中的行具有相同ProgKey的行

最后,来自这两组行的非空用户标识被连接到一个标量中

SELECT UPPER(p.User_ID) as User_ID 
FROM 
   [dbo].[Program] AS p WITH (NOLOCK) 
WHERE
   p.CompKey = @_CompKey
UNION
SELECT UPPER(ps.User_ID) as User_ID 
FROM  
   [dbo].[Program] AS p WITH (NOLOCK) 
   INNER JOIN [dbo].[Program_Scenario] AS ps WITH (NOLOCK) ON p.ProgKey = ps.ProgKey
WHERE 
    p.CompKey = @_CompKey
    AND ps.User_ID IS NOT NULL
为了使步骤1更有效,您需要在CompKey列(集群或非集群)上建立索引 为了使步骤2更有效,您需要在连接键上创建一个索引,该索引是Program_Scenario表上的ProgKey(这可能是一个非聚集索引,因为我无法想象ProgKey是PK)。很可能,SQL会采用循环连接策略——即,对于[Program]中找到的与CompKey条件匹配的每一行,它需要使用相同的ProgKey查找[Program_Scenario]中的对应行。不过这只是一个猜测,因为没有足够的关于数据基数和分布的信息

确保存在上述两个索引。 另外,正如其他人所注意到的,第二个左外部联接有点混乱,因为内部联接是处理它的正确方法

根据我的解释,查询的内部部分可以这样重写。此外,这也是在定位字符串连接部分之前最好运行和优化的查询。DISTINCT将被删除,因为它与UNION一起是自动的。尝试这个版本的查询以及上面的索引,如果它提供了必要的提升,那么就包括字符串连接或xml填充方法来返回标量

SELECT UPPER(p.User_ID) as User_ID 
FROM 
   [dbo].[Program] AS p WITH (NOLOCK) 
WHERE
   p.CompKey = @_CompKey
UNION
SELECT UPPER(ps.User_ID) as User_ID 
FROM  
   [dbo].[Program] AS p WITH (NOLOCK) 
   INNER JOIN [dbo].[Program_Scenario] AS ps WITH (NOLOCK) ON p.ProgKey = ps.ProgKey
WHERE 
    p.CompKey = @_CompKey
    AND ps.User_ID IS NOT NULL

如果使用
UNION
,则无需同时选择
DISTINCT
UNION
的重复数据消除工作在整个UNION集合上。您可能还需要一个
内部联接
,而不是
左联接
。对于
和ps.User\u ID不为NULL,它已经是一个内部联接。(这对性能没有帮助)谢谢FrankerZ,也谢谢你的意见,亲爱的。我现在要测试一下!因此,删除DISTINCT并将连接更改为内部连接实际上将运行时间增加了约100ms。不过,我完全理解你的想法。我对空值不感兴趣。然而,我发现如果我插入UNION ALL,它会减少运行时间,但会改变结果输出。只选择用户ID并返回一个表可能是一个更好的主意。如果需要,可以在其他地方进行连接。这到底是为了什么?通常不建议在SQL中处理逗号分隔的值,原子值更容易处理。有更好的方法生成逗号分隔的列表。您可以使用用于XML的东西。然后你可以把这个标量函数变成一个内联表值函数。并摆脱那些无锁的暗示。它们不是一个没有包袱的性能工具。它们可能导致重复和/或丢失行。如果您认为您的数据在大多数情况下都是正确的,那么它基本上是正确的。如果使用
UNION
,则无需同时选择
DISTINCT
UNION
的重复数据消除工作在整个UNION集合上。您可能还需要一个
内部联接
,而不是
左联接
。使用
和ps.Us