【发布时间】:2016-01-20 22:26:05
【问题描述】:
下面是我的标量函数:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [CheckClients]
(
@UserId Varchar(3),
@DbrNo varchar(10),
@V_DBR_CLIENT varchar(6)
)
RETURNS int
AS
BEGIN
Declare @Flag int
set @Flag=1
if(@V_DBR_CLIENT='XXXXXX')
BEGIN
if((select COUNT(USR_CLI)
from USRAGYCLI
inner join DBR on DBR_CLIENT = USR_CLI
where USR_CODE = @UserId and DBR_SERIES like @DbrNo +'T') <>
(select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like @DbrNo + 'T') OR
(select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like @DbrNo +'T') <= 0)
BEGIN
set @Flag=0
END
END
RETURN @Flag
END
这是我的存储过程:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [SEL_CLI]
@V_USER_ID VARCHAR(3),
@V_NUMBER_OF_ROWS INT,
@V_STARTS_WITH INT
AS
BEGIN
CREATE TABLE #tmpDbrNo
(
Code VARCHAR(10),
Name VARCHAR(100),
NumberOfDebtors int,
rownum int
)
;WITH Temp AS
(
SELECT
CLT_NO AS Code,
CLT_NAME AS Name,
COUNT(DBR_NO) AS NumberOfDebtors
FROM
DBR
JOIN
USRAGYCLI ON DBR_CLIENT = USR_AGY_CLI
JOIN
CLT ON DBR_CLIENT = CLT_NO
WHERE
AND USR_CODE = @V_USER_ID
AND 1 = CheckClients(@V_USER_ID, DBR_NO, DBR_CLIENT)
GROUP BY
CLT_NO, CLT_NAME
)
INSERT INTO #tmpDbrNo
SELECT
Code, Name, NumberOfDebtors,
ROW_NUMBER() OVER (ORDER by Code) rownum
FROM
Temp
SELECT
Code, Name, NumberOfDebtors
FROM
#tmpDbrNo
WHERE
rownum BETWEEN @V_STARTS_WITH AND @V_STARTS_WITH + @V_NUMBER_OF_ROWS
END
执行上述查询大约需要 25 秒,等待时间过长。如果我在where 子句中注释掉调用标量函数的行,则执行查询需要 0 秒。
任何人都可以提出更好的方法来执行查询吗?我试图在如下情况下调用函数,但没有成功。
AND 1 = CASE WHEN DBR_CLIENT='XXXXXX' THEN CheckClients(@V_USER_ID,DBR_NO,DBR_CLIENT) ELSE 1 END
【问题讨论】:
-
那是因为标量函数非常低效。当你把它放在 where 子句中时,就会呈现查询 nonSARGable。换句话说,它必须为表中的每一行计算函数的值。可能可以将您的标量函数转换为内联表值函数,并且性能几乎与排除您现在看到的函数一样快。
-
您可能可以用 SQL
EXISTS替换您的函数用法并显着提高性能。 -
必须使用函数,而不是使用内部子查询
标签: sql-server sql-server-2008 sql-server-2008-r2