在 Join 表达式中捕获所有记录

Catch All Record in a Join expression

我有一个很简单的(我觉得很简单,但我还在苦苦挣扎!)的问题。我有 table 辆汽车。我还有 table 辆 class 辆汽车。我想将一个连接到另一个以获得汽车的 class。

汽车 table 很简单,有型号和品牌。 class table 也有一个型号和一个品牌和一个类型 class。当我想将相同品牌的汽车归为一组而不考虑其型号时,问题就出现了。

例如,我有两辆车:

id  vehiclemake vehiclemodel
1   AUDI            R8
2   AUDI            Quattro

我有两个 classes:

id  vehiclemake vehiclemodel    classtype
1   AUDI            R8          A
2   AUDI            NULL        B

AUDI R8 将匹配 class A 型。我希望所有其他 AUDI,无论其型号如何,都匹配 class B 型。

我这里有一些示例代码,你可以玩一下!

create table #vehicle(id int, vehiclemake varchar(10), vehiclemodel varchar(10))
create table #vehicleclass(id int, vehiclemake varchar(10), vehiclemodel varchar(10), classtype varchar(1))


insert into #vehicle values(1, 'AUDI', 'R8')
insert into #vehicle values(2, 'AUDI', 'Quattro')

insert into #vehicleclass values(1, 'AUDI', 'R8', 'A')
insert into #vehicleclass values(2, 'AUDI', null, 'B')


select 
*
from
#vehicle v
left join #vehicleclass vc on 
(v.vehiclemake = vc.vehiclemake and v.vehiclemodel = vc.vehiclemodel)


drop table #vehicle
drop table #vehicleclass

上面的语句没有将 Quattro 记录加入 B class 记录

vehiclemodel 的连接上使用 COALESCE 以便当模型在 vehicleclass table 上为 NULL 时,车辆将改为匹配自己的模型.

[编辑]:阅读用户评论后,试试这个:

SELECT *
    FROM #vehicle v
        INNER JOIN #vehicleclass vc 
            ON v.vehiclemake = vc.vehiclemake 
                AND v.vehiclemodel = vc.vehiclemodel
UNION
SELECT *
    FROM #vehicle v
        INNER JOIN #vehicleclass vc 
            ON v.vehiclemake = vc.vehiclemake 
                AND vc.vehiclemodel IS NULL
    WHERE NOT EXISTS(SELECT 1
                         FROM #vehicleclass vc2
                         where vc2.vehiclemake = v.vehiclemake
                            and vc2.vehiclemodel = v.vehiclemodel);

可能这就是您想要的:

SELECT  *
FROM    #vehicle v
        JOIN #vehicleclass vc ON v.vehiclemake = vc.vehiclemake
                                 AND ( v.vehiclemodel = vc.vehiclemodel
                                       OR ( vc.vehiclemodel IS NULL
                                            AND v.vehiclemodel NOT IN (
                                            SELECT  vehiclemodel
                                            FROM    #vehicleclass
                                            WHERE   vehiclemodel IS NOT NULL )
                                          )
                                     )

输出:

id  vehiclemake vehiclemodel    id  vehiclemake vehiclemodel    classtype
1   AUDI        R8              1   AUDI        R8              A
2   AUDI        Quattro         2   AUDI        NULL            B
SELECT *
FROM #vehicle v
LEFT JOIN #vehicleclass vc 
ON (v.vehiclemake = vc.vehiclemake 
AND v.vehiclemodel = COALESCE(vc.vehiclemodel, v.vehiclemodel))
WHERE v.Id = vc.id

输出:

id  vehiclemake vehiclemodel    id  vehiclemake vehiclemodel    classtype
1       AUDI        R8          1       AUDI        R8              A
2       AUDI      Quattro       2       AUDI        NULL            B

这里我倾向于使用UNION ALL,因为你确实有两个要求。

  1. 获取车辆中有匹配记录的所有车辆的class。
  2. 为没有匹配 class 的所有车型获取默认值 class。

尝试在单个查询中执行此操作是可能的,但通常会掩盖执行计划,因此无法以最佳方式使用索引。

SELECT  *
FROM    #Vehicle AS v
        INNER JOIN #vehicleclass AS vc
            ON vc.VehicleMake = v.VehicleMake
            AND vc.VehicleModel = v.VehicleModel
UNION ALL
SELECT  *
FROM    #Vehicle AS v
        INNER JOIN #vehicleclass AS vc
            ON vc.VehicleMake = v.VehicleMake
            AND vc.VehicleModel IS NULL
WHERE   NOT EXISTS
        (   SELECT  1
            FROM    #vehicleclass AS vc2
            WHERE   vc2.VehicleMake = v.VehicleMake
            AND     vc2.VehicleModel = v.VehicleModel
        );

要在没有联合的情况下执行此操作,您可以使用:

SELECT  *
FROM    #Vehicle AS v
        CROSS APPLY
        (   SELECT  TOP 1 *
            FROM    #vehicleclass AS vc
            WHERE   vc.VehicleMake = v.VehicleMake
            ORDER BY CASE WHEN vc.VehicleModel = v.VehicleModel THEN 0 ELSE 1 END
        ) AS vc;

这将取决于您在每个 table 上拥有的索引,哪个会表现更好。没有索引后者有更好的执行计划,因为它只需要两次扫描,但是如果我为每个 table:

添加索引
CREATE NONCLUSTERED INDEX #IX_Vehicle__VehicleMake_VehicleClass ON #Vehicle (VehicleMake, VehicleModel) ;
CREATE NONCLUSTERED INDEX #IX_VehicleClass__VehicleMake_VehicleClass ON #Vehicle (VehicleMake, VehicleModel);

然后前者变得更有效率,因为它能够更好地利用索引搜索。