如何将序列号连接到不相关的数据(SQL Server)

时间:2011-03-24 20:53:30

标签: sql-server sql-server-2005 tsql sequential-number

这个问题是我之前关于发现未使用的序列号范围的问题的后续问题,而不必使用游标(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分钟),我一直在寻找替代方案。

注意:感谢所有发布答案的人。所有这些都很棒,我不得不选择一个看起来更容易实现并且最容易维护的人。非常感谢。

5 个答案:

答案 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)

您需要将两个表连接在一起。您可以在两个表之间匹配的一些数据值。

我假设您的数字表不仅仅是一列数字。如果您可以在其中找到与数据表匹配的内容,则可以获得更新。

如何使用游标更新数据表?