我有一个带有MERGE
语句的代码,该语句调用了标量函数,但性能却很差。
这段代码可能要花费我30分钟甚至更长的时间,有时甚至会在中间失败。
我的代码:
MERGE INTO Table1 AS md
USING (SELECT * FROM Table2 edb
WHERE edb.FileId = @FileId
AND edb.IsRowError = 0) edbTable ON dbo.GetId(edbTable.Filed1, edbTable.Filed2, edbTable.Filed3, edbTable.Filed4, edbTable.Filed5, edbTable.Filed6) = md.Id
AND md.IsActive = 1
WHEN MATCHED THEN
UPDATE
SET .
.
.
功能:
ALTER FUNCTION [dbo].[GetId]
(@Filed1 NVARCHAR(9),
@Filed2 NVARCHAR(9),
@Filed3 NVARCHAR(13),
@Filed4 INT,
@Filed5 DATE,
@Filed6 INT)
RETURNS INT
AS
BEGIN
DECLARE @Id INT = NULL
IF ((@Filed5 IS NULL) OR (@Filed6 = 0))
BEGIN
SET @Id = (SELECT TOP 1 md.Id
FROM Table1 md with(NOLOCK)
JOIN Table3 mb with(NOLOCK) ON md.Table3Id = mb.Id
WHERE (mb.Filed1 = @Filed1 AND mb.Filed2 = @Filed2
OR mb.Filed1 = @Filed2 AND mb.Filed2 = @Filed1)
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4
ORDER BY md.LastUpdateDate DESC)
IF @Id IS NOT NULL
RETURN @Id
ELSE
SET @Id = (SELECT TOP 1 md.Id
FROM Table1 md with(NOLOCK)
JOIN Table3 mb with(NOLOCK) ON md.Table3Id = mb.Id
WHERE (mb.Filed1 = @Filed1 OR mb.Filed1 = @Filed1)
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND Filed4 = md.Filed4
AND @Filed1 > 0
ORDER BY md.LastUpdateDate DESC)
IF @Id IS NOT NULL
RETURN @Id
ELSE
set @Id =(select top 1 md.Id from Table1 md with(NOLOCK)
join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
where (mb.Filed1 = @Filed2
or mb.Filed2 = @Filed2 )
and mb.Filed7 = 1
and @Filed3 = md.Filed3
and @Filed4 = md.Filed4
and @Filed2 >0
order by md.LastUpdateDate desc)
end
else
begin
set @Id = (select top 1 md.Id from Table1 md with(NOLOCK)
join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
where (mb.Filed1 = @Filed1 and mb.Filed2 = @Filed2
or mb.Filed1 = @Filed2 and mb.Filed2 = @Filed1 )
and mb.Filed7 = 1
and @Filed3 = md.Filed3
and @Filed4 = md.Filed4
and (@Filed5 = md.ContractDate or (@Filed5 is null and md.ContractDate is null))
and @Filed6 = md.PurchasePrice)
if @Id is not NULL
RETURN @Id
else
set @Id =(select top 1 md.Id from Table1 md with(NOLOCK)
join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
where (mb.Filed1 = @Filed1
or mb.Filed2 = @Filed1 )
and mb.Filed7 = 1
and @Filed3 = md.Filed3
and @Filed4 = md.Filed4
and (@Filed5 = md.ContractDate or (@Filed5 is null and md.ContractDate is null))
and @Filed6 = md.PurchasePrice
and @Filed1 >0)
if @Id is not NULL
RETURN @Id
else
set @Id =(select top 1 md.Id from Table1 md with(NOLOCK)
join Table3 mb with(NOLOCK) on md.Table3Id = mb.Id
where (mb.Filed1 = @Filed2
or mb.Filed2 = @Filed2 )
and mb.Filed7 = 1
and @Filed3 = md.Filed3
and @Filed4 = md.Filed4
and (@Filed5 = md.ContractDate or (@Filed5 is null and md.ContractDate is null))
and @Filed6 = md.PurchasePrice
and @Filed2 >0)
if @Id is not NULL
RETURN @Id
end
RETURN @Id
END
我需要更改什么? 如何更改代码?
我需要所有If
状态,因为对于要检索的记录有一个优先选择,所以我无法将其转换为join
。
因为如果有一些join
,他们会给我一个肯定的答案,那么它将给我列出所有来自该人群的人的名单,因此,重要的是要知道这是由于第一次加入还是第二次或第三次加入,因为我有优先选择权。
如果我不是将if
改为join
,是否可以提取出第一行?就是说:如果我加入,并且第一个没有结果,第二个有结果,而在第三个中,则仅返回第二个而不是第三个的结果-有这样的事情吗?
答案 0 :(得分:0)
好的,这是您逻辑的精确复制,但是作为内联表值函数。在必要条件相同的情况下,很有可能进一步削减这一比例。例如,mb.File7 = 1 AND @Filed3 = md.Filed3 AND @Filed4 = md.Filed4
出现在每个 WHEN
子句中,因此应将其从所有WHEN
子句中移出并放入WHERE
。这样可能会使查询的性能更高,也更容易阅读。
无论如何,这执行相同的逻辑。我在代码中添加了一些注释,不过您可能想看看:
ALTER FUNCTION dbo.GetID (@Filed1 nvarchar(9), --Honestly, give these better names. These parameters don't tell you anything about what they are
--which'll make it harder for people who don't know your function to use it.
@Filed2 nvarchar(9),
@Filed3 nvarchar(13),
@Filed4 int,
@Filed5 date,
@Filed6 int)
RETURNS TABLE AS RETURN
SELECT TOP 1 --This may be a bad idea, we'll see
CASE WHEN @Filed5 IS NULL OR @Filed6 = 0 THEN CASE WHEN (mb.Filed1 = @Filed1 AND mb.Filed2 = @Filed2
OR mb.Filed1 = @Filed2 AND mb.Filed2 = @Filed1)
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4 THEN md.Id
WHEN mb.Filed1 = @Filed1
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4
AND @Filed1 > 0 THEN md.ID
WHEN @filed2 IN (mb.Filed1, mb.Filed2)
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4
AND @Filed2 >0 THEN md.ID
END
--None of these in your original had an ORDER BY clause, was this intentional?
--If so, I assume you want a random row, instead. So you'll need a further
--CASE expression in your ORDER BY for these, and then the return either NEWID or md.LastUpdateDate
ELSE CASE WHEN (mb.Filed1 = @Filed1 --Your parenthesis seem wrong here
AND mb.Filed2 = @Filed2 --YOu have an OR in the middle
OR mb.Filed1 = @Filed2 --So I doubt this does what you want
AND mb.Filed2 = @Filed1 ) --But I haven't correct, as I don't know what it should do
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4
AND (@Filed5 = md.ContractDate
OR (@Filed5 IS NULL AND md.ContractDate IS))
AND @Filed6 = md.PurchasePrice THEN md.ID
WHEN (mb.Filed1 = @Filed1
OR mb.Filed2 = @Filed1)
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4
AND (@Filed5 = md.ContractDate
OR (@Filed5 IS NULL AND md.ContractDate IS NULL))
AND @Filed6 = md.PurchasePrice
AND @Filed1 >0 THEN md.Id
WHEN (mb.Filed1 = @Filed2
OR mb.Filed2 = @Filed2)
AND mb.Filed7 = 1
AND @Filed3 = md.Filed3
AND @Filed4 = md.Filed4
AND (@Filed5 = md.ContractDate
OR (@Filed5 IS NULL AND md.ContractDate IS NULL))
AND @Filed6 = md.PurchasePrice
AND @Filed2 >0 THEN md.Id
END
END AS Id
FROM dbo.Table1 md --Why md when the table is called "Table1". I assume this is anonymised
JOIN dbo.Table3 md ON md.Table3Id = mb.Id
ORDER BY md.LastUpdateDate DESC;
GO