sql - 在 SQL Server 中使用插入语句优化必要的 while 循环
问题描述
我有一系列在 while 循环中运行一些数学的程序。他们正在计算运行平均值,其中知道先前计算的值对于获得下一个值是绝对必要的。一个项目与一组值配对,按日期排序,并使用 10 种不同的方法来计算运行平均值。
提前回答一些问题:每次运行的每个 id 的数组长度都是相同的,但如果数据丢失,可能会有空值。我事先不知道正在计算滚动平均值的 id 数量,并且每次都会有所不同。我事先不知道必要的迭代次数,每次都会有所不同。尽管在微积分上工作了一年多,但我还没有找到一种数学方法来将系统扁平化为一个选择/加入——5 个是非线性的,而且我在大学里只有 4 个学期的微积分,所以我无法理解出一个扁平化的解决方案(我为我正在使用的 10 种方法中的 5 种做了,
我的代码运行了,使用 while 循环的 5 个方法确实完成了,但是每个方法运行 5000 个项目(所有项目都有自己的迭代和计算)大约需要 15 分钟到 30 分钟。我需要能够扩展到 300000 个项目,所以 15 分钟是站不住脚的,特别是因为这是在数据库中运行的几十个程序之一。
下面是我的其中一种方法的代码示例。我不是在数学部分寻求帮助(因为需要循环的 5 个过程中的每一个的数学都不同),而是在用作循环背后意图的插入语句中:
create proc analysis.calculateavg_logarithmicshifted
@maxi int --max number of iterations determined by separate proc
as
declare @icount int = 1;
declare @n int;
select @n = max(n) from #temp_pink; --comes from the procedure that calls this one, and is a way to identify the max array size
drop table if exists analysis.avglogarithmicshifted;
create table analysis.avglogarithmicshifted(
id nvarchar(64),
i int,
mu decimal(19, 6),
insertdate datetime,
avgname nvarchar(64)
);
drop table if exists #temp_k;
--Pull only the historic data from the source
select
id,
k,
price
into #temp_k
from #temp_bbfull --original list of data, n elements per id
where history = 1;
while @icount <= @maxi
begin;
drop table if exists #temp_premu;
--Calculate one sub-section of the rolling average
select
id,
sum(log(price)) / (@n - sum(case when price is null then 1 else 0 end)) as premu
into #temp_premu
from #temp_k
where k between @icount and @icount + @n
group by id;
drop table if exists #temp_f;
--Calculate the main component of the rolling average
select
k.id,
@icount + @n as k,
exp(p.premu + (sum(power(log(k.price) - p.premu, 2)) / (2 * (@n - sum(case when k.price is null then 1 else 0 end))))) as price
into #temp_f
from #temp_k k
join #temp_premu p
on p.id= k.id
join #temp_bbfull bb
on bb.id= k.id
and bb.k = k.k
where k.k between @icount
and @icount + @n
group by k.id, p.premu;
--Insert this iteration's rolling average into the table with incremented identifier k
insert into #temp_k (id, k, price)
select
*
from #temp_f
where price is not null;
select @icount = @icount + 1;
end;
--Insert final aggregated data into destination table
insert into analysis.avglogarithmicshifted (id, i, mu, insertdate, avgname)
select
k.id,
k.k - @n as I,
k.price as mu,
getdate() as insertdate,
'Logarithmic Shifted' as avgname
from #temp_k k
where k.k > @n
and k.price is not null;
名称和标识符已从我的原始代码更改,但没有其他任何内容。任何帮助将不胜感激。
我正在使用 SQL Server 17.9.1
解决方案
总体而言,如果不对代码进行重大重新设计,我认为您将无法获得您正在寻找的接近 100 倍(2 个数量级)的改进。我不了解您的代码逻辑,因此无能为力,只能说 SQL Server 2016 支持允许基于集合计算移动平均线的窗口函数。此链接可能会有所帮助
以下是一些优化它的提示:
- 连接许多临时表时,请确保具有大量数据的表具有索引。
- 尽量减少用于存储中间计算结果的临时表的数量。
- 确保中间临时表中的行数尽可能少。
从您给出的示例中,我可以看到一些可能的优化。由于我没有数据,我无法检查它们是否有帮助:
0 #temp_bbfull
- 看起来这个表被用作数据源并且有很多数据。您必须有正确的索引。
CREATE INDEX IX_temp_bbfull ON #temp_bbfull( id, k )
1 尽可能限制你的工作集
select id, k, price
into #temp_k
from #temp_bbfull --original list of data, n elements per id
-- As you are restricting iterations you might as well restrict the number of rows upfront
where history = 1 and k between @icount and @maxi;
2 这可能有帮助,也可能没有帮助。
CREATE INDEX IX_temp_k ON temp_k( id )
3 组合查询
select
k.id, @icount + @n as k,
exp(p.premu + (sum(power(log(k.price) - p.premu, 2)) / (2 * (@n - sum(case when k.price is null then 1 else 0 end))))) as price
into #temp_f
from #temp_k as k
join (
select id, sum(log(price)) / (@n - sum(case when price is null then 1 else 0 end)) as premu
from #temp_k
where k between @icount and @icount + @n
group by id ) as p
on p.id= k.id
join #temp_bbfull as bb on bb.id= k.id and bb.k = k.k
where k.k between @icount and @icount + @n
group by k.id, p.premu;
等等。
推荐阅读
- python - 如何从交互式图表中提取数据
- delphi - Delphi 中的最小和最大堆栈大小
- bash - 将 sftp 中的命令输出存储到变量和列表中
- angular - 无法通过 PopoverController 打开共享模块的组件
- c# - 如何绑定到 app.xaml 中另一个类的(静态?)属性
- java - 如果在 java 中使用 MAP,如何发送 JSON @RequestBody?
- azure - 从 Azure DevOps 获取组织/实例名称列表
- android - 如何为具有各种片段的 Kotlin Android 应用程序创建带有协程的单例计时器?
- shopify - 如何在 shopify 中创建自定义模块(CRUD)?喜欢,产品和导航
- sql - 多行的 SQL 查询