根据行号执行 SQL 更新并使用前一行进行计算

Posted

技术标签:

【中文标题】根据行号执行 SQL 更新并使用前一行进行计算【英文标题】:Performing SQL updates based on row number and using previous row for calculations 【发布时间】:2021-04-12 18:32:18 【问题描述】:

我有一个用于执行一些计算的 python/pandas 代码,但我遇到了性能问题。我正在尝试在 SQL 上编写所有内容,并使用 BigQuery 更新表。

我面临的问题是根据行号更新现有表并使用以前的行进行计算。

下面的代码是我正在使用的,现在我需要在 SQL 中执行此操作。 “i”是行号。

if i <= 4:
    perfil['B'].iloc[i] = 0
else:
    perfil['B'].iloc[i] = perfil['A'] + perfil['B'].iloc[i - 2]

所以,对于前 5 行,我做了一些不使用前几行的计算。但在那之后,计算将使用以前的行。

我的表已经这样创建了:

| DEPTH_M   | A | B |
|-----------|---|---|
|1.2        |2  |0  |
|1.4        |3  |0  |
|1.6        |6  |0  |
|1.8        |2  |0  |
|2.0        |1  |0  |
|2.2        |6  |0  |
|2.4        |7  |0  |
|2.6        |6  |0  |

在一些用户输入之后,我需要使用我之前显示的代码在表格中执行更新,结果如下:

| DEPTH_M   | A | B                                                    |
|-----------|---|------------------------------------------------------|
|1.2        |2  |0                                                     |
|1.4        |3  |0                                                     |
|1.6        |6  |0                                                     |
|1.8        |2  |0                                                     |
|2.0        |1  |0  (Zero till now, first 5 rows are filled with zeros)|
|2.2        |6  |6 (6 from A + 0 from the past two rows)               |
|2.4        |7  |7  (7 from A + 0 from the past two rows)              |
|2.6        |6  |12 (6  from A + 6 from the past two rows)             |

提前致谢!

【问题讨论】:

请提供示例数据、期望的结果以及您正在实施的逻辑的清晰解释。 你用的是哪个版本的mysql @GordonLinoff 我编辑了帖子,抱歉信息不足。 @KaziMohammadAliNur 实际上我正在使用 Python 脚本中的 Google BigQuery API。 【参考方案1】:

考虑下面的例子

#standardSQL
with `project.dataset.table` as (
  select 1.2 DEPTH_M, 2 A, 0 B union all
  select 1.4, 3, 0 union all
  select 1.6, 6, 0 union all
  select 1.8, 2, 0 union all
  select 2.0, 1, 0 union all
  select 2.2, 6, 0 union all
  select 2.4, 7, 0 union all
  select 2.6, 6, 0 union all 
  select 3.0, 1, 0 union all
  select 3.2, 6, 0 union all
  select 3.4, 7, 0 
)
select DEPTH_M, A,  
  ifnull(if(rn <= 5, 0, A) + sum(if(rn <= 5, 0, A)) over win, 0) as B,
  format('%i + %i', if(rn <= 5, 0, A), ifnull(sum(if(rn <= 5, 0, A)) over win, 0)) as expalnation
from (
  select DEPTH_M, A, B,
    row_number() over(order by DEPTH_M) rn
  from `project.dataset.table`
)
window win as (partition by mod(rn, 2) order by rn rows between unbounded preceding and 1 preceding)
order by DEPTH_M           

有输出

如果您需要更新表格 - 您可以在下面使用

update `project.dataset.table` u
set B = s.B
from (
  select  DEPTH_M, A, ifnull(if(rn <= 5, 0, A) + sum(if(rn <= 5, 0, A)) over win, 0) as B 
  from (
    select DEPTH_M, A, B,
      row_number() over(order by DEPTH_M) rn
    from `project.dataset.table`
  )
  window win as (partition by mod(rn, 2) order by rn rows between unbounded preceding and 1 preceding)
) s
where u.DEPTH_M = s.DEPTH_M
and u.A = s.A;

【讨论】:

【参考方案2】:

您的循环隐式定义了递归关系。如果您展开单个术语 Bn,您将得到如下扩展表达式:

最终每个Bn 都归结为前面A 项的总和(只要n &gt; 5。)看到B1B5A 系列的初始值是强制为零,这个简单的映射函数将无需迭代存储输出即可工作。

请注意,要组合的数组索引(下标)都偏移了 2 的连续倍数。这意味着它们都属于同一个模同余类,也就是说,它们除以 2 时具有相同的余数。这就是使用mod() 的分区进来的地方。

with r as ( /* attach row numbers starting with 1 */
    select *, DEPTH_M, A, B, row_number() over (order by DEPTH_M) as rn
    from T
)
select DEPTH_M, A,  
     sum(case when rn > 5 then A else 0 end)
         over (partition by mod(rn, 2) order by rn) as B,
from r
order by DEPTH_M;

此查询不需要处理空值。它适用于 sum() over () 的默认窗口,其中包括当前行。

【讨论】:

以上是关于根据行号执行 SQL 更新并使用前一行进行计算的主要内容,如果未能解决你的问题,请参考以下文章

基于 OVER PARTITION BY 子句的 SQL 计算列

重学SQL窗口函数

SQL如何计算订单购买之间的平均时间? (根据下一行和上一行进行sql计算)

迭代数据框并根据一列的值在具有前一行值的新列中执行操作

Oracle SQL:使用前一行计算值

根据前一行和相同的计算列计算列值