【问题标题】:SQL COUNT by each date with multiple date fields具有多个日期字段的每个日期的 SQL COUNT
【发布时间】:2019-05-18 14:35:35
【问题描述】:

我有下表和查询,但没有给出正确的结果。

JOB 表有 Open、Finish 和 Closed 日期。

现在我需要按每个日期和位置提取所选日期组之间的打开、完成和关闭作业的计数。

请帮助我得到如下预期结果中的结果

+-------+-----------+------------+-----------+----------+
| JOB_id| DateOpen  | DateFinish | DateClose | Location |
+-------+-----------+------------+-----------+----------+
|   100 | 16-Dec-18 | 18-Dec-18  | 19-Dec-18 | A        |
|   101 | 16-Dec-18 | 18-Dec-18  | 19-Dec-18 | A        |
|   102 | 17-Dec-18 | 19-Dec-18  | 20-Dec-18 | C        |
|   103 | 10-Dec-18 | 11-Dec-18  | 16-Dec-18 | D        |
|   104 | 17-Dec-18 | 19-Dec-18  | 18-Dec-18 | E        |
+-------+-----------+------------+-----------+----------+

查询:

SELECT count(DateOpen) as Opened,
       count(DateFinish) as Finised,
      count(DateClose) as Closed,
      (DateOpen) as Date 
FROM JOBS
WHERE DateOpen BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/17/2018')
group by DateOpen

预期结果:

+-----------+------+----------+--------+----------+
|   Date    | Open | Finished | Closed | Location |
+-----------+------+----------+--------+----------+
| 16-Dec-18 |    2 |        0 |      0 | A        |
| 16-Dec-18 |    0 |        0 |      1 | D        |
| 17-Dec-18 |    1 |        0 |      0 | C        |
| 17-Dec-18 |    1 |        0 |      0 | E        |
+-----------+------+----------+--------+----------+

【问题讨论】:

  • 当前输出是多少?
  • 能否粘贴查询生成数据
  • 如何通过状态区分 JOB_id 101 和 102 ?两者在 DateFinished 和 DateClosed 中都有相同的日期
  • 为什么您的日期列还不是 DATE 类型?
  • @SalmanA BETWEEN

标签: sql sql-server datetime sql-server-2012 sql-server-2014


【解决方案1】:

您可以将所有打开、完成和关闭日期放在一个列中,然后将其加入您的工作表:

DECLARE @date1 AS DATE = '2018-12-16';
DECLARE @date2 AS DATE = '2018-12-17';

WITH dates(date) AS (
    SELECT DateOpen FROM jobs
    UNION
    SELECT DateFinish FROM jobs
    UNION
    SELECT DateClose FROM jobs
)
SELECT dates.date
     , Location
     , COUNT(CASE WHEN dates.date = DateOpen THEN 1 END) AS Opened
     , COUNT(CASE WHEN dates.date = DateFinish THEN 1 END) AS Finished
     , COUNT(CASE WHEN dates.date = DateClose THEN 1 END) AS Closed
FROM dates
LEFT JOIN jobs ON dates.date IN (DateOpen, DateFinish, DateClose)
WHERE dates.date BETWEEN @date1 AND @date2
GROUP BY dates.date
       , Location

结果:

| date       | Location | Opened | Finished | Closed |
|------------|----------|--------|----------|--------|
| 16/12/2018 | A        | 2      | 0        | 0      |
| 16/12/2018 | D        | 0      | 0        | 1      |
| 17/12/2018 | C        | 1      | 0        | 0      |
| 17/12/2018 | E        | 1      | 0        | 0      |

Demo on DB Fiddle

【讨论】:

  • 您的查询正确提取了数据,但加载时间很长,因为我有一百万行
  • 你有在表上创建的索引吗?
  • 我感觉发生了隐式转换
【解决方案2】:

您可以使用以下查询来获取所需的结果集。

select coalesce(t1.date, t2.date, t3.date) as date, t1.Opened, t2.Finished, t3.Closed,
coalesce(t1.location, t2.location, t3.location) as location
from
(SELECT Convert(date,DateOpen) as Date ,count(JobID) as Opened, location      
FROM JOBS
WHERE DateOpen BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/17/2018')
group by Convert(date,DateOpen), location 
) t1
Full join 
(SELECT Convert(date,DateFinish) as Date ,count(JobID) as Finished, location      
FROM JOBS
WHERE DateFinish BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/17/2018')
group by Convert(date,DateFinish), location 
) t2  ON(t1.date = t2.date and t1.location = t2.location)
FULL JOIN
(SELECT Convert(date,DateClose) as Date ,count(JobID) as Closed, location      
FROM JOBS
WHERE DateClose BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/17/2018')
group by Convert(date,DateClose), location 
) t3  ON(t2.date = t3.date and t2.location = t3.location)

