SQL 服务器:用于查找计数器列中的间隙的脚本或存储过程
SQL Server : script or stored procedure to find gaps in a counter column
我们有一个 SQL 服务器 table amPOrder
,其中有一列 PONumber VARCHAR(30)
,其中包含 COM######
形式的采购订单编号(即前缀 COM
后跟 8 个字符长度的前导零填充整数。PO 编号是连续的但不一定是唯一的(可以有一个有效的 PO 和草稿共享相同的 PONumber
)。
PONumber
---
COM142069
COM142082
COM142082
COM142083
COM142088
COM142090
COM142090
COM142090
COM142110
COM142111
COM142113
COM142113
COM142115
COM142116
COM142307
[...]
COM820111
COM820112
COM820113
COM820114
COM820116
COM820121
COM820122
由于最近在软件中发现了一个错误,PONumber
被错误地创建并造成了以下影响:
PONumber
之间的许多差距导致大约 95% 的可能价值被“浪费”
- 我们正在接近允许的最大值
COM999999
最大可能值
我们想要识别“自由”PONumber
值,即从 100000 到 900000 的整数值,在 amPOrder 中没有匹配的 PONumber
值,并将它们插入新的 table(我们将能够从中“消耗”间隙)。
CREATE TABLE fix_amPOrder_PONumber
(
lValue INT PRIMARY KEY,
luserspid INT
)
如果没有匹配的 PONumber
,有人可以帮助在两个整数之间插入当前值的“循环”吗?
已解决
根据@john-joseph 的回答,我们做了以下工作:
- 创建一个
fillGap
存储过程,它采用起始值和要插入的行数:
CREATE PROCEDURE fillGap
@lValue INT,
@times INT
AS
BEGIN
WHILE @times > 0 BEGIN
INSERT INTO fix_amPOrder_PONumber
(lValue)
VALUES
(@lValue)
SET @lValue = @lValue + 1
SET @times = @times - 1
END
END
GO
- 使用游标遍历已识别的间隙并为每个间隙执行
fillGap
。
DECLARE @lValue INT, @times INT
DECLARE cursorElement CURSOR FOR
SELECT try_cast(replace([PreviousPONumber],'COM','') AS INT) + 1 AS freeValue, Gap - 1 AS times
FROM (
SELECT *, try_cast(replace([PONumber],'COM','') AS INT) - try_cast(replace([PreviousPONumber],'COM','') AS INT) AS Gap
FROM (
SELECT [PONumber], lag([PONumber], 1) OVER (ORDER BY [PONumber]) AS PreviousPONumber
FROM [amPOrder]) src1
) src2
WHERE Gap > 1
OPEN cursorElement
FETCH NEXT FROM cursorElement INTO @lValue, @times
WHILE ( @@FETCH_STATUS = 0 )
BEGIN
EXEC fillGap @lValue, @times
FETCH NEXT FROM cursorElement INTO @lValue, @times
END
CLOSE cursorElement
DEALLOCATE cursorElement
GO
这种方法可能对某些人有用,特别是因为识别差距和处理差距明显不同。
已解决 2
感谢@larnu 提出最快的解决方案:
--DELETE FROM fix_amPOrder_PONumber
WITH
N AS (SELECT N FROM (VALUES (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL)) AS N(N)),
Tally AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I FROM N N1, N N2, N N3, N N4, N N5, N N6),
ALLCOMs AS (SELECT CONCAT('COM', RIGHT(CONCAT('000000', I), 6)) AS COM FROM Tally),
COMs AS (
SELECT COM FROM ALLCOMs
WHERE COM > (
SELECT TOP(1) PONumber FROM amPOrder WHERE PONumber IS NOT NULL ORDER BY PONumber ASC
) AND COM < (
SELECT TOP(1) PONumber FROM amPOrder WHERE PONumber IS NOT NULL ORDER BY PONumber DESC
)
)
INSERT INTO fix_amPOrder_PONumber
(lValue)
SELECT try_cast(replace(C.COM,'COM','') AS INT)
FROM COMs AS C
LEFT JOIN amPOrder AS PO ON C.COM = PO.PONumber
WHERE PO.PONumber IS NULL
ORDER BY C.COM
GO
感谢大家的大力帮助。
查找缺失值最快的方法是使用 Tally 生成所有值,然后 LEFT JOIN
:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3, N N4, N N5, N N6),
COMs AS(
SELECT CONCAT('COM',RIGHT(CONCAT('000000',I),6)) AS COM
FROM Tally)
SELECT C.COM
FROM COMs C
LEFT JOIN dbo.YourTable YT ON C.COM = YT.PONumber
WHERE YT.PONumber IS NULL;
然后您可以轻松地将其操作为 INSERT
值,或者执行某种 UPDATE
.
您可以使用 SQL LAG() 函数在按 PO 编号对记录集进行排序后访问以前的 PO 编号。将其与一些算术相结合,找出你的差距...
select *
from
(
select
*
,try_cast(replace([PONumber],'COM','') as int) - try_cast(replace([PreviousPONumber],'COM','') as int) as Gap
from
(
select
[PONumber]
,lag([PONumber],1) over (order by [PONumber]) as PreviousPONumber
from [amPOrder]
) src1
) src2
where Gap > 1
这给你...
我们有一个 SQL 服务器 table amPOrder
,其中有一列 PONumber VARCHAR(30)
,其中包含 COM######
形式的采购订单编号(即前缀 COM
后跟 8 个字符长度的前导零填充整数。PO 编号是连续的但不一定是唯一的(可以有一个有效的 PO 和草稿共享相同的 PONumber
)。
PONumber
---
COM142069
COM142082
COM142082
COM142083
COM142088
COM142090
COM142090
COM142090
COM142110
COM142111
COM142113
COM142113
COM142115
COM142116
COM142307
[...]
COM820111
COM820112
COM820113
COM820114
COM820116
COM820121
COM820122
由于最近在软件中发现了一个错误,PONumber
被错误地创建并造成了以下影响:
PONumber
之间的许多差距导致大约 95% 的可能价值被“浪费”- 我们正在接近允许的最大值
COM999999
最大可能值
我们想要识别“自由”PONumber
值,即从 100000 到 900000 的整数值,在 amPOrder 中没有匹配的 PONumber
值,并将它们插入新的 table(我们将能够从中“消耗”间隙)。
CREATE TABLE fix_amPOrder_PONumber
(
lValue INT PRIMARY KEY,
luserspid INT
)
如果没有匹配的 PONumber
,有人可以帮助在两个整数之间插入当前值的“循环”吗?
已解决
根据@john-joseph 的回答,我们做了以下工作:
- 创建一个
fillGap
存储过程,它采用起始值和要插入的行数:
CREATE PROCEDURE fillGap
@lValue INT,
@times INT
AS
BEGIN
WHILE @times > 0 BEGIN
INSERT INTO fix_amPOrder_PONumber
(lValue)
VALUES
(@lValue)
SET @lValue = @lValue + 1
SET @times = @times - 1
END
END
GO
- 使用游标遍历已识别的间隙并为每个间隙执行
fillGap
。
DECLARE @lValue INT, @times INT
DECLARE cursorElement CURSOR FOR
SELECT try_cast(replace([PreviousPONumber],'COM','') AS INT) + 1 AS freeValue, Gap - 1 AS times
FROM (
SELECT *, try_cast(replace([PONumber],'COM','') AS INT) - try_cast(replace([PreviousPONumber],'COM','') AS INT) AS Gap
FROM (
SELECT [PONumber], lag([PONumber], 1) OVER (ORDER BY [PONumber]) AS PreviousPONumber
FROM [amPOrder]) src1
) src2
WHERE Gap > 1
OPEN cursorElement
FETCH NEXT FROM cursorElement INTO @lValue, @times
WHILE ( @@FETCH_STATUS = 0 )
BEGIN
EXEC fillGap @lValue, @times
FETCH NEXT FROM cursorElement INTO @lValue, @times
END
CLOSE cursorElement
DEALLOCATE cursorElement
GO
这种方法可能对某些人有用,特别是因为识别差距和处理差距明显不同。
已解决 2
感谢@larnu 提出最快的解决方案:
--DELETE FROM fix_amPOrder_PONumber
WITH
N AS (SELECT N FROM (VALUES (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL), (NULL)) AS N(N)),
Tally AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I FROM N N1, N N2, N N3, N N4, N N5, N N6),
ALLCOMs AS (SELECT CONCAT('COM', RIGHT(CONCAT('000000', I), 6)) AS COM FROM Tally),
COMs AS (
SELECT COM FROM ALLCOMs
WHERE COM > (
SELECT TOP(1) PONumber FROM amPOrder WHERE PONumber IS NOT NULL ORDER BY PONumber ASC
) AND COM < (
SELECT TOP(1) PONumber FROM amPOrder WHERE PONumber IS NOT NULL ORDER BY PONumber DESC
)
)
INSERT INTO fix_amPOrder_PONumber
(lValue)
SELECT try_cast(replace(C.COM,'COM','') AS INT)
FROM COMs AS C
LEFT JOIN amPOrder AS PO ON C.COM = PO.PONumber
WHERE PO.PONumber IS NULL
ORDER BY C.COM
GO
感谢大家的大力帮助。
查找缺失值最快的方法是使用 Tally 生成所有值,然后 LEFT JOIN
:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3, N N4, N N5, N N6),
COMs AS(
SELECT CONCAT('COM',RIGHT(CONCAT('000000',I),6)) AS COM
FROM Tally)
SELECT C.COM
FROM COMs C
LEFT JOIN dbo.YourTable YT ON C.COM = YT.PONumber
WHERE YT.PONumber IS NULL;
然后您可以轻松地将其操作为 INSERT
值,或者执行某种 UPDATE
.
您可以使用 SQL LAG() 函数在按 PO 编号对记录集进行排序后访问以前的 PO 编号。将其与一些算术相结合,找出你的差距...
select *
from
(
select
*
,try_cast(replace([PONumber],'COM','') as int) - try_cast(replace([PreviousPONumber],'COM','') as int) as Gap
from
(
select
[PONumber]
,lag([PONumber],1) over (order by [PONumber]) as PreviousPONumber
from [amPOrder]
) src1
) src2
where Gap > 1
这给你...