SQL中的高级循环

时间:2018-10-18 12:17:09

标签: mysql sql loops

create table Numbers(id int, Number float);
insert into Numbers(id, Number) 
    values  (1, 3.00), 
            (2, 3.30), 
            (3, 4.50), 
            (4, 2.25), 
            (5, 6.50);

select min(Number) from Numbers into @minNumber;
select id, Number, (Number - @minNumber) from Numbers;

我需要显示数字列表,并且要显示每个数字旁边的数字,我必须显示数字本身与当前行上方的最小数字(除当前行以外的所有先前行)之间的差。所以输出应该是。

3.00  3.00
3.30  0.30
4.50  1.50
2.25  -0.75
6.50  4.25

现在,它仅显示数字和所有数字的最小值(2.25)之间的差。不确定如何运行循环以使其起作用。

3 个答案:

答案 0 :(得分:3)

假设您有一个主键列id始终请记住,数据是以无序方式存储的,因此,如果没有PK,我们实际上无法定义当前行的“行上方”。

您可以利用Correlated Subqueries来确定当前行(Number)上方各行中的最小t2.id < t1.id

对于第一行,上面没有任何数字,因此我们将不得不使用Coalesce()函数将null的值视为0:

DB Fiddle DEMO

SELECT 
  t1.id, 
  t1.Number, 
  (t1.Number - COALESCE((SELECT MIN(t2.Number)
                         FROM Numbers AS t2 
                         WHERE t2.id < t1.id),0)) AS difference 
FROM Numbers AS t1 
ORDER BY t1.id 

从MySQL 8.0.2 开始,我们也可以使用Window Functions with Frame。我们可以考虑从一开始(UNBOUNDED PRECEDING)到当前行之前的一行(1 PRECEDING)的“递增”帧,并确定最小值。

尝试以下操作(仅适用于 MySQL 8.0.2 + ):

DB Fiddle Demo

SELECT 
  id, 
  Number, 
  (Number - 
   COALESCE(MIN(Number) OVER(ORDER BY id 
                             ROWS BETWEEN UNBOUNDED PRECEDING 
                              AND 1 PRECEDING)
            ,0)) AS difference 
FROM Numbers 
ORDER BY id 

答案 1 :(得分:1)

可能最有效的方法是使用变量:

select n.*,
       (number -
        (case when (@min2 := @min) = NULL then 0  -- never happens
              when @min := least(coalesce(@min, n.number), n.number) = NULL then 0  -- never happens
              else coalesce(@min2, 0)
         end)
       ) as diff
from numbers n cross join
     (select @min := NULL) params
order by n.id;

DB Fiddle DEMO

答案 2 :(得分:0)

交叉连接怎么了?

SELECT n.id, n.Number, n.Number - MIN(n2.Number)
FROM Numbers n
CROSS JOIN Numbers n2
WHERE 
    n2.id < n.id
GROUP BY  n.id, n.Number;

DB Fiddle Demo