#sql #sql-server #datetime #left-join #where-clause
#sql #sql-сервер #дата и время #соединение по левому краю #where-предложение
Вопрос:
У меня есть следующие таблицы:
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
Но оно не возвращает значения для всех типов карт. Смотрите эту скрипку: Скрипка
Комментарии:
1. Вы
WHERE
превращаете свои внешние соединения во внутренние соединения
Ответ №1:
Переместите условия в LEFT JOIN
таблицах редактирования из 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()
таблице, к которой они принадлежат (мне пришлось сделать предположение): это хорошая практика, которая делает запрос понятным и однозначным YYYYMMDD
поддерживается во всех версиях SQL Server в качестве литерала даты, независимо от региональных настроек, поэтому это может быть лучшим выбором, чем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
— и это декартово произведение означает, что сумма вычисляется правильно:
Если CarType
является уникальным в Cars
, вы можете использовать:
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