使用带有标量函数的 where 条件与使用交叉应用和表值函数的 where 条件

Posted

技术标签:

【中文标题】使用带有标量函数的 where 条件与使用交叉应用和表值函数的 where 条件【英文标题】:Using where condition with scalar functions vs where condition using cross apply & table valued function 【发布时间】:2016-05-06 00:16:02 【问题描述】:

我正在尝试为我正在创建的存储过程确定最佳方法(最快!)。这是一个相当基本的存储过程,将用于比较两个系统之间的字段以确保数据完整性。起初我想使用临时表和添加索引,但我没有看到速度上有任何差异。然后在阅读了通过表值函数获得的性能提升后,我认为这将是一种可行的方法,但一些简单的测试并没有显示出任何改进。我什至担心优化查询的原因是,由于系统之间的字段长度不匹配(另一个问题),where 条件将使用许多标量函数,所以我假设在 where 子句中执行许多标量函数对性能。

这是方法#1:

SELECT
SLXID
FROM dbo.Salesforce_Contacts a WITH(NOLOCK)
JOIN _SLX_Contact b WITH(NOLOCK)
    ON a.SLXID = b.Contactid
WHERE
a.IsDeleted = 0
AND
(
LEFT(a.FirstName, 32) != b.FirstName
AND LEFT(a.LastName, 32) != b.LastName
AND LEFT(a.Title, 64) != b.Title
AND dbo.GetSLXContactStatus(a.Contact_Status__c) != b.Status
)

这是使用表值函数的方法#2:

SELECT
a.SLXID
FROM dbo.Salesforce_Contacts a WITH(NOLOCK)
JOIN _SLX_Contact b WITH(NOLOCK)
ON a.SLXID = b.Contactid
CROSS APPLY
dbo._SFContact_Functions_TEMP(a.FirstName, a.LastName, a.Title, a.Contact_Status__c)  CA
WHERE 
a.IsDeleted = 0
AND
(
b.FirstName != CA.FirstName_32
AND b.LastName != CA.LastName_32 -- etc.
AND b.Title != CA.Title_64
AND b.Status != CA.Contact_Status
)

这是#2中使用的表值函数:

ALTER FUNCTION dbo._SFContact_Functions_TEMP 
( 
 @FirstName VARCHAR(40) 
,@LastName VARCHAR(80)
,@Title VARCHAR(128)
,@Status VARCHAR(20)
)

RETURNS TABLE

AS

RETURN

SELECT 
 LEFT(@FirstName, 32) FirstName_32
,LEFT(@LastName, 32) LastName_32
,LEFT(@Title, 64) Title_64
,dbo.GetSLXContactStatus(@Status) Contact_Status
GO

最后是执行计划(实际):

对于#1 对于 #2 非常感谢任何帮助或建议!

【问题讨论】:

我更喜欢没有自定义函数的版本;它似乎更简单。但是你的问题是什么? @GordonLinoff 我很可能是强迫症。查询需要 20-40 秒才能完成,这非常长(相对而言),所以我想尽可能加快速度。或许左函数等,所花费的时间是无法避免的? 【参考方案1】:

第一种方法更好。

首先,您要做什么?数据库设计似乎是错误的。

这是一次性查询还是永久查询。

假设你正在尝试做的事情是正确的,那么 20-40 秒的时间要少得多。

还显示 dbo.GetSLXContactStatus(a.Contact_Status__c) 内部的内容。

可能一次这样尝试,

tABLE dbo.Salesforce_Contacts
cLUSTERED INDEX ON SLXID
Create non clusterindex on IsDeleted AND INCLUDE COLUMN(FirstName,LastName,Title)

tABLE dbo._SLX_Contact
cLUSTERED INDEX ON Contactid

;With CTE
(
 SELECT SLXID ,LEFT(a.FirstName, 32) FirstName, LEFT(a.LastName, 32) LastName
, LEFT(a.Title, 64) Title
FROM dbo.Salesforce_Contacts a WITH(NOLOCK)

WHERE
a.IsDeleted = 0

)

select * from CTE
JOIN _SLX_Contact b WITH(NOLOCK)
    ON a.SLXID = b.Contactid
WHERE 
a.FirstName=! b.FirstName
 AND (a.LastName != b.LastName
AND a.Title != b.Title
AND dbo.GetSLXContactStatus(a.Contact_Status__c) != b.Status-- EXPLAIN THIS
)

如果您有很多记录,那么您可以将 CTE 记录插入到具有适当索引的临时表中

【讨论】:

【参考方案2】:

从提供的图片来看,性能和自动优化计划#1 和#2 几乎没有差异。 SSMS 会提示您一个额外的索引。如果 #1 和 #2 不同(几乎不可能),请分析它们。尝试添加提供的索引。分析计划的具体用途。

【讨论】:

以上是关于使用带有标量函数的 where 条件与使用交叉应用和表值函数的 where 条件的主要内容,如果未能解决你的问题,请参考以下文章

选择与表值函数连接中的 Oracle 标量函数

如何使用带有交叉应用的 SQL OPENJSON 函数测量 json 文件中数组内数组的长度

在 Join 条件中使用标量函数

Mysql数据库操作总结

使用 codeigniter 中的 where 条件计算非空值

在mysql中使用带有单个关键字的单个where条件搜索表的所有列