有没有办法优化递归查询?
Is there a way to optimize recursive query?
我有这个查询,它创建了相同 table 的两个副本(原始 table 没有唯一 ID)所以我使用 row_number
来订购 tables:
SELECT ROW_NUMBER() OVER(ORDER BY Policy ASC) AS RowNumber, *
INTO Example1
FROM Payments
SELECT ROW_NUMBER() OVER(ORDER BY Policy ASC) AS RowNumber, *
INTO Example2
FROM Payments
我想检查 'Import' 中的值是否与 'Import' 中的值相同,该值来自我的 table 中按政策排序的实际值之前的行。
我与我的两个 table 进行了比较,使用行号检查实际值之前的值,如果它们都相等,则将列 'Review' 设置为选中,这意味着导入它与从实际值之前的行导入相同。
这是我所做的,但是执行起来需要很长时间...所以我想知道我是否可以优化此查询或以不同的方式做我想做的事?
DECLARE @intCount INT
SELECT @intCount = COUNT(DISTINCT(RowNumber)) FROM Example1
DECLARE @i int
SET @i = 1
WHILE @i <= @intCount
BEGIN
DECLARE @Import decimal(15,2)
SELECT @Import = Import
FROM Example1
WHERE RowNumber = @i
DECLARE @RowNumberBefore bigint
SELECT @RowNumberBefore = RowNumber
FROM Example1
WHERE RowNumber = @i - 1
UPDATE TOP (1) Example1
SET Review = 'Checked'
FROM Example1 a
JOIN Example2 b ON a.Policy = b.Policy
WHERE a.Import = @Import
AND a.RowNumber = @RowNumberBefore
SET @i = @i + 1
END
期望结果示例:
如果我有这个:
RowNumber Policy Import Review
--------- ------ ------ ------
1 0001 586.45
2 0002 586.45
3 0003 65.50
4 0004 249.30
5 0005 65.50
6 0005 153.35
7 0006 32.50
8 0006 32.50
9 0007 32.50
10 0009 250.00
我想用 'checked' 更新评论,如果它的导入与实际导入之前的导入相同(我不能将策略用作 id,因为它可以有重复的值)。
因此,如果我有按策略排序的值(使用行数),我将使用 rowNumber
按照我想要的顺序比较 'Policy':
RowNumber Policy Import Review
--------- ------ ------ ------
1 0001 586.45 Checked
2 0002 586.45 Checked
3 0003 65.50
4 0004 249.30
5 0005 65.50
6 0005 153.35
7 0006 32.50 Checked
8 0006 32.50 Checked
9 0007 32.50 Checked
10 0009 250.00
And I want to check if the value in 'Import' it's the same as the value in 'Import' from the row before the actual one from my table ordered by policy.
我不明白 row_number()
或 update
与这个问题有什么关系。如果要识别 import
更改的行,那么如何:
select p.*
from (select p.*,
lag(p.import) over (order by p.policy) as prev_import
from payments p
) p
where prev_import <> import;
编辑:
在 SQL Server 2008 中,您只需使用 outer apply
即可:
select p.*
from (select p.*, p2.import as prev_import
from payments p outer apply
(select top 1 p2.*
from payments p2
where p2.policy < p.policy
order by p2.policy desc
) p2
) p
where prev_import <> import;
性能仍然很差,但 payments(policy, import)
上的索引会有所帮助。
您可以使用 LAG()
在一个查询中执行此操作以获取前一行的值,如下所示:
SELECT *, CASE WHEN Import = LAG(Import) over (ORDER BY Policy)
THEN 'Checked'
ELSE 'Whatever'
END Review
INTO Example1
FROM Payments
SQL Server 2008 及之前的版本将使用与您的方法类似的逻辑,但在 table 级别而不是行中执行。这是一种方法:
SELECT p.*, CASE WHEN p.Import = p2.Import
THEN 'Checked'
ELSE 'Whatever'
END as Review
INTO Example1
FROM (SELECT *, ROW_NUMBER() over (ORDER BY policy) RN
FROM Payment) p
LEFT JOIN (SELECT Import, ROW_NUMBER() over (ORDER BY policy) RN
FROM Payment) p2 on p.RN = p2.RN - 1
首先不需要用row_number创建两个版本。您可以加入相同的 table.
SELECT Payment.Policy, Payment.Import, ROW_NUMBER() over (ORDER BY policy) RN
INTO #temp
FROM Payment
如果您的 table 很大,您现在可以为其添加索引。注意我只使用了我需要的列。那么查询就是
SELECT t.Import,t.policy, CASE WHEN t.Import = t2.Import
THEN 'Checked'
ELSE 'Whatever'
END as Review
FROM #Temp t
LEFT JOIN #Temp t2 on t.RN = t2.RN - 1
如果您真的想更新原始 table,那么您还有一个问题,因为您在 table 中没有唯一标识符。那你加入什么?此外,我可以看到您的数据存在一些问题,因为您已根据政策订购但有多个政策记录。假设您有以下数据:
RowNumber Policy Import Review
--------- ------ ------ ------
1 0001 586.45
2 0002 586.45
3 0003 65.50
4 0004 249.30
5 0005 65.50
6 0005 249.30
7 0006 32.50
8 0006 32.50
9 0007 32.50
10 0009 250.00
现在政策 0004 和 0005 中的审核值可能取决于它选择处理两条 0005 记录的方式。一个匹配,但另一个不匹配,您无法保证这些 005 记录的顺序。整个问题是一个经典案例,说明为什么你不应该在没有主键的情况下创建 table。
我有这个查询,它创建了相同 table 的两个副本(原始 table 没有唯一 ID)所以我使用 row_number
来订购 tables:
SELECT ROW_NUMBER() OVER(ORDER BY Policy ASC) AS RowNumber, *
INTO Example1
FROM Payments
SELECT ROW_NUMBER() OVER(ORDER BY Policy ASC) AS RowNumber, *
INTO Example2
FROM Payments
我想检查 'Import' 中的值是否与 'Import' 中的值相同,该值来自我的 table 中按政策排序的实际值之前的行。
我与我的两个 table 进行了比较,使用行号检查实际值之前的值,如果它们都相等,则将列 'Review' 设置为选中,这意味着导入它与从实际值之前的行导入相同。
这是我所做的,但是执行起来需要很长时间...所以我想知道我是否可以优化此查询或以不同的方式做我想做的事?
DECLARE @intCount INT
SELECT @intCount = COUNT(DISTINCT(RowNumber)) FROM Example1
DECLARE @i int
SET @i = 1
WHILE @i <= @intCount
BEGIN
DECLARE @Import decimal(15,2)
SELECT @Import = Import
FROM Example1
WHERE RowNumber = @i
DECLARE @RowNumberBefore bigint
SELECT @RowNumberBefore = RowNumber
FROM Example1
WHERE RowNumber = @i - 1
UPDATE TOP (1) Example1
SET Review = 'Checked'
FROM Example1 a
JOIN Example2 b ON a.Policy = b.Policy
WHERE a.Import = @Import
AND a.RowNumber = @RowNumberBefore
SET @i = @i + 1
END
期望结果示例:
如果我有这个:
RowNumber Policy Import Review
--------- ------ ------ ------
1 0001 586.45
2 0002 586.45
3 0003 65.50
4 0004 249.30
5 0005 65.50
6 0005 153.35
7 0006 32.50
8 0006 32.50
9 0007 32.50
10 0009 250.00
我想用 'checked' 更新评论,如果它的导入与实际导入之前的导入相同(我不能将策略用作 id,因为它可以有重复的值)。
因此,如果我有按策略排序的值(使用行数),我将使用 rowNumber
按照我想要的顺序比较 'Policy':
RowNumber Policy Import Review
--------- ------ ------ ------
1 0001 586.45 Checked
2 0002 586.45 Checked
3 0003 65.50
4 0004 249.30
5 0005 65.50
6 0005 153.35
7 0006 32.50 Checked
8 0006 32.50 Checked
9 0007 32.50 Checked
10 0009 250.00
And I want to check if the value in 'Import' it's the same as the value in 'Import' from the row before the actual one from my table ordered by policy.
我不明白 row_number()
或 update
与这个问题有什么关系。如果要识别 import
更改的行,那么如何:
select p.*
from (select p.*,
lag(p.import) over (order by p.policy) as prev_import
from payments p
) p
where prev_import <> import;
编辑:
在 SQL Server 2008 中,您只需使用 outer apply
即可:
select p.*
from (select p.*, p2.import as prev_import
from payments p outer apply
(select top 1 p2.*
from payments p2
where p2.policy < p.policy
order by p2.policy desc
) p2
) p
where prev_import <> import;
性能仍然很差,但 payments(policy, import)
上的索引会有所帮助。
您可以使用 LAG()
在一个查询中执行此操作以获取前一行的值,如下所示:
SELECT *, CASE WHEN Import = LAG(Import) over (ORDER BY Policy)
THEN 'Checked'
ELSE 'Whatever'
END Review
INTO Example1
FROM Payments
SQL Server 2008 及之前的版本将使用与您的方法类似的逻辑,但在 table 级别而不是行中执行。这是一种方法:
SELECT p.*, CASE WHEN p.Import = p2.Import
THEN 'Checked'
ELSE 'Whatever'
END as Review
INTO Example1
FROM (SELECT *, ROW_NUMBER() over (ORDER BY policy) RN
FROM Payment) p
LEFT JOIN (SELECT Import, ROW_NUMBER() over (ORDER BY policy) RN
FROM Payment) p2 on p.RN = p2.RN - 1
首先不需要用row_number创建两个版本。您可以加入相同的 table.
SELECT Payment.Policy, Payment.Import, ROW_NUMBER() over (ORDER BY policy) RN
INTO #temp
FROM Payment
如果您的 table 很大,您现在可以为其添加索引。注意我只使用了我需要的列。那么查询就是
SELECT t.Import,t.policy, CASE WHEN t.Import = t2.Import
THEN 'Checked'
ELSE 'Whatever'
END as Review
FROM #Temp t
LEFT JOIN #Temp t2 on t.RN = t2.RN - 1
如果您真的想更新原始 table,那么您还有一个问题,因为您在 table 中没有唯一标识符。那你加入什么?此外,我可以看到您的数据存在一些问题,因为您已根据政策订购但有多个政策记录。假设您有以下数据:
RowNumber Policy Import Review
--------- ------ ------ ------
1 0001 586.45
2 0002 586.45
3 0003 65.50
4 0004 249.30
5 0005 65.50
6 0005 249.30
7 0006 32.50
8 0006 32.50
9 0007 32.50
10 0009 250.00
现在政策 0004 和 0005 中的审核值可能取决于它选择处理两条 0005 记录的方式。一个匹配,但另一个不匹配,您无法保证这些 005 记录的顺序。整个问题是一个经典案例,说明为什么你不应该在没有主键的情况下创建 table。