Sql server DATENAME原因';独特的';被忽视

Sql server DATENAME原因';独特的';被忽视,sql-server,tsql,Sql Server,Tsql,将DATENAME()函数添加到查询会导致重复的行,尽管“distinct” TREE - TreeId, CityId, DatePlanted WATER - WaterId, TreeId(fk), DateWatered 表1是表2的一对多 树表中的每一行表示树的种植。地下水位只是给那棵树浇水的一个例子。一棵树一年要浇很多次水。你明白了 我需要返回一份报告,显示每月种植的树木数量和浇水次数 SELECT t.CityId , COUNT(distinct t.TreeI

将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棵树。如果我在第一次查询时没有返回日期,我会得到正确的号码。因此,通过添加日期,即使我按年份分组,然后按月份分组,对同一棵树进行两次浇水,它也显示了种植了两次的树。我目前正在调查CTE的使用情况,以使查询的每个部分保持独立

   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列保留在那里,以备将来可能需要

这里唯一的缺点是,如果一棵树在一个月内没有浇水,那么你的日期将为空,因此我添加了一个检查…不确定你的数据是什么样的,因此忽略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列保留在那里,以备将来可能需要使用。

这演示了如何在公共表表达式(CTE)中将问题分解为多个步骤。请注意,您可以将最后的
select
与其中一个注释的
select
s互换,以查看中间结果。这是一种方便的测试、调试或理解正在发生的事情的方法

你要解决的一个问题是试图仅仅根据浇水日期来总结数据。如果一棵树在一个月内没有浇水,那么它就不会被计算在内。下面的代码分别总结了日期范围内的种植和浇水情况,然后将它们组合成一个结果集

-- 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;
——示例数据。
将@Trees声明为表(TreeId Int Identity、CityId Int、DatePlanted Date);
将@Waterings声明为表(WateringId Int-Identity、TreeId Int、DateWatered-Date);
插入@Trees(CityId,dateplant)值
( 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' );
插入@Waterings(TreeId,DateWatered)值
( 1, '20160122' ), ( 1, '20160129' ), ( 1, '20160210' ), ( 1, '20160601' ),
( 5, '20160120' ), ( 5, '20160127' ), ( 5, '20160215' ), ( 5, '20160301' ), ( 5, '20160515' );
从@Trees中选择*;
从@Waterings中选择*;
--合并数据。
声明@StartDate为日期='20100101',@EndDate为日期='20200101';
具有
--每棵树都有种植年份和月份。
树被称为(
选择CityId、TreeId、,
DatePart(年份,DatePlanted)作为YearPlanted,
日期部分(月,日期种植)为月份
来自@Trees

其中@StartDate演示了如何在公共表表达式(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;
——示例数据。
将@Trees声明为表(TreeId Int Identity、CityId Int、DatePlanted Date);
将@Waterings声明为表(WateringId Int-Identity、TreeId Int、DateWatered-Date);
插入@Trees(CityId,dateplant)值
( 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' );
插入@Waterings(TreeId,DateWatered)值
( 1, '20160122' ), ( 1, '20160129' ), ( 1, '20160210' ), ( 1, '20160601' ),
( 5, '20160120' ), ( 5, '20160127' ), ( 5, '20160215' ), ( 5, '20160301' ), ( 5, '20160515' );
从@Trees中选择*;
从@Waterings中选择*;
--合并数据。
声明@StartDate为日期='20100101',@EndDate为日期='20200101';
具有
--每棵树都有种植年份和月份。
树被称为(
选择CityId、TreeId、,
DatePart(年份,DatePlanted)作为YearPlanted,
Da