【问题标题】:How to check the current row with next row and update the current row not using while loop and cursor如何使用下一行检查当前行并更新当前行而不使用while循环和游标
【发布时间】:2021-09-22 04:01:53
【问题描述】:

除了使用游标或while循环检查当前行与下一行并更新当前行并通过数据集中的所有行执行此操作之外,还有其他方法吗?

例如,这是我们的表:

id  date                      value
1   2020-09-01 00:00:00.000   0.00
2   2020-09-01 00:15:00.000   0.30
3   2020-09-01 00:30:00.000   0.00
4   2020-09-01 00:45:00.000   0.15
5   2020-09-01 01:00:00.000   0.20
6   2020-09-01 01:15:00.000   0.10
7   2020-09-01 01:30:00.000   0.10
8   2020-09-01 01:45:00.000   0.00
9   2020-09-01 02:00:00.000   0.00



declare @i int = 1, @new_value money = 0.3

while @i <= 10
begin

        select @new_value = iif(value> (@new_value * 0.2) and value < @new_value,
                                       value,
                                       @new_value)
        from table
        where value >= 0.1
        and id = @i + 1;

    set @i = @i + 1
end

select @new_value -- Output will be 0.10

这个while循环遍历每一行并根据某些条件更新@new_value,然后检查下一行。

我想看看有没有办法不使用循环或游标并获得相同的结果。

【问题讨论】:

  • 当您说check the current row with the next row 时,we have new value = 3 的作用是什么?在表格中,您从未提及下一行的任何内容
  • 用@Squirrel 的更多细节更新了问题。谢谢。
  • 可以使用递归cte。

标签: sql tsql sql-server-2016


【解决方案1】:

这里是递归cte

declare @tbl table
(
    id  int,
    [value] decimal(5,2)
)

insert into @tbl values
(1, 0.00),
(2, 0.30),
(3, 0.00),
(4, 0.15),
(5, 0.20),
(6, 0.10),
(7, 0.10),
(8, 0.00),
(9, 0.00);

declare @new_value decimal(5,2) = 0.3;

with rcte as
(
    select  id, value, 
            new_value = iif(value > (@new_value * 0.2) 
                        and value < @new_value,
                        value,
                        new_value)
    from    @tbl
    where   id  = 1

    union all

    select  t.id, t.value, 
            new_value = iif(t.value > (r.new_value * 0.2) 
                        and t.value < r.new_value,
                        t.value,
                        r.new_value)
    from    rcte r
            inner join @tbl t   on  r.id    = t.id - 1
)
select  *
from    rcte

【讨论】:

  • 谢谢!虽然性能仍然是一个问题,但比使用循环更好。我的数据集大约有 10 万行
  • 这样的计算,你无法避免某种形式的循环
  • @Squirrel 你确定这本身需要一个递归循环吗,看起来窗口函数应该可以解决问题
  • @Charlieface 要是能有窗口功能就好了
  • 只是想一想:虽然我在小提琴中得到了相同的结果,但语义并不完全相同。如果一个值 > 0.2 * 以前的值,但不是 > 0.2 *(以前的值 > 0.2 of its 以前的值),那么你和 OP 排除它,我包含它。
【解决方案2】:

您可以为此使用LAG 窗口函数以及MIN 聚合

SELECT MIN(t.value)
FROM (
    SELECT *,
        LAG(t.value, 1, 0.3) OVER (ORDER BY t.id) AS prevValue
    FROM [table] AS t
) AS t
WHERE t.value > (t.prevValue * 0.2) AND value < prevValue;

SQL Fiddle


我强烈建议您不要使用money 数据类型,而是使用decimal

【讨论】:

  • 谢谢!虽然输出是相同的并且滞后是一个更好的选择,但在我的情况下,prevValue 总是逐行变化。在您的答案中,您获得所有 preValues 一次,然后执行 where 子句。在我的情况下,应该每次都检查 where 子句并更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
  • 2020-07-23
  • 2016-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多