【问题标题】:Catch All Record in a Join expression捕获联接表达式中的所有记录
【发布时间】:2015-04-09 13:47:37
【问题描述】:

我有一个很简单(我觉得很简单,但我还在苦苦挣扎!)的问题。我有一张汽车表。我还有另一张汽车等级表。我想加入一个到另一个为汽车上课。

汽车表很简单,有型号和品牌。类表也有一个模型和一个品牌和一个类的类型。当我想将相同品牌的汽车组合在一起时,就会出现问题,而不管它们的型号如何。

例如,我有两辆车:

id  vehiclemake vehiclemodel
1   AUDI            R8
2   AUDI            Quattro

我有两个班级:

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

AUDI R8 将与 A 类匹配。我希望所有其他 AUDI,无论其型号如何,都与 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 类记录中

【问题讨论】:

  • 在这种情况下你想得到什么输出?
  • SQLFiddle 链接:sqlfiddle.com/#!6/bf58e/1/0
  • 在这种情况下,结果应该是:1 AUDI R8 1 AUDI R8 A 2 AUDI Quattro 2 AUDI NULL B
  • 在车辆型号为空的表vehicleclass中,每个vehiclemake是否最多有1条记录?
  • 是的,每个型号/车辆制造只有一条记录,其中型号/null 是全部

标签: sql tsql join sql-server-2008-r2 left-join


【解决方案1】:

vehiclemodel 的连接上使用COALESCE,这样当vehicleclass 表上的模型为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);

【讨论】:

  • 但它为 R8 带回了两条记录,其中它与模型匹配并且模型为空。对于 R8 记录,它应该只带回来一次
【解决方案2】:

也许这就是你想要的:

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

【讨论】:

  • 您好!我想我们可能会有赢家!
【解决方案3】:
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

【讨论】:

  • 这些Id 值在表之间匹配只是一个巧合。这不是要匹配的有效列。
【解决方案4】:

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

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

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

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;

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

CREATE NONCLUSTERED INDEX #IX_Vehicle__VehicleMake_VehicleClass ON #Vehicle (VehicleMake, VehicleModel) ;
CREATE NONCLUSTERED INDEX #IX_VehicleClass__VehicleMake_VehicleClass ON #Vehicle (VehicleMake, VehicleModel);

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多