以下是我的标量函数:
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
答案 0 :(得分:0)
您可以优化标量函数查询以减少多次读取。像:
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
DECLARE @Count INT = ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE @DbrNo + 'T'
), 0);
IF (
(ISNULL((
SELECT COUNT(USR_CLI)
FROM USRAGYCLI
INNER JOIN DBR ON DBR_CLIENT = USR_CLI
WHERE USR_CODE = @UserId
AND DBR_SERIES LIKE @DbrNo + 'T'
), 0) <> @Count)
OR (@Count <= 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
USR_CODE = @V_USER_ID
AND 1 =
(CASE
WHEN (@V_DBR_CLIENT = 'XXXXXX') THEN
(CASE
WHEN (
ISNULL((
SELECT COUNT(USR_CLI)
FROM USRAGYCLI
INNER JOIN DBR ON DBR_CLIENT = USR_CLI
WHERE USR_CODE = @UserId
AND DBR_SERIES LIKE @DbrNo + 'T'
), 0) <> ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE @DbrNo + 'T'
), 0)
)
OR (ISNULL((
SELECT COUNT(DBR_CLIENT)
FROM DBR
WHERE DBR_SERIES LIKE @DbrNo + 'T'
), 0) <= 0)
THEN 0
ELSE 1
END)
ELSE 1
END)--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
正如您所看到的,标量函数可以包含在同一查询中,但是如果您很好地研究函数,那么标量函数中的查询显然不完全依赖于存储过程中的查询。它正在计数,并且每次都会重新读取和操作表格中的数据。
因此,使用这种类型的查询使得Sargable的非Sargable不会提高性能。该问题的可能解决方案是
答案 1 :(得分:0)
这只是一个黑暗的镜头,因为我们没有提供任何ddl或很多工作。我想我正确地解释了标量函数中的现有逻辑。作为一般规则,您应该避免使用标志。这是一个非常古老的学校思维方式,根本不适合关系数据。我怀疑通过了解实际需求可以大大改善这一点,但这是我能用有限的细节做的最好的。
CREATE FUNCTION [CheckClients]
(
@UserId Varchar(3),
@DbrNo varchar(10),
@V_DBR_CLIENT varchar(6)
)
RETURNS table as return
with RowCounts as
(
select
(
select COUNT(DBR_CLIENT)
from DBR
where DBR_SERIES like @DbrNo + 'T'
) as ClientCount
,
(
select COUNT(USR_CLI)
from USRAGYCLI u
inner join DBR d on d.DBR_CLIENT = u.USR_CLI
where u.USR_CODE = @UserId
and d.DBR_SERIES like @DbrNo +'T'
) as UserCount
)
select case
when @V_DBR_CLIENT = 'XXXXXX' then
Case when rc.UserCount <> rc.ClientCount then 0
when rc.ClientCount < 0 then 0
else 1
end
else 1
end as Flag
from RowCounts rc