提问人:Luiz 提问时间:11/3/2023 最后编辑:Dale KLuiz 更新时间:11/3/2023 访问量:48
合并两个具有精确数据分布的表
Merge Two Tables with Exact Data Distribution
问:
我正在使用 SQL Server。
我需要根据“Ctrl”列连接两个表。第一个表“Cad_Clients”包含客户端信息,而第二个表“Cad_Colors”包含信息说明。连接它们的条件是两个表中的“Ctrl”值必须相同。但是,分布需要精确,这意味着不会发生重复。
想象一下,我在“Cad_Colors”表中创建了一个“for”循环。在每次迭代中,只要“Ctrl”值匹配,我就会将表中的记录添加到“Cad_Clients”。之后,我从“Cad_Colors”中删除了该记录。如果循环表变为空或循环到达其末尾,则意味着我已经分发了尽可能多的记录。仅此而已,但我不想在数据库中使用循环;我相信,通过在这里讨论它,我们可以获得更简洁的结果。
问题的视觉描述:
Cad_Clients
| idPerson | Person | Ctrl |
|----------|----------------|-------|
| 1 | John | 100 |
| 2 | Sarah | 101 |
| 3 | Michael | 102 |
| 4 | Emily | 103 |
| 5 | William | 104 |
| 6 | Olivia | 105 |
| 7 | James | 100 |
| 8 | Emma | 101 |
| 9 | Benjamin | 102 |
| 10 | Sophia | 103 |
| 11 | Samuel | 104 |
| 12 | Ava | 105 |
| 13 | Joseph | 100 |
Cad_Colors
| Ctrl | Color | idColor |
|------|----------|---------|
| 100 | Red | 1 |
| 104 | Blue | 2 |
| 105 | Green | 3 |
| 103 | Yellow | 4 |
| 104 | Purple | 5 |
| 105 | Red | 6 |
合并两个表:
| idPerson | Person | Ctrl | Color |
|----------|---------|------|--------|
| 1 | John | 100 | Red |
| 4 | Emily | 103 | Yellow |
| 5 | William | 104 | Blue |
| 6 | Olivia | 105 | Green |
| 11 | Samuel | 104 | Purple |
| 12 | Ava | 105 | Red |
一些不成功的尝试:
INSERT INTO @Tmp
SELECT ccl.idPerson, ccl.Person, ccl.Ctrl,
(SELECT TOP 1 cco.idColor FROM Cad_Colors cco WHERE cco.Ctrl = ccl.Ctrl AND cco.idColor NOT IN(SELECT idColor FROM @Tmp))
FROM Cad_Clients ccl
INSERT INTO @Tmp
SELECT ccl.idPerson, ccl.Person, ccl.Ctrl, cco.idColor, cco.Color
FROM Cad_Clients ccl
JOIN (SELECT TOP 1 cco.* FROM Cad_Colors cco WHERE cco.idColor NOT IN(SELECT idColor FROM @Tmp)) cco ON cco.Ctrl = ccl.Ctrl
INSERT INTO @Tmp
SELECT ccl.idPerson, ccl.Person, ccl.Ctrl, cco.idColor, cco.Color
FROM Cad_Clients ccl
JOIN Cad_Colors cco ON cco.Ctrl = ccl.Ctrl
WHERE NOT EXISTS (SELECT 1 FROM @Tmp WHERE Ctrl = cco.Ctrl)
MERGE INTO Cad_Clients AS target
USING Cad_Colors AS source
ON target.Ctrl = source.Ctrl
WHEN MATCHED THEN
UPDATE SET target.idPerson = target.idPerson
OUTPUT inserted.idPerson, inserted.Person, inserted.Ctrl, inserted.idColor, inserted.Color INTO @tmp;
协助创建方案:
CREATE TABLE Cad_Clients( [idPerson] [int] IDENTITY(1,1) NOT NULL, [Person] [varchar](60) NULL, [Ctrl] [int] NULL );
CREATE TABLE Cad_Colors( [Ctrl] [int] NULL, [Color] [varchar](60) NULL, [idColor] [int] IDENTITY(1,1) NOT NULL );
INSERT INTO Cad_Clients (Person, Ctrl) VALUES ('John',100), ('Sarah',101), ('Michael',102), ('Emily',103), ('William',104), ('Olivia',105), ('James',100), ('Emma',101), ('Benjamin',102), ('Sophia',103), ('Samuel',104), ('Ava',105), ('Joseph',100);
INSERT INTO Cad_Colors (Ctrl, Color) VALUES (100, 'Red'), (104, 'Blue'), (105, 'Green'), (103, 'Yellow'), (104, 'Purple'), (105, 'Red');
DECLARE @Tmp TABLE( idPerson INT, Person VARCHAR(60), Ctrl INT, idColor INT /*Color VARCHAR(60)*/ );
答:
2赞
siggemannen
11/3/2023
#1
这应该是一个简单的ROW_NUMBER联接:
WITH persons AS (
SELECT *
FROM (
VALUES (1, N'John', 100)
, (2, N'Sarah', 101)
, (3, N'Michael', 102)
, (4, N'Emily', 103)
, (5, N'William', 104)
, (6, N'Olivia', 105)
, (7, N'James', 100)
, (8, N'Emma', 101)
, (9, N'Benjamin', 102)
, (10, N'Sophia', 103)
, (11, N'Samuel', 104)
, (12, N'Ava', 105)
, (13, N'Joseph', 100)
) t (idPerson,Person,Ctrl)
)
, colors AS (
SELECT *
FROM
(
VALUES (100, N'Red', 1)
, (104, N'Blue', 2)
, (105, N'Green', 3)
, (103, N'Yellow', 4)
, (104, N'Purple', 5)
, (105, N'Red', 6)
) t (Ctrl,Color,idColor)
)
SELECT p.idPerson, p.Person, p.Ctrl, c.Color
FROM (
SELECT *
, ROW_NUMBER() OVER(partition BY ctrl ORDER BY idperson) AS sort
FROM persons c
) p
INNER JOIN (
SELECT *
, ROW_NUMBER() OVER(partition BY ctrl ORDER BY idcolor) AS sort
FROM colors
) c
ON c.Ctrl = p.Ctrl
AND c.sort = p.sort
您可以为按 Ctrl 喜欢分组的每个人创建一个计数器,并对颜色执行相同的操作,然后将它们连接在一起。ROW_NUMBER() OVER(partition BY ctrl ORDER BY idperson)
输出:
idPerson的 | 人 | Ctrl 组合键 | 颜色 |
---|---|---|---|
1 | John | 100 | 红 |
4 | 艾米丽 | 103 | 黄色 |
5 | 威廉 | 104 | 蓝 |
11 | 塞缪尔 | 104 | 紫色 |
6 | 奥利维亚 | 105 | 绿 |
12 | 艾娃 | 105 | 红 |
评论
0赞
Luiz
11/3/2023
哇!我不知道这个Row_Number功能。在连接两个表的条件中创建唯一的关联 ID 真的很酷。感谢您的帮助,说真的,我刚刚开始使用 SQL Server,我有点迷茫。
0赞
siggemannen
11/3/2023
是的,不久前添加时,它改变了游戏规则!很高兴它对你有用。顺便说一句,如果答案有帮助,您可以通过单击某个按钮来“批准它”,因此它变为绿色
-1赞
Saikat
11/3/2023
#2
这是解决方案。您可以使用row_number窗口函数来实现这一点。
select
a.idPerson ,
a.Person ,
a.Ctrl ,
b.Color
from
(select * , row_number () over(partition by Ctrl order by idPerson) as cad_person_seq from Cad_Clients)
as a inner join
(select * , row_number () over(partition by Ctrl order by idColor) as cad_color_seq from Cad_Colors) as b
on a.Ctrl = b.Ctrl and a.cad_person_seq = b.cad_color_seq
order by a.cad_person_seq , a.Ctrl;
评论