根据引用 table 中的计数将记录拆分到桶中

Split records into buckets based on counts in reference table

这是一个简化版本,精简到我的核心问题。我有一个包含数百万行数据的 ContactData table,每条联系人记录都使用 ReferenceID 分成几类。我现在必须根据单独的 NewValues table 以及 ReferenceID 的计数,为每个联系人记录分配一个新的 UpdatedValue。将哪些记录分配给每个组并不重要,它可以是随机的或其他方式,只要每个组的记录数是正确的。

用下面的例子来说明:如果#ContactData中有800条ReferenceID=1的记录,那么使用#NewValues中的RecordTotal计数,我想将200分配给Group1,350分配给Group2,250分配给Group3.

我可以使用嵌套循环和更新来做到这一点。但是 NewValues 中的数据会定期更改,由此产生的组到联系人的分配也会随之更改。此外,更新后的联系人数据将转储到单独的第三个 table 而不是更新原始联系人数据 table。所以我希望有任何更简单的方法可以在将数据选择到第三个 table 中时即时分配此值。下面是示例 tables 和数据来说明。任何帮助将不胜感激。

DROP TABLE IF EXISTS #ContactData 
CREATE TABLE #ContactData  ( 
    RowId INT IDENTITY(1,1) NOT NULL, 
    ReferenceID INT,
    FirstName VARCHAR(10)) 
GO
INSERT INTO #ContactData (ReferenceID,FirstName)
VALUES (1,'John'), (1,'Mary'), (1,'Dan'), (2,'Sue'), (2,'Harvey'), (3,'Frank'), (3,'Mike')
GO
DROP TABLE IF EXISTS #NewValues 
CREATE TABLE #NewValues  ( 
    RowId INT IDENTITY(1,1) NOT NULL, 
    ReferenceID INT, 
    RecordTotal DECIMAL(10,4),
    UpdatedValue NVARCHAR(20)
    ) 
GO
INSERT INTO #NewValues (ReferenceID,RecordTotal,UpdatedValue)
VALUES (1,200,'Group1'), (1,350,'Group2'), (1,250,'Group3'), (2,500,'Group4'), (2,300,'Group5'), (3,150,'Group6'), (3,850,'Group7')
GO

您可以使用 Row_Number()MatchID

上随机分配 MatchIdJOIN
WITH ContactDataCTE AS(
  SELECT *, ROW_NUMBER() OVER(PARTITION BY ReferenceID ORDER BY FirstName) AS MatchId
  FROM #ContactData
), NewValuesCTE AS (
  SELECT *, ROW_NUMBER() OVER(PARTITION BY ReferenceID ORDER BY RecordTotal) AS MatchId
  FROM #NewValues
)
SELECT CD.RowId
      ,CD.ReferenceID
      ,CD.FirstName
      ,NV.RecordTotal
      ,NV.UpdatedValue 
FROM ContactDataCTE CD
JOIN NewValuesCTE NV
  ON CD.ReferenceID = NV.ReferenceID
 AND CD.MatchId = NV.MatchId

避免对此类事情使用循环。窗口函数对于此类问题非常有用。

0 - 我在循环中使用您的代码创建了一个示例数据集:

declare @i int = 1
while(@i <200)
begin

    INSERT INTO #ContactData (ReferenceID,FirstName)
    VALUES (1,'John'), (1,'Mary'), (1,'Dan'), (2,'Sue'), (2,'Harvey'), (3,'Frank'), (3,'Mike')

    set @i = @i + 1
end

1 - 计算每个 ReferenceID 的最大和最小行值:

select
    f1.*
    ,   sum(RecordTotal) over(partition by ReferenceID order by RowId asc) - RecordTotal minValue
    ,   sum(RecordTotal) over(partition by ReferenceID order by RowId asc)  maxValue
from #NewValues f1

2 - 然后你需要计算每个 ReferenceID 按任意列排序:

select
    *
,   sum(1) over(partition by ReferenceID order by RowId asc) rn    
from #ContactData f1

3 - 通过使用 rn步骤 2 中计算,您可以将记录分配给动态存储桶。这是完整的代码:

select
    g1.*
,   g2.UpdatedValue as Bucket
from 
    (
    select
        *
    ,   sum(1) over(partition by ReferenceID order by RowId asc) rn    
    from #ContactData f1
    ) g1
inner join 
    (
    select
        f1.*
        ,   sum(RecordTotal) over(partition by ReferenceID order by RowId asc) - RecordTotal minValue
        ,   sum(RecordTotal) over(partition by ReferenceID order by RowId asc)  maxValue
    from #NewValues f1
    ) g2 on g1.ReferenceID = g2.ReferenceID and g1.rn >= g2.minValue and g1.rn < g2.maxValue