【讨论】:

  • 错误:从字符串转换日期和/或时间时转换失败。
【解决方案3】:

首先,我建议您停止将日期存储为文本并使用正确的数据类型。

要执行您需要的操作,请通过选择每个日期列和位置来列出所有日期和位置,并将它们合并为一个结果(第一个 cte - allDates)。然后我们需要一个不同的列表(第二个 cte - 聚合)来选择,并计算表中与当前日期/位置值匹配的行数。

这是整个解决方案:

declare @JOBS table(JOB_ID int, DateOpen varchar(10), DateFinish varchar(10), DateClose varchar(10), Location varchar(5))
insert into @JOBS values
(   100, '16-Dec-18', '18-Dec-18', '19-Dec-18', 'A'),
(   101, '16-Dec-18', '18-Dec-18', '19-Dec-18', 'A'),
(   102, '17-Dec-18', '19-Dec-18', '20-Dec-18', 'C'),
(   103, '10-Dec-18', '11-Dec-18', '16-Dec-18', 'D'),
(   104, '17-Dec-18', '19-Dec-18', '18-Dec-18', 'E')

;with allDates as (
select convert(date, DateOpen) as [Date], Location from @JOBS
union
select convert(date, DateFinish), Location from @JOBS
union
select convert(date, DateClose), Location from @JOBS
),
aggregated as (
    select [Date], Location
    from allDates
    group by [Date], Location
)
select
    a.Date
    , (select count(*) from @JOBS where a.[Date] = DateOpen and a.Location = Location) [Open]
    , (select count(*) from @JOBS where a.[Date] = DateFinish and a.Location = Location) Finished
    , (select count(*) from @JOBS where a.[Date] = DateClose and a.Location = Location) Closed
    , Location
from aggregated a
where a.Date between '20181216' and '20181217'

【讨论】:

  • 那你为什么将它们转换为日期,例如group by Convert(date,DateOpen)?
【解决方案4】:

您可以使用带有 sum 的 case 语句,

    SELECT  Convert(date,DateOpen) as Date ,      
           sum(case when DateOpen =DateOpen then 1 else 0 end) as Opened,
           sum(case when DateFinish=DateOpen then 1 else 0 end) as Finised,
           sum(case when DateClose=DateOpen then 1 else 0 end) as Closed,
           Location

    FROM JOBS
    WHERE DateOpen BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/17/2018')
    group by Convert(date,DateOpen),Location

    UNION

    SELECT  Convert(date,DateClose) as Date ,      
           sum(case when DateOpen =DateClose then 1 else 0 end) as Opened,
           sum(case when DateFinish=DateClose then 1 else 0 end) as Finised,
           sum(case when DateClose=DateClose then 1 else 0 end) as Closed,
           Location

    FROM JOBS
    WHERE DateClose BETWEEN '12/16/2018' AND DATEADD(DAY, 1, '12/16/2018')
    group by Convert(date,DateClose),Location

