LEFT JOIN 不 return 所有值

LEFT JOIN doesn't return all values

我有以下表格:

create table Cars
(
  CarID int,
  CarType varchar(50)
);


create table Maintenances
(
  CarID int,
  MaintenanceDate date,
  MaintenanceCost money
);

create table Repairs
(
  CarID int,
  RepairDate date,
  RepairCost money
);

我试过这个:

SELECT C.CarType,
SUM(MaintenanceCost),
SUM(RepairCost)
FROM Cars AS C 
LEFT JOIN Maintenances AS M ON M.CarID=C.CarID
LEFT JOIN Repairs AS R ON R.CarID=C.CarID
WHERE M.MaintenanceDate BETWEEN '2020-12-01' AND '2020-12-31' AND
R.RepairDate BETWEEN '2020-12-01' AND '2020-12-31'
GROUP BY C.CarType

但它并不是所有 CarType 的 return 值。看到这个 fiddle:Fiddle

LEFT JOINed table 上的条件从 WHERE 子句移动到联接的 ON 子句 - 否则,它们将成为强制性的,而不是- 过滤掉 table 中的匹配行。

所以:

SELECT C.CarType, SUM(M.MaintenanceCost), SUM(R.RepairCost)
FROM Cars AS C 
LEFT JOIN Maintenances M 
    ON  M.CarID = C.CarID 
    AND M.MaintenanceDate BETWEEN '20201201' AND '20201231'
LEFT JOIN Repairs R 
    ON  R.CarID = C.CarID 
    AND R.RepairDate BETWEEN '20201201' AND '20201231'
GROUP BY C.CarType

旁注:

  • 我在 SUM() 中的列前面加上它们所属的 table 前缀(我不得不猜测):这是很好的做法,并且使查询自-解释性的,明确的

  • YYYYMMDD 在 SQL 服务器的所有版本中都支持作为日期文字,无论区域设置如何,因此它可能是比 YYYY-MM-DD 更好的选择

那是因为您是按维护和维修日期过滤的 table。在 tables.

中,只有 CarID 3 具有您给定日期范围的条目

如果您希望结果包含您日期范围内的所有车型,您可以试试这个查询。

    SELECT C.CarType,
    ISNULL(SUM(MaintenanceCost),0) AS MaintenanceCost,
    ISNULL(SUM(RepairCost),0) AS RepairCost
     Cars AS C 
     JOIN Maintenances AS M 
       M.CarID = C.CarID
       M.MaintenanceDate BETWEEN '2020-12-01' AND '2020-12-31'
     JOIN Repairs AS R 
       R.CarID = C.CarID
       R.RepairDate BETWEEN '2020-12-01' AND '2020-12-31'
     BY C.CarType

如果您希望 SUM() 正确,我建议使用相关子查询编写此逻辑。问题是你最终得到每个 Carid 的笛卡尔积——笛卡尔积意味着总和计算正确:

如果CarTypeCars中是唯一的,你可以使用:

SELECT C.CarType,
       (SELECT SUM(M.MaintenanceCost)
        FROM Maintenances M 
        WHERE M.CarID = C.CarID AND
              M.MaintenanceDate BETWEEN '20201201' AND '20201231' 
       ),
       (SELECT SUM(R.RepairCost)
        FROM Repairs R
        WHERE R.CarID = R.CarID AND
              R.RepairDate BETWEEN '20201201' AND '20201231' 
       )
FROM Cars C ;

否则,这看起来有点复杂,因为您需要在每个子查询中使用 CarType

SELECT C.CarType,
       (SELECT SUM(M.MaintenanceCost)
        FROM Maintenances M JOIN
             Cars C
             ON M.CarID = C.CarID
        WHERE C.CarType = CT.CarType AND
              M.MaintenanceDate BETWEEN '20201201' AND '20201231' 
       ),
       (SELECT SUM(R.RepairCost)
        FROM Repairs R JOIN
             Cars C
             ON R.CarID = C.CarID
        WHERE C.CarType = CT.CarType AND
              R.RepairDate BETWEEN '20201201' AND '20201231' 
       )
FROM (SELECT DISTINCT CarType FROM Cars C) CT