这个问题是我之前关于发现未使用的序列号范围的问题的后续问题,而不必使用游标(Working with sequential numbers in SQL Server 2005 without cursors)。我正在使用SQL Server 2005。
我需要对这些数字做的是将这些数字分配给表格中的记录。我似乎无法想出一种方法来实际将数字表与需要这些数字的记录联系起来。
我想到的一个可能的解决方案是使用标识将记录插入临时表中,并使用数字范围的开头作为标识种子。这种方法的唯一问题是,如果数字序列中存在间隙,那么我将最终得到重复的控制数。
这就是我的表格的样子(过度简化):
数字表:
Number
-------
102314
102315
102319
102320
102324
102329
数据表:
CustomerId PaymentAmt ControlNumber
---------- ---------- -------------
1001 4502.01 NULL
1002 890.00 NULL
9830 902923.34 NULL
我需要一种方法来实现它,所以我最终得到了:
CustomerId PaymentAmt ControlNumber
---------- ---------- -------------
1001 4502.01 102314
1002 890.00 102315
9830 902923.34 102319
这是否可以在不使用游标的情况下实现?我之所以避免使用游标是因为我们当前的实现使用了游标,因为它很慢(超过12,000条记录的8分钟),我一直在寻找替代方案。
注意:感谢所有发布答案的人。所有这些都很棒,我不得不选择一个看起来更容易实现并且最容易维护的人。非常感谢。
答案 0 :(得分:6)
试试这个:
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(ORDER BY CustomerId) Corr
FROM DataTable
)
UPDATE CTE
SET CTE.ControlNumber = B.Number
FROM CTE
JOIN ( SELECT Number, ROW_NUMBER() OVER(ORDER BY Number) Corr
FROM NumberTable) B
ON CTE.Corr = B.Corr
答案 1 :(得分:2)
根据链接问题对Martin的代码进行补充,您可以将没有控制编号的所有行赋予行号。然后给所有未使用的数字一个行号。将两组加在一起,每行得到一个唯一的数字:
DECLARE @StartRange int, @EndRange int
SET @StartRange = 790123401
SET @EndRange = 790123450;
; WITH YourTable(ControlNumber, CustomerId) AS
(
SELECT 790123401, 1000
UNION ALL SELECT 790123402, 1001
UNION ALL SELECT 790123403, 1002
UNION ALL SELECT 790123406, 1003
UNION ALL SELECT NULL, 1004
UNION ALL SELECT NULL, 1005
UNION ALL SELECT NULL, 1006
)
, YourTableNumbered(rn, ControlNumber, CustomerId) AS
(
select row_number() over (
partition by IsNull(ControlNumber, -1)
order by ControlNumber)
, *
from YourTable
)
, Nums(N) AS
(
SELECT @StartRange
UNION ALL
SELECT N+1
FROM Nums
WHERE N < @EndRange
)
, UnusedNums(rn, N) as
(
select row_number() over (order by Nums.N)
, Nums.N
from Nums
where not exists
(
select *
from YourTable yt
where yt.ControlNumber = Nums.N
)
)
select ytn.CustomerId
, IsNull(ytn.ControlNumber, un.N)
from YourTableNumbered ytn
left join
UnusedNums un
on un.rn = ytn.rn
OPTION (MAXRECURSION 0)
答案 2 :(得分:2)
您需要的只是数据表中的确定性顺序。如果您有,可以使用ROW_NUMBER()
作为连接条件:
with cte as (
select row_number() over (order by CustomerId) as [row_number],
ControlNumber
from [Data Table]
where ControlNumber is null),
nte as (
select row_number() over (order by Number) as [row_number],
Number
from [Numbers])
update cte
set ControlNumber = Number
from cte
join nte on nte.[row_number] = cte.[row_number];
如果你需要它作为证据证明,它确实变得更加复杂。
答案 3 :(得分:1)
已编辑,以通过UPDATE的OUTPUT caluse和DELETE
从@Number中删除used
值
尝试使用ROW_NUMBER()加入它们:
DECLARE @Number table (Value int)
INSERT @Number VALUES (102314)
INSERT @Number VALUES (102315)
INSERT @Number VALUES (102319)
INSERT @Number VALUES (102320)
INSERT @Number VALUES (102324)
INSERT @Number VALUES (102329)
DECLARE @Data table (CustomerId int, PaymentAmt numeric(10,2),ControlNumber int)
INSERT @Data VALUES (1001, 4502.01 ,NULL)
INSERT @Data VALUES (1002, 890.00 ,NULL)
INSERT @Data VALUES (9830, 902923.34 ,NULL)
DECLARE @Used table (Value int)
;WITH RowNumber AS
(
SELECT Value,ROW_NUMBER() OVER(ORDER BY Value) AS RowNumber FROM @Number
)
,RowData AS
(
SELECT CustomerId,ROW_NUMBER() OVER(ORDER BY CustomerId) AS RowNumber, ControlNumber FROM @Data WHERE ControlNumber IS NULL
)
UPDATE d
SET ControlNumber=r.Value
OUTPUT r.Value INTO @Used
FROM RowData d
INNER JOIN RowNumber r ON d.RowNumber=r.RowNumber
DELETE @Number WHERE Value IN (SELECT Value FROM @Used)
SELECT * FROM @Data
SELECT * FROM @Number
输出:
CustomerId PaymentAmt ControlNumber
----------- --------------------------------------- -------------
1001 4502.01 102314
1002 890.00 102315
9830 902923.34 102319
(3 row(s) affected)
Value
-----------
102320
102324
102329
(3 row(s) affected)
答案 4 :(得分:0)
您需要将两个表连接在一起。您可以在两个表之间匹配的一些数据值。
我假设您的数字表不仅仅是一列数字。如果您可以在其中找到与数据表匹配的内容,则可以获得更新。
如何使用游标更新数据表?