【讨论】:

    【解决方案5】:
    DECLARE @startDate DATETIME = '12/16/2018'
    DECLARE @endDate DATETIME = '12/17/2018'
    
    SELECT 
      count(CASE when DateOpen BETWEEN @startDate AND @endDate THEN 1 end) as Opened,
      count(CASE when DateFinish BETWEEN @startDate AND @endDate THEN 1 end) as Finised,
      count(CASE when DateClose BETWEEN @startDate AND @endDate THEN 1 end) as Closed,
      DateOpen as Date,
      Location
    FROM JOBS
    WHERE DateOpen BETWEEN @startDate AND @endDate
    group by DateOpen, Location
    

    【讨论】:

      【解决方案6】:

      您可以尝试以下代码,使用 SUM 函数对输入日期的进程状态求和:

      SELECT Convert(date1,DateOpen) as Date,
             sum(case
                  when DateOpen = Convert(date1,DateOpen) then 1
                  else 0
                 end) as Open,
             sum(case
                  when DateFinish = Convert(date1,DateOpen) then 1
                  else 0
                 end) as Finished,
            sum(case
                  when DateClose = Convert(date1,DateOpen) then 1
                  else 0
                 end) as Closed,
            Location
      FROM JOBS
      group by Location
      UNION ALL  
      SELECT Convert(date2,DateOpen) as Date,
             sum(case
                  when DateOpen = Convert(date2,DateOpen) then 1
                  else 0
                 end) as Open,
             sum(case
                  when DateFinish = Convert(date2,DateOpen) then 1
                  else 0
                 end) as Finished,
            sum(case
                  when DateClose = Convert(date2,DateOpen) then 1
                  else 0
                 end) as Closed,
            Location
      FROM JOBS
      group by Location;
      

      已编辑:date1date2 是输入参数。

      【讨论】:

        【解决方案7】:

        你可以试试用 unpivot 看看性能如何

        with cols_to_rows
          as (
              select *
                from t
              unpivot(col for val in (dateopen,datefinish,dateclose))m 
              )
        select col
               ,location 
               ,count(case when val='dateopen' then 1 end)   as open1
               ,count(case when val='datefinish' then 1 end) as finish
               ,count(case when val='dateclose' then 1 end)  as close1
          from cols_to_rows
        where col between cast('2018-12-16' as date)
          and cast('2018-12-17' as date)
        group by col,location,val
        order by col,location
        
        
        | 10/12/2018 00:00:00 | D | 1 | 0 | 0 |
        | 11/12/2018 00:00:00 | D | 0 | 1 | 0 |
        | 16/12/2018 00:00:00 | A | 2 | 0 | 0 |
        | 16/12/2018 00:00:00 | D | 0 | 0 | 1 |
        | 17/12/2018 00:00:00 | C | 1 | 0 | 0 |
        | 17/12/2018 00:00:00 | E | 1 | 0 | 0 |
        | 18/12/2018 00:00:00 | A | 0 | 2 | 0 |
        | 18/12/2018 00:00:00 | E | 0 | 0 | 1 |
        | 19/12/2018 00:00:00 | A | 0 | 0 | 2 |
        | 19/12/2018 00:00:00 | C | 0 | 1 | 0 |
        | 19/12/2018 00:00:00 | E | 0 | 1 | 0 |
        | 20/12/2018 00:00:00 | C | 0 | 0 | 1 |
        

        这是一个 dbfiddle 链接

        https://dbfiddle.uk/?rdbms=sqlserver_2012&fiddle=1ca0c0180a4d31d6e112e9f3b1b99715

        【讨论】:

          【解决方案8】:

          试试这个:

              DECLARE @MinDate DATE = '12/16/2018',
                      @MaxDate DATE = '12/17/2018'
          
          
              DECLARE @DateTable TABLE (DateOpen DATETIME)
          
              INSERT INTO @DateTable
              SELECT  TOP (DATEDIFF(DAY, @MinDate, @MaxDate) + 1)
                      Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate)
              FROM    sys.all_objects a
                      CROSS JOIN sys.all_objects b;
          
              ;with cte
              As
              (
                  SELECT DISTINCT d.DateOpen, 
                  SUM(CASE WHEN j.DateOpen =d.DateOpen THEN 1 ELSE 0 END) OVER(partition by d.DateOpen,Location) As [Open]
                  ,SUM(CASE WHEN j.DateFinish =d.DateOpen THEN 1 ELSE 0 END) OVER(partition by d.DateOpen,Location) As [Finished]
                  ,SUM(CASE WHEN j.DateClose =d.DateOpen THEN 1 ELSE 0 END) OVER(partition by d.DateOpen,Location) As [Closed]
                  ,Location
                  FROM @DateTable d
                  CROSS JOIN JOBS j   
              )
          
              Select * from cte where [Open]>0 or Finished>0 or Closed>0
              Order by DateOpen,Location
          

          【讨论】:

            【解决方案9】:

            试试这个

            WITH CTE AS
            (
            SELECT *
            FROM
            (
            SELECT Location
            FROM T
            GROUP BY Location
            ) L CROSS APPLY
            (
              SELECT DateOpen
              FROM T
              WHERE DateOpen BETWEEN '2018-12-16' AND '2018-12-18'
            ) D
            GROUP BY Location, DateOpen
            ),
             F AS
            (
            SELECT *,
                   (SELECT COUNT(1) FROM T WHERE Location = CTE.Location AND DateOpen = CTE.DateOpen) [Open],
                   (SELECT COUNT(1) FROM T WHERE Location = CTE.Location AND DateFinish = CTE.DateOpen)[Finish],
                   (SELECT COUNT(1) FROM T WHERE Location = CTE.Location AND DateClose = CTE.DateOpen) [Close]
            FROM CTE
            )
            SELECT DateOpen,
                   [Open],
                   [Finish],
                   [Close],
                   Location
            FROM F
            WHERE [Open] > 0
                  OR
                  [Finish] > 0
                  OR
                  [Close] > 0
            ORDER BY DateOpen
            

            返回:

            +---------------------+------+--------+-------+----------+
            |      DateOpen       | Open | Finish | Close | Location |
            +---------------------+------+--------+-------+----------+
            | 16/12/2018 00:00:00 |    2 |      0 |     0 | A        |
            | 16/12/2018 00:00:00 |    0 |      0 |     1 | D        |
            | 17/12/2018 00:00:00 |    1 |      0 |     0 | C        |
            | 17/12/2018 00:00:00 |    1 |      0 |     0 | E        |
            +---------------------+------+--------+-------+----------+
            

            Demo

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-11-26
              • 2013-04-27
              • 2022-12-19
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多