2017-04-07 75 views
0

添加DATENAME()函數查詢會導致重複行,儘管'distinct'。DATENAME導致'Distinct'被忽略

TREE - TreeId, CityId, DatePlanted 
WATER - WaterId, TreeId(fk), DateWatered 

表1是一對多的表2

在樹表中的每一行表示一個樹的栽培。水錶是澆樹的單一實例。一棵樹每年澆水多次。你明白了。

我需要返回一個報告顯示種植樹木的數量,按月份和數量它被澆水的次數。

SELECT t.CityId 
     , COUNT(distinct t.TreeId) as 'Trees Planted' 
     , COUNT(w.TreeId) as 'Trees Watered'   
FROM TREE t 
JOIN WATER w ON t.TreeId = w.TreeId 
WHERE w.DateWatered between @Start AND @End 
GROUP BY t.CityId 

這工作正常。但是,當我嘗試按月分組時,t.Treeid不再明顯,所以樹的數量太高。

SELECT t.CityId 
    , DATENAME(month, w.DateWatered) 
     , COUNT(distinct t.TreeId) as 'Trees Planted' 
     , COUNT(w.TreeId) as 'Trees Watered'   
FROM TREE t 
JOIN WATER w ON t.TreeId = w.TreeId 
WHERE w.DateWatered between @Start AND @End 
GROUP BY t.CityId, DATENAME(month, w.DateWatered) 

編輯:我發現爲什麼我得到重複,但沒有如何解決它。如果2016年4月和2016年5月再次澆灌樹木,我會收到2棵樹的種植計數,2棵樹澆水,應該是種植1棵樹和2次澆水。如果我在沒有返回日期的情況下執行第一個查詢,我會得到正確的數字。因此,通過添加日期,即使我按年分組,然後按月分組,同一棵樹上有兩次澆水,它也顯示樹種兩次。我目前正在調查CTE的使用,可能會將查詢的每個部分分開。

+0

你有超過12個月的數據嗎?有時幾個月會重演 – HABO

+0

'Group by t.CityId,Datepart(month,w.DateWatered),Datepart(year,w.DateWatered)''而不是'DATENAME(month,w.DateWatered)' – TriV

+0

@habo - 是的,有很多年數據。這是爲什麼它是複製的,因爲幾個月?我如何解決它? – BattlFrog

回答

1
SELECT t.CityId 
     , ISNULL(DATENAME(month, w.DateWatered), DATENAME(month, t.DatePlanted)) 
     , (SELECT COUNT(tDistinct.TreeId) FROM TREE tDistinct 
     WHERE tDistinct.TreeId = t.TreeId AND DATENAME(month, tDistinct.DatePlanted) = DATENAME(month, t.DateWatered) AND t.DatePlanted between @Start AND @End) as 'Trees Planted' 
     , COUNT(w.TreeId) as 'Trees Watered'   
    FROM TREE t 
    JOIN WATER w ON t.TreeId = w.TreeId 
    WHERE w.DateWatered between @Start AND @End 
    GROUP BY t.CityId, DATENAME(month, w.DateWatered), DATENAME(month, t.DatePlanted) 

唯一的缺點是這裏在沒有樹的地方一個月一棵樹栽你的日期將是空的,所以我增加了一個檢查澆灌的情景......不知道你的數據是什麼樣子所以忽略ISNULL檢查有利於您的原始分組

編輯: 根據您的要求,我不認爲CTE是必要的;根據您所提供的我已經改變了查詢稍稍滿足您的需求的附加信息:

`SELECT DATENAME(MONTH, myConsolidatedTree.DateAction) as myDate 
      ,(SELECT COUNT(*) 
       FROM TREE AS t 
      WHERE 
      DATENAME(MONTH, myConsolidatedTree.DateAction) = DATENAME(MONTH, t.DatePlanted) 
      ) as myNumberOfPlanted 
      ,(SELECT COUNT(*) 
       FROM WATER AS w 
      WHERE 
       DATENAME(MONTH, myConsolidatedTree.DateAction) = DATENAME(MONTH, w.DateWatered) 
        ) as myNumberOfWatered 

     FROM(
      SELECT t.DatePlanted as DateAction 
        ,t.TreeId as IdAction 
        ,'PLANTED' as TreeAction 
       FROM TREE t 

      UNION 

      SELECT w.DateWatered as DateAction 
        ,w.TreeId as IdAction 
        ,'WATERED' as TreeAction 
       FROM WATER w) as myConsolidatedTree 
    WHERE myConsolidatedTree.DateAction between @StartDate and @EndDate 
    GROUP BY DATENAME(MONTH, myConsolidatedTree.DateAction), DATEPART(MONTH, myConsolidatedTree.DateAction) 
    ORDER BY DATEPART(MONTH, myConsolidatedTree.DateAction)` 

