带有双GROUP BY的MsSQL左联接

时间:2018-10-04 11:29:41

标签: sql sql-server join

我正在尝试将MsSQL用于产品运输数据库。我花了很长时间尝试编写一个SQL查询,以获取从今天开始的4天内每个区域每个日期到每个送货地点的产品数量。 在一个区域中意味着有另一个位置是该位置的父级。

有关表是产品和位置,其结构如下

Products    
ProductID  DeliveryDate  DestinationID
1          2018-10-05    1
2          2018-10-05    2
3          2018-10-05    3
4          2018-10-06    1
5          2018-10-06    5

Locations
LocationID  OwnerID     
1           4
2           4
3           4
4           Null
5           6
6           Null

所需的输出如下

DeliveryDate  Destination ProductCount
2018-10-04    1           Null
2018-10-04    2           Null
2018-10-04    3           Null
2018-10-05    1           1
2018-10-05    2           1
2018-10-05    3           1 
2018-10-06    1           1
2018-10-06    2           Null
2018-10-06    3           Null
2018-10-07    1           Null
2018-10-07    2           Null
2018-10-07    3           Null

到目前为止,我尝试过的是

DECLARE @startdate DATETIME
    ,@enddate DATETIME

SET @startdate = convert(varchar, '2018-10-04 00:00:00', 102)
SET @enddate = convert(varchar, '2018-10-07 00:00:00', 102)

;WITH DateArray
AS (
    SELECT @startdate DateVal

    UNION ALL

    SELECT DateVal + 1
    FROM DateArray
    WHERE DateVal + 1 <= @enddate
    )
SELECT * FROM
(SELECT da.DateVal AS DeliveryDate
FROM DateArray da) a
LEFT JOIN 
(SELECT ISNULL(COUNT(p.ProductID),0) AS ProductCount,
        DeliveryDate,
        ISNULL(p.DestinationID,'') AS Destination
FROM Product p
AND p.DestinationID IN(SELECT LocationID FROM Locations WHERE OwnerID = 4)
GROUP BY p.DestinationID, p.DeliveryDate) AS b
ON b.DeliveryDate = a.DeliveryDate

该结果可确保即使ProductCount为空,也将显示所有日期,但是,如果count为空,则不会显示每个目标。如下所示:

DeliveryDate  Destination  ProductCount
2018-10-04    Null        Null
2018-10-05    1           1
2018-10-05    2           1
2018-10-05    3           1 
2018-10-06    1           1
2018-10-07    Null        Null

我花了两天的时间来顽固地尝试使用许多在线SQL资源并仔细检查StackOverFlow来解决这个问题,但是没有运气。

1 个答案:

答案 0 :(得分:0)

我建议创建一个数字表或日历表,但是递归查询足以满足小型集合的需求。

DECLARE
  @startdate DATETIME = '2018-10-04 00:00:00',
  @enddate   DATETIME = '2018-10-07 00:00:00';

WITH
  DateArray AS
(
  SELECT @startdate DateVal
  UNION ALL
  SELECT DateVal + 1 FROM DateArray WHERE DateVal + 1 <= @enddate
)
SELECT
  DateArray.DateVal          AS DeliveryDate,
  Locations.LocationID       AS Destination,
  COUNT(Products.ProductID)  AS ProductCount
FROM
  DateArray
CROSS JOIN
(
  SELECT * FROM Locations WHERE OwnerID = 4
)
  Locations
LEFT JOIN
  Products
    ON  Products.DeliveryDate  = DateArray.DateVal
    AND Products.DestinationID = Locations.LocationID
GROUP BY
  DateArray.DateVal,
  Locations.LocationID

或者...

SELECT
  DateArray.DateVal          AS DeliveryDate,
  Locations.LocationID       AS Destination,
  Products.ProductCount      AS ProductCount
FROM
  DateArray
CROSS JOIN
(
  SELECT * FROM Locations WHERE OwnerID = 4
)
  Locations
LEFT JOIN
(
  SELECT DeliveryDate, DestinationID, COUNT(*) AS ProductCount
    FROM Products
GROUP BY DeliveryDate, DestinationID
)
  Products
    ON  Products.DeliveryDate  = DateArray.DateVal
    AND Products.DestinationID = Locations.LocationID
相关问题