Sql “如果一对多表有值”作为SELECT中的列

Sql “如果一对多表有值”作为SELECT中的列,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我有一张有汽车的桌子,还有一张有燃料类型的桌子。第三个表格记录了哪些汽车可以使用哪些燃料类型。我需要选择所有汽车的所有数据,包括它们可以使用的燃油类型: Car表有Car\u ID、Car\u名称等 燃料表有燃料ID、燃料名称 Car_Fuel table具有Car_ID,Fuel_ID一辆车可以有多种燃油选项 我想回报的是: SELECT * , Can_Use_Gas , Can_Use_Diesel , Can_Use_Electric FROM Car

我有一张有汽车的桌子,还有一张有燃料类型的桌子。第三个表格记录了哪些汽车可以使用哪些燃料类型。我需要选择所有汽车的所有数据,包括它们可以使用的燃油类型:

Car表有Car\u ID、Car\u名称等

燃料表有燃料ID、燃料名称

Car_Fuel table具有Car_ID,Fuel_ID一辆车可以有多种燃油选项

我想回报的是:

SELECT
    *
    , Can_Use_Gas
    , Can_Use_Diesel
    , Can_Use_Electric
FROM Car
Can_Use列是一个位值,指示汽车在car_燃料表中是否有匹配的燃料条目

我可以用多个SELECT语句来实现这一点,但这看起来非常混乱,可能效率非常低?。我希望有更好的办法:

SELECT
    c.*
    , (SELECT COUNT(*) FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 1) AS Can_Use_Gas
    , (SELECT COUNT(*) FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 2) AS Can_Use_Diesel
    , (SELECT COUNT(*) FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 3) AS Can_Use_Electric
FROM Car c

假设汽车燃油中没有重复项,因此不需要聚合。因此,您可以:

SELECT c.*,
       ISNULL((SELECT TOP 1 1 FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 1), 0) AS Can_Use_Gas
       ISNULL((SELECT TOP 1 1 FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 2), 0) AS Can_Use_Diesel
       ISNULL((SELECT TOP 1 1 FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 3), 0) AS Can_Use_Electric
FROM Car c;

在这种情况下,ISNULL比COALESCE具有性能优势,因为COALESCE对第一个参数求值两次。

假设汽车燃油中没有重复项,因此不需要聚合。因此,您可以:

SELECT c.*,
       ISNULL((SELECT TOP 1 1 FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 1), 0) AS Can_Use_Gas
       ISNULL((SELECT TOP 1 1 FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 2), 0) AS Can_Use_Diesel
       ISNULL((SELECT TOP 1 1 FROM Car_Fuel f WHERE f.Car_ID = c.Car_ID AND f.Fuel_ID = 3), 0) AS Can_Use_Electric
FROM Car c;

在这种情况下,ISNULL比COALESCE具有性能优势,因为COALESCE对第一个参数求值两次。

虽然不是完美的解决方案,但您可以使用:

请参见,其中输出如下表:

| car_name | Gas | Diesel | Electric |
|----------|-----|--------|----------|
|   Jaguar |   0 |      1 |        0 |
| Mercedes |   0 |      1 |        1 |
|    Volvo |   1 |      0 |        1 |
SQL语句在pivot部分的for子句中仍然具有硬编码列表,但是当燃料类型的数量增加时,这可能更容易管理,并且具有更好的性能

动态生成SQL 如果使用应用程序服务器,可以首先执行以下查询:

SELECT stuff(   (   SELECT ',' + fuel_name
                    FROM Fuel FOR XML PATH('')
                ), 1, 1, '') columns
这将以一个逗号分隔的值返回列列表,例如:

Gas,Diesel,Electric
SELECT c.Car_ID
     , c.Car_Name
     , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
     , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
     , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
 FROM Car c
 LEFT
 JOIN Car_Fuel f
   ON f.Car_ID = c.Car_ID
GROUP
   BY c.Car_ID
    , c.Car_Name
SELECT c.Car_ID
     , c.Car_Name
     , ISNULL(u.Can_Use_Gas,0)      AS Can_Use_Gas
     , ISNULL(u.Can_Use_Diesel,0)   AS Can_Use_Diesel
     , ISNULL(u.Can_Use_Electric,0) AS Can_Use_Electric
  FROM Car c
  LEFT
  JOIN ( SELECT f.Car_ID
              , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
              , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
              , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
           FROM Car_Fuel f
          GROUP BY f.Car_ID
       ) u
    ON u.Car_ID = c.Car_ID

您可以获取该结果并将其插入FOR子句的第一个查询中。

虽然不是一个完美的解决方案,但您可以使用:

请参见,其中输出如下表:

| car_name | Gas | Diesel | Electric |
|----------|-----|--------|----------|
|   Jaguar |   0 |      1 |        0 |
| Mercedes |   0 |      1 |        1 |
|    Volvo |   1 |      0 |        1 |
SQL语句在pivot部分的for子句中仍然具有硬编码列表,但是当燃料类型的数量增加时,这可能更容易管理,并且具有更好的性能

