SQL Server 交叉应用性能问题

Posted

技术标签:

【中文标题】SQL Server 交叉应用性能问题【英文标题】:SQL Server cross apply performance issue 【发布时间】:2015-08-27 11:22:49 【问题描述】:

我的看法如下:

CREATE VIEW V1 
AS
   SELECT 
       T1.Col1, F1.Col1, T1.Col2, T2.Col2... 
   FROM 
       T1 
   INNER JOIN 
       T2 ON T1.Col2 = T2.Col1 
   CROSS APPLY 
       UDF(T1.Col1, T2.Col2) F1

内连接返回百万条记录。在这里,在这种情况下,我知道 T1.Col1 的确切值,它将从应用程序传递。

有没有办法重写上面的视图,使交叉应用可以只应用于过滤的记录(基于T1.Col1值),而不是处理所有记录,然后再过滤?当前视图即使在 20 分钟后也不会返回任何值,而当我将值直接传递给 UDF 时,它会在不到一秒的时间内返回输出。

更新: 以下是 UDF 的结构,我根据以下 cmets 更新了问题:

CREATE FUNCTION [dbo].[UDF](@Col1 INT, @Col2 INT) RETURNS @TBL TABLE(Col1 int, Col2 int, Col3 VARCHAR(10), Col4 int) AS DECLARE CURSOR1 CURSOR FOR SELECT DISTINCT Col1 FROM TBL1 WHERE Col2 = @Col1 AND Col3 = @Col2 OPEN CURSOR1 FETCH NEXT FROM CURSOR1 INTO @Col1 WHILE (@@FETCH_STATUS = 0) BEGIN DECLARE CURSOR2 CURSOR FOR... INSERT INTO @TBLVAR SELECT * FROM SRCTBL1 WHERE CLOSE CURSOR2 DEALLOCATE CURSOR2 DECLARE CURSOR3 CURSOR FOR... INSERT INTO @TBLVAR SELECT * FROM SRCTBL2 WHERE CLOSE CURSOR3 DEALLOCATE CURSOR3 DECLARE CURSOR4 CURSOR FOR... INSERT INTO @TBLVAR SELECT * FROM SRCTBL3 WHERE CLOSE CURSOR4 DEALLOCATE CURSOR4 CLOSE CURSOR1 DEALLOCATE CURSOR2 SELECT Col1, Col2, Col3, Col4

【问题讨论】:

你可以将UDF代码合并到查询中sqlblog.com/blogs/hugo_kornelis/archive/2012/05/20/… 如果您可以将视图转换为内联 TVF,您将能够在 T1.Col1 上传递参数和过滤器。但是,我担心UDF本身正在扼杀性能,有没有办法内联它? 恐怕UDF不能内联。它包括 4 个游标,其中一个父游标在其中处理 3 个以上。每个游标都有一个复杂的逻辑。我想,我们需要将完整的 UDF 重写为单独的 UDF。但是,同样在这种情况下,我不确定性能改进。 你真的有 4 个光标在这个东西上?从您发布的骨架看来,您正在使用游标进行插入。更糟糕的是,听起来你有嵌套的游标。只有一种方法可以解决此问题。删除这些光标并使您的函数集基于。我愿意打赌所有这些游标都可以用基于集合的逻辑替换,性能问题也会消失。 @SeanLange Insert 在所有游标中都很常见,其中两个也具有基于条件的更新。它有 7 年历史的遗留脚本根据需要进行了更新,现在看起来像第一次面对我的怪物 :)。我只是想过滤掉连接返回的记录,如果可能的话,这样就至少没有了。对十字架的呼吁适用。我想现在我必须重写整个事情。告诉我一件事,如果我可以使每个光标内联并从父级调用它们,性能会受到严重影响吗? 【参考方案1】:

创建内联表值函数而不是视图:

create function fnx (@col1 int)
returns table 
as
return (
   select 
       t1.col1, f1.col1, t1.col2, t2.col2... 
   from 
       t1 
   inner join 
       t2 on t1.col2 = t2.col1 
   cross apply 
       udf(t1.col1, t2.col2) f1
    where t1.col1 = @col1
)

这样你就可以将参数传递给 t1.col1 上的过滤器了。

【讨论】:

这在这里没有帮助,因为问题是糟糕的 udf。 @sean 部分同意,我已经在 cmets 中声明自己 UDF 正在扼杀性能。但是,如果 UDF 被调用一百万次或一千次,情况就不一样了。所以,它会在一定程度上有所帮助。【参考方案2】:

使用公用表表达式:

 CREATE VIEW V1 
 AS
   WITH cte(cols...) AS
   (
       SELECT 
          T1.Col1, F1.Col1, T1.Col2, T2.Col2... 
       FROM  T1 
       INNER JOIN T2 
          ON T1.Col2 = T2.Col1 
       WHERE T1.col1 = ?
  ) 
  SELECT *
  FROM cte c
  CROSS APPLY UDF(c.Col1, c.Col2) F1

【讨论】:

CTE 只是编写查询的一种更简洁的方式,不会影响执行计划。即不保证 UDF 仅在过滤的结果集上调用。使用临时表或表变量强制结果集。 你如何将参数传递到视图中? 如果这必须是参数化视图,当然它不会起作用,作者应该使用另一个构造。如果作者事先知道 col1 的值,他仍然可以使用它。【参考方案3】:

使用内存系统。如果您使用 java 或 .net 编写此代码。将产生更快的结果。

创建一个微服务来缓存结果并提供服务。

参考

微服务,***,

https://en.m.wikipedia.org/wiki/Microservices

【讨论】:

我们只管理数据库,应用程序归第三方供应商所有。;暂时,我们不能这样做。

以上是关于SQL Server 交叉应用性能问题的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL 中将 XML 文档转换为表格数据集的有效方法,因为随着 xml 的增长,交叉应用 xml 查询的性能呈指数级下降

MS SQL 交叉连接性能评估

SQL Server 查询性能不佳

理解性能的奥秘——应用程序中慢,SSMS中快——SQL Server如何编译存储过程

理解性能的奥秘——应用程序中慢,SSMS中快——SQL Server如何编译动态SQL

SQL Server 数据库性能 [重复]