Mad*_*Boy 2 sql sql-server-2008
我在MS SQL中有这个查询非常奇怪(至少从我的角度来看).
我有一个名为dbo.NajblizszaDataWyceny(3,'2010-02-05')的用户定义函数,它可以简单地检查一个表中的TOP 1条目以及其他几个表.查询本身需要几毫秒,所以这不是一个大问题,但无论如何我都会显示该功能.
CREATE FUNCTION [dbo].[NajblizszaDataWyceny] (@idPortfela INT, @dataWaluty DATETIME)
RETURNS DATETIME
AS BEGIN
RETURN (
SELECT TOP 1 [WycenaData]
FROM [BazaZarzadzanie].[dbo].[Wycena] t1
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfeleKonta] t3
ON t1.[KlienciPortfeleKontaID] = t3.[KlienciPortfeleKontaID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfele] t4
ON t3.[PortfelID] = t4.[PortfelID]
WHERE [WycenaData] <= @dataWaluty AND [t3].[PortfelID] = @idPortfela
ORDER BY [WycenaData] DESC)
END
Run Code Online (Sandbox Code Playgroud)
当我以下列方式使用此功能时:
DECLARE @dataWyceny DATETIME
SET @dataWyceny = dbo.NajblizszaDataWyceny(3, '2010-02-05')
SELECT t1.[KlienciPortfeleKontaID],
t4.[PortfelIdentyfikator] AS 'UmowaNr',
t5.[KlienciRachunkiNumer],
[WycenaData],
t2.[InISIN] AS 'InstrumentISIN',
t2.[InNazwa] AS 'InstrumentNazwa',
[WycenaWartosc]
FROM [BazaZarzadzanie].[dbo].[Wycena] t1
LEFT JOIN [BazaZarzadzanie].[dbo].[Instrumenty] t2
ON t1.[InID] = t2.[InID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfeleKonta] t3
ON t1.[KlienciPortfeleKontaID] = t3.[KlienciPortfeleKontaID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfele] t4
ON t3.[PortfelID] = t4.[PortfelID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciRachunki] t5
ON t3.[KlienciRachunkiID] = t5.[KlienciRachunkiID]
LEFT JOIN [BazaZarzadzanie].[dbo].[WycenaTyp] t6
ON t1.[WycenaTyp] = t6.[WycenaTyp]
WHERE WycenaData = @dataWyceny AND t3.[PortfelID] = 3
ORDER BY t5.[KlienciRachunkiNumer],
WycenaData
Run Code Online (Sandbox Code Playgroud)
运行需要1秒钟.但是当我将用户函数直接放在WHERE中时,它看起来像:
SELECT t1.[KlienciPortfeleKontaID],
t4.[PortfelIdentyfikator] AS 'UmowaNr',
t5.[KlienciRachunkiNumer],
[WycenaData],
t2.[InISIN] AS 'InstrumentISIN',
t2.[InNazwa] AS 'InstrumentNazwa',
[WycenaWartosc]
FROM [BazaZarzadzanie].[dbo].[Wycena] t1
LEFT JOIN [BazaZarzadzanie].[dbo].[Instrumenty] t2
ON t1.[InID] = t2.[InID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfeleKonta] t3
ON t1.[KlienciPortfeleKontaID] = t3.[KlienciPortfeleKontaID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciPortfele] t4
ON t3.[PortfelID] = t4.[PortfelID]
LEFT JOIN [BazaZarzadzanie].[dbo].[KlienciRachunki] t5
ON t3.[KlienciRachunkiID] = t5.[KlienciRachunkiID]
LEFT JOIN [BazaZarzadzanie].[dbo].[WycenaTyp] t6
ON t1.[WycenaTyp] = t6.[WycenaTyp]
WHERE WycenaData = dbo.NajblizszaDataWyceny(3, '2010-02-05') AND t3.[PortfelID] = 3
ORDER BY t5.[KlienciRachunkiNumer],
WycenaData
Run Code Online (Sandbox Code Playgroud)
完成需要1.5分钟.任何人都可以解释为什么会这样吗?
在SQL Server中,函数不被认为是纯粹的,这意味着查询优化器不会缓存函数的结果并重新使用它; 每次引用时都会调用该函数.对于只返回数字的简单函数来说,这是正确的(正如我们在使用函数模拟常量的项目中发现的成本...).
所以在第一个版本中,当你调用它时,函数被调用一次,结果被手动缓存并在查询中重用.但是在第二个版本中,当WHERE子句尝试匹配行时,将为每一行调用该函数.如果你有很多行,那么每行几毫秒开始加起来.
(另请注意,您的查询在语义上是不同的.在第一个查询中,您说"其中的内容与我在开始时评估的函数的结果相同",而在第二个查询中,您说的是"事情在哪里"因为我在这个特定的实例中评估函数的结果,因为我考虑了行".因为你的函数使用了一个SELECT语句 - 取决于事务隔离级别 - 它可能会返回不同行的不同结果基础数据发生变化.)