动态生成SQL 如果使用应用程序服务器,可以首先执行以下查询:

SELECT stuff(   (   SELECT ',' + fuel_name
                    FROM Fuel FOR XML PATH('')
                ), 1, 1, '') columns
这将以一个逗号分隔的值返回列列表,例如:

Gas,Diesel,Electric
SELECT c.Car_ID
     , c.Car_Name
     , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
     , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
     , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
 FROM Car c
 LEFT
 JOIN Car_Fuel f
   ON f.Car_ID = c.Car_ID
GROUP
   BY c.Car_ID
    , c.Car_Name
SELECT c.Car_ID
     , c.Car_Name
     , ISNULL(u.Can_Use_Gas,0)      AS Can_Use_Gas
     , ISNULL(u.Can_Use_Diesel,0)   AS Can_Use_Diesel
     , ISNULL(u.Can_Use_Electric,0) AS Can_Use_Electric
  FROM Car c
  LEFT
  JOIN ( SELECT f.Car_ID
              , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
              , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
              , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
           FROM Car_Fuel f
          GROUP BY f.Car_ID
       ) u
    ON u.Car_ID = c.Car_ID

您将获取该结果并将其注入FOR子句中的第一个查询。

我怀疑使用counts将是低效的,因为将有大量子查询运行以合计所有计数

下面是使用自联接的替代方法。它不像您的示例那么短,但可能更易于维护和阅读,并且应该更高效

select car.car_id, car.car_name,
-- Select fuel variables 
CASE WHEN lpg.fuel_id IS NULL THEN 0 ELSE 1 END AS LPG,
CASE WHEN unleaded.fuel_id IS NULL THEN 0 ELSE 1 END AS Unleaded,
CASE WHEN electric.fuel_id IS NULL THEN 0 ELSE 1 END AS Electric,
CASE WHEN diesel.fuel_id IS NULL THEN 0 ELSE 1 END AS Diesel
FROM car
-- Self Join fuel records
LEFT join car_fuel as lpg on car.car_id = lpg.car_id and lpg.fuel_id = 1
LEFT join car_fuel as unleaded on car.car_id = unleaded.car_id and unleaded.fuel_id = 2
LEFT join car_fuel as electric on car.car_id = electric.car_id and electric.fuel_id = 3
LEFT join car_fuel as diesel on car.car_id = diesel.car_id and diesel.fuel_id = 4
如果汽车不使用该燃油类型,自联接将返回NULL。如果联接找到该汽车/燃油的记录,则案例返回1,如果没有,则返回0


我希望这会有所帮助。

我怀疑使用计数会效率低下,因为会有大量子查询运行以合计所有计数

下面是使用自联接的替代方法。它不像您的示例那么短,但可能更易于维护和阅读,并且应该更高效

select car.car_id, car.car_name,
-- Select fuel variables 
CASE WHEN lpg.fuel_id IS NULL THEN 0 ELSE 1 END AS LPG,
CASE WHEN unleaded.fuel_id IS NULL THEN 0 ELSE 1 END AS Unleaded,
CASE WHEN electric.fuel_id IS NULL THEN 0 ELSE 1 END AS Electric,
CASE WHEN diesel.fuel_id IS NULL THEN 0 ELSE 1 END AS Diesel
FROM car
-- Self Join fuel records
LEFT join car_fuel as lpg on car.car_id = lpg.car_id and lpg.fuel_id = 1
LEFT join car_fuel as unleaded on car.car_id = unleaded.car_id and unleaded.fuel_id = 2
LEFT join car_fuel as electric on car.car_id = electric.car_id and electric.fuel_id = 3
LEFT join car_fuel as diesel on car.car_id = diesel.car_id and diesel.fuel_id = 4
如果汽车不使用该燃油类型,自联接将返回NULL。如果联接找到该汽车/燃油的记录,则案例返回1,如果没有,则返回0


我希望这有帮助。

您可以使用条件聚合

对Car\u Fuel表进行外部联接,并按Car\u ID进行分组以折叠行

对于来自Car_Fuel的每一行,如果Fuel_ID与您正在检查的匹配,则返回1,否则返回0。并使用最大聚合筛选行,找出其中是否有返回1的行

例如:

Gas,Diesel,Electric
SELECT c.Car_ID
     , c.Car_Name
     , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
     , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
     , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
 FROM Car c
 LEFT
 JOIN Car_Fuel f
   ON f.Car_ID = c.Car_ID
GROUP
   BY c.Car_ID
    , c.Car_Name
