Sql server 如何获取唯一的匹配记录(参见示例)
这个例子只是为了说明这个问题,与我正在处理的实际数据完全不同,但是使用任何类似于实际数据的东西都会非常复杂 假设我有这两套:Sql server 如何获取唯一的匹配记录(参见示例),sql-server,Sql Server,这个例子只是为了说明这个问题,与我正在处理的实际数据完全不同,但是使用任何类似于实际数据的东西都会非常复杂 假设我有这两套: id name license ----------- ---- ----------- 1 Joe 1 2 Eric 1 3 Jane 2 4 Mike 2 id name license ----------- -------- ------
id name license
----------- ---- -----------
1 Joe 1
2 Eric 1
3 Jane 2
4 Mike 2
id name license
----------- -------- -----------
11 Van #1 1
12 Van #2 1
13 Truck #1 2
14 Truck #2 2
我想为每辆车找到一个有资格驾驶它的司机。而且,由于示例许可证对于每种类型的车辆都是唯一的,驾驶卡车之类的任何事情都不能使驾驶员具备驾驶面包车的资格。因此,预期结果如下所示:
driver_id driver_name driver_license vehicle_id vehicle_name vehicle_license
----------- ----------- -------------- ----------- ------------ ---------------
1 Joe 1 11 Van #1 1
2 Eric 1 12 Van #2 1
3 Jane 2 13 Truck #1 2
4 Mike 2 14 Truck #2 2
我已经能够通过下面的查询得到这个结果,但是如果查询集较大,结果可能会变慢。有没有其他更好的方法获得同样的结果
select d.id driver_id
,d.name driver_name
,d.license driver_license
,v.id vehicle_id
,v.name vehicle_name
,v.license vehicle_license
from (select id
,name
,license
,rank() over (partition by license order by id) rank_driver
from ( values ( 1, 'Joe', 1),
( 2, 'Eric', 1),
( 3, 'Jane', 2),
( 4, 'Mike', 2) ) driver (id, name, license)) d
left join (select id
,name
,license
,rank() over (partition by license order by id) rank_vehicle
from ( values ( 11, 'Van #1', 1) ,
( 12, 'Van #2', 1),
( 13, 'Truck #1', 2),
( 14, 'Truck #2', 2) ) vehicle (id, name, license)) v
on d.license = v.license and d.rank_driver = v.rank_vehicle
当您问这个问题时,如果您为表添加DDL脚本并为示例数据添加脚本,那将非常好。如果在性能方面存在问题,则需要添加适当的索引
CREATE NONCLUSTERED INDEX ix_drivers ON drivers (name) INCLUDE (license);
CREATE NONCLUSTERED INDEX ix_vehicles ON vehicles (name) INCLUDE (license);
CREATE TABLE #drivers
(
id INT, name VARCHAR(100), license int
);
CREATE TABLE #vehicles
(
id INT, name VARCHAR(100), license int
);
INSERT INTO #drivers
( id, name, license )
VALUES
(1, 'Joe', 1),
(2, 'Eric', 1),
(3, 'Jane', 2),
(4, 'Mike', 2);
INSERT INTO #vehicles
( id, name, license )
VALUES
(11, 'Van #1', 1),
(12, 'Van #2', 1),
(13, 'Truck #1', 2),
(14, 'Truck #2', 2)
SELECT a.id, a.name, a.license, b.id, b.name, b.license
FROM
(
SELECT id, name, license, ROW_NUMBER() OVER (PARTITION BY license ORDER BY name) AS rownum
FROM #drivers
) a
JOIN
(
SELECT id, name, license, ROW_NUMBER() OVER (PARTITION BY license ORDER BY name) AS rownum
FROM #vehicles
) b
ON a.license = b.license
AND a.rownum = b.rownum
ORDER BY 1
你的解决方案很好。我会这样写:
select d.id driver_id, d.name driver_name, d.license driver_license,
v.id vehicle_id, v.name vehicle_name, v.license vehicle_license
from (select d.*,
row_number() over (partition by license order by id) as rank_driver
from drivers d
) d left join
(select v.*,
row_number() over (partition by license order by id) as rank_vehicle
from vehicles v
) v
on d.license = v.license and d.rank_driver = v.rank_vehicle
如果您关心可伸缩性,我建议在表中使用索引:driverslicense,id和vehicleslicense,id
虽然在外部查询中避免使用*是一种很好的做法,但对于子查询来说,这是一种过分的做法——除非您生成的是一个准备好的语句或视图,其编译形式可能会持续很长时间。数据库本身将优化查询,以仅选择所需的列。在实现子查询的MySQL中不是这样,但那是另一回事。感谢DDL语句,我倾向于在这些练习中使用from值。不过,您发布的解决方案完全相同,只是将排名更改为行号。首先,排名与行号不同。不过,我也添加了索引。我知道排名和行号不一样,但就本例而言,它们是可交换的,或者你是想说在这种情况下使用行号更好?是的,如果你有相同的驱动程序名,那么你会遇到麻烦。车辆名称也是如此。仅当名称列将包含在功能的分区/顺序部分时,它们不是。感谢您的帮助和兴趣,我同意使用select*,但我从未在SO问题中使用select*,因为有几十人抱怨它,而不是看问题;