根据引用 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
上随机分配 MatchId
和 JOIN
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
这是一个简化版本,精简到我的核心问题。我有一个包含数百万行数据的 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
MatchId
和 JOIN
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