使用 window 函数将存储函数转换为 Select
Convert Stored Function to Select with window-functions
我有一个计算价格的函数:
ALTER FUNCTION [dbo].[LAWI_DEinkauf](@sArtikelID varchar(36))
RETURNS NUMERIC(14, 2)
AS
BEGIN
DECLARE @menge DECIMAL(16,6)
DECLARE @tmenge DECIMAL(16,6)
DECLARE @wert DECIMAL(16,6)
DECLARE @ekpreis DECIMAL(16,6)
DECLARE @ekmenge DECIMAL(16,6)
DECLARE @myposI CURSOR
SET @ekpreis = 0
SET @ekmenge = 0
SET @wert = 0
SET @menge = 0
SET @tmenge = 0
SET @myposI = CURSOR SCROLL FOR
SELECT einkaufspreis, menge
FROM lawi_bewegung
WHERE artikelid = @sArtikelID
ORDER BY datum, ident;
OPEN @myposI
FETCH NEXT FROM @myposI INTO @ekpreis, @ekmenge
WHILE @@fetch_status = 0
BEGIN
SET @tmenge = @tmenge + @ekmenge
IF @ekpreis <> 0
SET @menge = @menge + @ekmenge
SET @wert = @wert + @ekpreis * @ekmenge
IF @tmenge = 0
SET @wert = 0
IF @tmenge = 0
SET @menge = 0
FETCH NEXT FROM @myposI INTO @ekpreis, @ekmenge;
END
CLOSE @myposI
DEALLOCATE @myposI
IF @menge = 0
SET @menge = 1
RETURN @wert / @menge
END
我尝试将此存储函数转换为具有 window 函数的 select。原因是存储函数在具有多行的 select 中使用时性能不佳。
通常情况如下:
(SELECT TOP 1
wert / CASE WHEN menge = 0 THEN 1 ELSE menge END
FROM
(SELECT
SUM(menge) OVER (ORDER BY datum, ident) as tmenge,
SUM(CASE WHEN einkaufspreis <> 0 THEN menge ELSE 0 END) OVER (ORDER BY datum, ident) AS menge,
SUM(einkaufspreis * menge) OVER (ORDER BY datum, ident) AS wert,
ROW_NUMBER() OVER (ORDER BY datum, ident) AS rn
FROM
lawi_bewegung
WHERE
artikelid = XXXX) a order by rn desc
在那里,XXXX 将是参数 sArtikelID
(在我的例子中来自外部 select)。
我的问题是存储函数的一部分,其中总和被重置:
IF @tmenge = 0
SET @wert = 0
IF @tmenge = 0
SET @menge = 0
如何将此逻辑包含到使用 window 函数的 select 中?
以下查询似乎可以解决您的问题,但我不确定它是否会正常运行。你能用一些数据试试看并检查值和性能吗?
WITH step1 AS (
SELECT
*,
CASE WHEN SUM(menge) OVER (PARTITION BY artikelid ORDER BY datum, ident) = 0 THEN 1 ELSE 0 END AS reset
FROM lawi_bewegung
),
step2 AS (
SELECT *,
SUM(reset) OVER (PARTITION BY artikelid ORDER BY datum, ident) - reset AS g
FROM step1
),
step3 AS (
SELECT artikelid,
SUM(CASE WHEN einkaufspreis <> 0 THEN menge ELSE 0 END)
OVER (PARTITION BY artikelid, g ORDER BY datum, ident) AS menge,
SUM(einkaufspreis * menge)
OVER (PARTITION BY artikelid, g ORDER BY datum, ident) AS wert,
ROW_NUMBER() OVER (PARTITION BY artikelid ORDER BY datum, ident) AS rn
FROM step2
)
SELECT i.artikelid,
d.*
FROM lawi_inventur i
CROSS APPLY (
SELECT TOP 1
CAST(wert / CASE WHEN menge = 0 THEN 1 ELSE menge END AS DECIMAL(14, 2)) AS DEinkauf
FROM step3 s
WHERE s.artikelid = i.artikelid
ORDER BY rn DESC
) d
;
其工作方式如下:
- 第 1 步将标记 tmenge 为 0 的行,这是您的重置点;
- 第 2 步将对
reset
列进行 运行 求和,有效地创建一个分组列。每次tmenge总和为0,后面的行就会有不同的分组值;
- 第 3 步利用此分组值,并使用 OVER 子句的 PARTITION BY 选项使用它对要求和的行进行分区。它还计算所有行的 ROW_NUMBER() ,然后外部查询使用它来选择最后一行;
如果您查看中间步骤的输出,可能会更容易理解。只需将最后的 SELECT(最后 4 行)更改为 SELECT * from step1
,或 SELECT * FROM step2
等等。
如果您有一个适合此查询的索引,可能会有所帮助,类似于 ON (artikelid, datum, ident) INCLUDE (menge, einkaufspreis)
。
我有一个计算价格的函数:
ALTER FUNCTION [dbo].[LAWI_DEinkauf](@sArtikelID varchar(36))
RETURNS NUMERIC(14, 2)
AS
BEGIN
DECLARE @menge DECIMAL(16,6)
DECLARE @tmenge DECIMAL(16,6)
DECLARE @wert DECIMAL(16,6)
DECLARE @ekpreis DECIMAL(16,6)
DECLARE @ekmenge DECIMAL(16,6)
DECLARE @myposI CURSOR
SET @ekpreis = 0
SET @ekmenge = 0
SET @wert = 0
SET @menge = 0
SET @tmenge = 0
SET @myposI = CURSOR SCROLL FOR
SELECT einkaufspreis, menge
FROM lawi_bewegung
WHERE artikelid = @sArtikelID
ORDER BY datum, ident;
OPEN @myposI
FETCH NEXT FROM @myposI INTO @ekpreis, @ekmenge
WHILE @@fetch_status = 0
BEGIN
SET @tmenge = @tmenge + @ekmenge
IF @ekpreis <> 0
SET @menge = @menge + @ekmenge
SET @wert = @wert + @ekpreis * @ekmenge
IF @tmenge = 0
SET @wert = 0
IF @tmenge = 0
SET @menge = 0
FETCH NEXT FROM @myposI INTO @ekpreis, @ekmenge;
END
CLOSE @myposI
DEALLOCATE @myposI
IF @menge = 0
SET @menge = 1
RETURN @wert / @menge
END
我尝试将此存储函数转换为具有 window 函数的 select。原因是存储函数在具有多行的 select 中使用时性能不佳。
通常情况如下:
(SELECT TOP 1
wert / CASE WHEN menge = 0 THEN 1 ELSE menge END
FROM
(SELECT
SUM(menge) OVER (ORDER BY datum, ident) as tmenge,
SUM(CASE WHEN einkaufspreis <> 0 THEN menge ELSE 0 END) OVER (ORDER BY datum, ident) AS menge,
SUM(einkaufspreis * menge) OVER (ORDER BY datum, ident) AS wert,
ROW_NUMBER() OVER (ORDER BY datum, ident) AS rn
FROM
lawi_bewegung
WHERE
artikelid = XXXX) a order by rn desc
在那里,XXXX 将是参数 sArtikelID
(在我的例子中来自外部 select)。
我的问题是存储函数的一部分,其中总和被重置:
IF @tmenge = 0
SET @wert = 0
IF @tmenge = 0
SET @menge = 0
如何将此逻辑包含到使用 window 函数的 select 中?
以下查询似乎可以解决您的问题,但我不确定它是否会正常运行。你能用一些数据试试看并检查值和性能吗?
WITH step1 AS (
SELECT
*,
CASE WHEN SUM(menge) OVER (PARTITION BY artikelid ORDER BY datum, ident) = 0 THEN 1 ELSE 0 END AS reset
FROM lawi_bewegung
),
step2 AS (
SELECT *,
SUM(reset) OVER (PARTITION BY artikelid ORDER BY datum, ident) - reset AS g
FROM step1
),
step3 AS (
SELECT artikelid,
SUM(CASE WHEN einkaufspreis <> 0 THEN menge ELSE 0 END)
OVER (PARTITION BY artikelid, g ORDER BY datum, ident) AS menge,
SUM(einkaufspreis * menge)
OVER (PARTITION BY artikelid, g ORDER BY datum, ident) AS wert,
ROW_NUMBER() OVER (PARTITION BY artikelid ORDER BY datum, ident) AS rn
FROM step2
)
SELECT i.artikelid,
d.*
FROM lawi_inventur i
CROSS APPLY (
SELECT TOP 1
CAST(wert / CASE WHEN menge = 0 THEN 1 ELSE menge END AS DECIMAL(14, 2)) AS DEinkauf
FROM step3 s
WHERE s.artikelid = i.artikelid
ORDER BY rn DESC
) d
;
其工作方式如下:
- 第 1 步将标记 tmenge 为 0 的行,这是您的重置点;
- 第 2 步将对
reset
列进行 运行 求和,有效地创建一个分组列。每次tmenge总和为0,后面的行就会有不同的分组值; - 第 3 步利用此分组值,并使用 OVER 子句的 PARTITION BY 选项使用它对要求和的行进行分区。它还计算所有行的 ROW_NUMBER() ,然后外部查询使用它来选择最后一行;
如果您查看中间步骤的输出,可能会更容易理解。只需将最后的 SELECT(最后 4 行)更改为 SELECT * from step1
,或 SELECT * FROM step2
等等。
如果您有一个适合此查询的索引,可能会有所帮助,类似于 ON (artikelid, datum, ident) INCLUDE (menge, einkaufspreis)
。