SELECT c.Car_ID
     , c.Car_Name
     , ISNULL(u.Can_Use_Gas,0)      AS Can_Use_Gas
     , ISNULL(u.Can_Use_Diesel,0)   AS Can_Use_Diesel
     , ISNULL(u.Can_Use_Electric,0) AS Can_Use_Electric
  FROM Car c
  LEFT
  JOIN ( SELECT f.Car_ID
              , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
              , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
              , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
           FROM Car_Fuel f
          GROUP BY f.Car_ID
       ) u
    ON u.Car_ID = c.Car_ID
对于SQL Server,您需要重复GROUPBY子句中SELECT列表中的每个非聚合表达式。如果从Car表中添加更多列以选择列表,则必须将这些列复制到GROUP BY

如果这太痛苦,您可以在内联视图中进行聚合,然后进行连接。要确保不返回NULL,可以在外部查询中将NULL值替换为0:

例如:

Gas,Diesel,Electric
SELECT c.Car_ID
     , c.Car_Name
     , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
     , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
     , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
 FROM Car c
 LEFT
 JOIN Car_Fuel f
   ON f.Car_ID = c.Car_ID
GROUP
   BY c.Car_ID
    , c.Car_Name
SELECT c.Car_ID
     , c.Car_Name
     , ISNULL(u.Can_Use_Gas,0)      AS Can_Use_Gas
     , ISNULL(u.Can_Use_Diesel,0)   AS Can_Use_Diesel
     , ISNULL(u.Can_Use_Electric,0) AS Can_Use_Electric
  FROM Car c
  LEFT
  JOIN ( SELECT f.Car_ID
              , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
              , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
              , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
           FROM Car_Fuel f
          GROUP BY f.Car_ID
       ) u
    ON u.Car_ID = c.Car_ID

您可以使用条件聚合

对Car\u Fuel表进行外部联接,并按Car\u ID进行分组以折叠行

对于来自Car_Fuel的每一行,如果Fuel_ID与您正在检查的匹配,则返回1,否则返回0。并使用最大聚合筛选行,找出其中是否有返回1的行

例如:

Gas,Diesel,Electric
SELECT c.Car_ID
     , c.Car_Name
     , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
     , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
     , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
 FROM Car c
 LEFT
 JOIN Car_Fuel f
   ON f.Car_ID = c.Car_ID
GROUP
   BY c.Car_ID
    , c.Car_Name
SELECT c.Car_ID
     , c.Car_Name
     , ISNULL(u.Can_Use_Gas,0)      AS Can_Use_Gas
     , ISNULL(u.Can_Use_Diesel,0)   AS Can_Use_Diesel
     , ISNULL(u.Can_Use_Electric,0) AS Can_Use_Electric
  FROM Car c
  LEFT
  JOIN ( SELECT f.Car_ID
              , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
              , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
              , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
           FROM Car_Fuel f
          GROUP BY f.Car_ID
       ) u
    ON u.Car_ID = c.Car_ID
对于SQL Server,您需要重复GROUPBY子句中SELECT列表中的每个非聚合表达式。如果从Car表中添加更多列以选择列表,则必须将这些列复制到GROUP BY

如果这太痛苦,您可以在内联视图中进行聚合,然后进行连接。要确保不返回NULL,可以在外部查询中将NULL值替换为0:

例如:

Gas,Diesel,Electric
SELECT c.Car_ID
     , c.Car_Name
     , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
     , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
     , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
 FROM Car c
 LEFT
 JOIN Car_Fuel f
   ON f.Car_ID = c.Car_ID
GROUP
   BY c.Car_ID
    , c.Car_Name
SELECT c.Car_ID
     , c.Car_Name
     , ISNULL(u.Can_Use_Gas,0)      AS Can_Use_Gas
     , ISNULL(u.Can_Use_Diesel,0)   AS Can_Use_Diesel
     , ISNULL(u.Can_Use_Electric,0) AS Can_Use_Electric
  FROM Car c
  LEFT
  JOIN ( SELECT f.Car_ID
              , MAX(CASE WHEN f.Fuel_ID=1 THEN 1 ELSE 0 END) AS Can_Use_Gas
              , MAX(CASE WHEN f.Fuel_ID=2 THEN 1 ELSE 0 END) AS Can_Use_Diesel
              , MAX(CASE WHEN f.Fuel_ID=3 THEN 1 ELSE 0 END) AS Can_Use_Electric
           FROM Car_Fuel f
          GROUP BY f.Car_ID
       ) u
    ON u.Car_ID = c.Car_ID

您需要旋转carfuel表并
将结果连接到汽车上。我不明白-你能提供一个代码示例,或更清楚地解释吗?你需要旋转carfuel表并将结果连接到汽车上。我不明白-你能提供一个代码示例,或更清楚地解释吗?我使用的是ISNULL-显然sql server没有IFNULL。不管是哪种方式,它都奏效了,谢谢。@steelerose。哦,我不确定我在想什么。我使用了ISNULL-显然sql server没有IFNULL。不管是哪种方式,它都奏效了,谢谢。@steelerose。哎呀,我不知道我在想什么。