使用 SQL 服务器中另一个 table 的 ID 更新现有 table 的新外键列
Update new foreign key column of existing table with ids from another table in SQL Server
我有一个现有的 table,我在其中添加了一个新列,该列应该在另一个(新)table.
中保存记录的 ID
简化后的结构是这样的:
客户table
[CustomerId] [GroupId] [LicenceId] <-- new column
许可 table <-- 新 table
[LicenceId] [GroupId]
许可证 table 每个组的许可证数量超过了可以分配给同一组客户的许可证数量。有多个组,每个组都有可变数量的客户和许可证。
假设第 1 组有 100 个可用许可证,第 1 组有 50 个客户,所以每个人都可以获得一个许可证。客户永远不会超过许可证。
样本
Customer
[CustomerId] [GroupId] [LicenceId]
1 1 NULL
2 1 NULL
3 1 NULL
4 1 NULL
5 2 NULL
6 2 NULL
7 2 NULL
8 3 NULL
9 3 NULL
Licence
[LicenceId] [GroupId]
1 1
2 1
3 1
4 1
5 1
6 1
7 2
8 2
9 2
10 2
11 2
12 3
13 3
14 3
15 3
16 3
17 3
Desired outcome
Customer
[CustomerId] [GroupId] [LicenceId]
1 1 1
2 1 2
3 1 3
4 1 4
5 2 7
6 2 8
7 2 9
8 3 12
9 3 13
所以现在我必须进行一次更新才能为每个客户提供一个许可证,但我不知道该怎么做。
不允许我使用光标。我似乎无法进行 MERGE UPDATE,因为通过 GroupId 将客户加入许可证 table 会导致多次点击。
如何在一次查询中为每个客户分配其组内的下一个可用 LicenceId?
这可能吗?
您可以使用 window 函数:
with c as (
select c.*, row_number() over (partition by groupid order by newid()) as seqnum
from customers c
),
l as (
select l.*, row_number() over (partition by groupid order by newid()) as seqnum
from licenses c
)
update c
set c.licenceid = l.licenseid
from c join
l
on c.seqnum = l.seqnum and c.groupid = l.groupid;
这会随机分配许可证。那真的只是为了好玩。最有效的方法是使用:
row_number() over (partition by groupid order by (select null)) as seqnum
SQL 服务器通常会在这种情况下避免额外的排序操作。
但您可能希望通过其他方式对它们进行排序,例如通过客户 ID 的排序、日期列或其他方式。
戈登在他的回答中说得很好。
让我为您分解为更简单的步骤。
第 1 步。使用 ROW_NUMBER() 函数为客户分配一个 SeqNum。使用 PARTITION BY GroupId,以便每个组中的数字从 1 开始。我会按 CustomerId
订购
第 2 步。使用 ROW_NUMBER() 函数为许可证分配一个 SeqNum。使用 PARTITION BY GroupId,以便每个组中的数字从 1 开始。 ORDER BY LicenseId 因为您的要求是“为每个客户分配其组中的下一个可用 LicenceId”。
现在使用这 2 个查询更新客户 table 中的 LicenseId。
我有一个现有的 table,我在其中添加了一个新列,该列应该在另一个(新)table.
中保存记录的 ID简化后的结构是这样的:
客户table
[CustomerId] [GroupId] [LicenceId] <-- new column
许可 table <-- 新 table
[LicenceId] [GroupId]
许可证 table 每个组的许可证数量超过了可以分配给同一组客户的许可证数量。有多个组,每个组都有可变数量的客户和许可证。 假设第 1 组有 100 个可用许可证,第 1 组有 50 个客户,所以每个人都可以获得一个许可证。客户永远不会超过许可证。
样本
Customer
[CustomerId] [GroupId] [LicenceId]
1 1 NULL
2 1 NULL
3 1 NULL
4 1 NULL
5 2 NULL
6 2 NULL
7 2 NULL
8 3 NULL
9 3 NULL
Licence
[LicenceId] [GroupId]
1 1
2 1
3 1
4 1
5 1
6 1
7 2
8 2
9 2
10 2
11 2
12 3
13 3
14 3
15 3
16 3
17 3
Desired outcome
Customer
[CustomerId] [GroupId] [LicenceId]
1 1 1
2 1 2
3 1 3
4 1 4
5 2 7
6 2 8
7 2 9
8 3 12
9 3 13
所以现在我必须进行一次更新才能为每个客户提供一个许可证,但我不知道该怎么做。
不允许我使用光标。我似乎无法进行 MERGE UPDATE,因为通过 GroupId 将客户加入许可证 table 会导致多次点击。
如何在一次查询中为每个客户分配其组内的下一个可用 LicenceId? 这可能吗?
您可以使用 window 函数:
with c as (
select c.*, row_number() over (partition by groupid order by newid()) as seqnum
from customers c
),
l as (
select l.*, row_number() over (partition by groupid order by newid()) as seqnum
from licenses c
)
update c
set c.licenceid = l.licenseid
from c join
l
on c.seqnum = l.seqnum and c.groupid = l.groupid;
这会随机分配许可证。那真的只是为了好玩。最有效的方法是使用:
row_number() over (partition by groupid order by (select null)) as seqnum
SQL 服务器通常会在这种情况下避免额外的排序操作。
但您可能希望通过其他方式对它们进行排序,例如通过客户 ID 的排序、日期列或其他方式。
戈登在他的回答中说得很好。 让我为您分解为更简单的步骤。
第 1 步。使用 ROW_NUMBER() 函数为客户分配一个 SeqNum。使用 PARTITION BY GroupId,以便每个组中的数字从 1 开始。我会按 CustomerId
订购第 2 步。使用 ROW_NUMBER() 函数为许可证分配一个 SeqNum。使用 PARTITION BY GroupId,以便每个组中的数字从 1 开始。 ORDER BY LicenseId 因为您的要求是“为每个客户分配其组中的下一个可用 LicenceId”。
现在使用这 2 个查询更新客户 table 中的 LicenseId。