雖然合併子查詢包含比需要爲這個問題,我離開了附加TreeId更多的信息和衍生TreeAction列有在您未來可能會遇到此需求。

1

這演示瞭如何將問題分解成公用表表達式(CTE)中的步驟。請注意,您可以將最後的select替換爲註釋select之一以查看中間結果。這是測試,調試或理解正在發生的事情的便捷方式。

你所面對的問題之一就是試圖僅基於飲水日期總結數據。如果一棵樹在一個沒有澆水的月份裏種植,那麼它不會被計算在內。下面的代碼分別總結了日期範圍內的種植和供水情況,然後將它們組合成單個結果集。

-- Sample data. 
declare @Trees as Table (TreeId Int Identity, CityId Int, DatePlanted Date); 
declare @Waterings as Table (WateringId Int Identity, TreeId Int, DateWatered Date); 
insert into @Trees (CityId, DatePlanted) values 
    (1, '20160115'), (1, '20160118'), 
    (1, '20160308'), (1, '20160318'), (1, '20160118'), 
    (1, '20170105'), 
    (1, '20170205'), 
    (1, '20170401'), 
    (2, '20160113'), (2, '20160130'), 
    (2, '20170226'), (2, '20170227'), (2, '20170228'); 
insert into @Waterings (TreeId, DateWatered) values 
    (1, '20160122'), (1, '20160129'), (1, '20160210'), (1, '20160601'), 
    (5, '20160120'), (5, '20160127'), (5, '20160215'), (5, '20160301'), (5, '20160515'); 
select * from @Trees; 
select * from @Waterings; 

-- Combine the data. 
declare @StartDate as Date = '20100101', @EndDate as Date = '20200101'; 
with 
    -- Each tree with the year and month it was planted. 
    TreesPlanted as (
    select CityId, TreeId, 
     DatePart(year, DatePlanted) as YearPlanted, 
     DatePart(month, DatePlanted) as MonthPlanted 
     from @Trees 
     where @StartDate <= DatePlanted and DatePlanted <= @EndDate), 
    -- Tree plantings summarized by city, year and month. 
    TreesPlantedSummary as (
    select CityId, YearPlanted, MonthPlanted, Count(TreeId) as Trees 
     from TreesPlanted 
     group by CityId, YearPlanted, MonthPlanted), 
    -- Each watering and the year and month it occurred. 
    TreesWatered as (
    select CityId, W.TreeId, 
     DatePart(year, W.DateWatered) as YearWatered, 
     DatePart(month, W.DateWatered) as MonthWatered 
     from @Trees as T left outer join 
     @Waterings as W on W.TreeId = T.TreeId 
     where @StartDate <= W.DateWatered and W.DateWatered <= @EndDate), 
    -- Waterings summarized by city, year and month. 
    TreesWateredSummary as (
    select CityId, YearWatered, MonthWatered, 
     Count(distinct TreeId) as Trees, Count(TreeId) as Waterings 
     from TreesWatered 
     group by CityId, YearWatered, MonthWatered) 
    -- Combine the plantings and waterings for the specified period. 
    select Coalesce(TPS.CityId, TWS.CityId) as CityId, 
    Coalesce(TPS.YearPlanted, TWS.YearWatered) as Year, 
    Coalesce(TPS.MonthPlanted, TWS.MonthWatered) as Month, 
    Coalesce(TPS.Trees, 0) as TreesPlanted, 
    Coalesce(TWS.Trees, 0) as TreesWatered, 
    Coalesce(TWS.Waterings, 0) as Waterings 
    from TreesPlantedSummary as TPS full outer join 
     TreesWateredSummary as TWS on TWS.CityId = TPS.CityId and 
     TWS.YearWatered = TPS.YearPlanted and TWS.MonthWatered = TPS.MonthPlanted 
    order by CityId, Year, Month; 
-- Alternative queries for testing/debugging/understanding: 
-- select * from TreesPlantedSummary order by CityId, YearPlanted, MonthPlanted; 
-- select * from TreesWateredSummary order by CityId, YearWatered, MonthWatered; 

現在你想要在結果中包含缺失的月份(沒有活動),呃?