【问题标题】:LEFT JOIN doesn't return all valuesLEFT JOIN 不会返回所有值
【发布时间】:2020-12-17 10:44:25
【问题描述】:

我有以下表格:

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 的值。看到这个小提琴:Fiddle

【问题讨论】:

  • 您的WHERE 正在将您的外连接变成内连接

标签: sql sql-server datetime left-join where-clause


【解决方案1】:

LEFT JOINed 表上的条件从WHERE 子句移动到连接的ON 子句 - 否则,它们将成为强制性的,并且任一表中的不匹配行都将被过滤掉。

所以:

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() 中的列前面加上它们所属的表(我不得不猜测):这是一种很好的做法,并且使查询不言自明,明确

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

【讨论】:

    【解决方案2】:

    这是因为您按维护和维修表的日期进行过滤。只有 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
    

    【讨论】:

      【解决方案3】:

      如果您希望SUM()s 正确,我建议使用相关子查询编写此逻辑。问题是每个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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-09
        • 2016-07-31
        • 1970-01-01
        • 2017-09-02
        相关资源
        最近更新 更多