【问题标题】:SQL query to count number of objects in each state on each daySQL查询以统计每天每个状态中的对象数
【发布时间】:2015-01-08 10:49:28
【问题描述】:

给定一组记录对象进入特定状态的日期的数据库记录,我想生成一个查询,显示在任何特定日期每个状态有多少对象。结果将用于生成趋势报告,显示每个状态下的对象数量如何随时间变化。

我有一个如下表,记录了对象进入特定状态的日期:

ObjID EntryDate  State
----- ---------- -----
    1 2014-11-01   A
    1 2014-11-04   B
    1 2014-11-06   C
    2 2014-11-01   A
    2 2014-11-03   B
    2 2014-11-10   C
    3 2014-11-03   B
    3 2014-11-08   C

有任意数量的对象和状态。

我需要生成一个查询,返回每个日期在每个状态中的对象数。结果如下所示:

Date       State Count
---------- ----- -----
2014-11-01   A       2
2014-11-01   B       0
2014-11-01   C       0
2014-11-02   A       2
2014-11-02   B       0
2014-11-02   C       0
2014-11-03   A       1
2014-11-03   B       2
2014-11-03   C       0
2014-11-04   A       0
2014-11-04   B       3
2014-11-04   C       0
2014-11-05   A       0
2014-11-05   B       3
2014-11-05   C       0
2014-11-06   A       0
2014-11-06   B       2
2014-11-06   C       1
2014-11-07   A       0
2014-11-07   B       2
2014-11-07   C       1
2014-11-08   A       0
2014-11-08   B       1
2014-11-08   C       2
2014-11-09   A       0
2014-11-09   B       1
2014-11-09   C       2
2014-11-10   A       0
2014-11-10   B       0
2014-11-10   C       3

我正在使用 Oracle 数据库。

我找不到与我的情况相匹配的示例。以下问题看起来像是在寻求类似但不同问题的解决方案:

任何可以提供的帮助或提示将不胜感激。

【问题讨论】:

  • 您的预期结果包括表格中没有的数据...这是从哪里来的?
  • 数据库记录对象进入特定状态的日期。对象保持在该状态,直到它转换到另一个状态。理想情况下,查询结果将提供每个状态中的对象数量,即使在没有发生状态转换的日期也是如此。

标签: sql oracle


【解决方案1】:
SELECT EntryDate AS "Date", State, COUNT(DISTINCT ObjectId) AS "Count" GROUP BY EntryDate, State ORDER BY EntryDate, State;

【讨论】:

    【解决方案2】:

    我将采用一种快速而肮脏的方法来获取数字。您可以选择您喜欢的方法。 . .使用递归 CTE、connect by 或数字表。因此,以下生成日期和状态的所有组合。然后它使用相关子查询来计算每个日期每个状态中的对象数量:

    with n as (
          select rownum - 1 as n
          from table t
         ),
         dates as (
          select mind + n.n
          from (select min(date) as mind, max(date) as maxd from table) t
          where mind + n.n <= maxd
         )
    select d.date, s.state,
           (select count(*)
            from (select t2.*, lead(date) over (partition by ObjId order by date) as nextdate
                  from table t2
                 ) t2
            where d.date >= t2.date and (d.date < t2.nextdate or t2.nextdate is null) and
                  d.state = t2.state
           ) as counts
    from dates d cross join
         (select distinct state from table t)
    

    【讨论】:

      【解决方案3】:

      此查询将列出每天有多少对象进入特定状态,假设每个对象每天仅更改一次状态。如果对象每天更改状态不止一次,则需要使用 count(distinct objid):

      select entrydate, state, count(objid) 
      from my_table
      group by entrydate, state
      order by entrydate, state
      

      但是,您是在询问每天有多少对象处于特定状态,因此您需要一个非常不同的查询来显示这一点。由于您在示例中仅提供该特定表,因此我将仅使用该表:

      select alldatestates.entrydate, alldatestates.state, count(statesbyday.objid)
      from
          (
          select alldates.entrydate, allstates.state
          from (select distinct entrydate from mytable) alldates,
               (select distinct state from mytable) allstates
          ) alldatestates
          left join
          (
          select alldates.entrydate, allobjs.objid, (select min(state) as state from mytable t1 
                                                where t1.objid = allobjs.objid and 
                                                      t1.entrydate = (select max(entrydate) from mytable t2 
                                                                      where t2.objid = t1.objid and
                                                                            t2.entrydate <= alldates.entrydate)) as state
          from (select distinct entrydate from mytable) alldates,
               (select distinct objid from mytable) allobjs
          ) statesbyday
          on alldatestates.entrydate = statesbyday.entrydate and alldatestates.state = statesbyday.state
      group by alldatestates.entrydate, alldatestates.state
      order by alldatestates.entrydate, alldatestates.state
      

      当然,如果您有一个包含所有可能状态的表和一个包含所有可能对象 ID 的表,则此查询会简单得多。

      另外,您可能会发现一个比这更简单的查询,但这个有效。缺点是,它很快就会成为优化器的噩梦! :)

      【讨论】:

        【解决方案4】:

        由于不是每个日期都记录每个状态,因此您需要执行 CROSS JOIN 以获得唯一状态,然后执行GROUP BY

        SELECT EntryDate, 
               C.State, 
               SUM(case when C.state = Table1.state then 1 else 0 end) as Count
        FROM Table1
        CROSS JOIN ( SELECT DISTINCT State FROM Table1) C
        GROUP BY EntryDate, C.State
        ORDER BY EntryDate
        

        【讨论】:

          【解决方案5】:

          试试这个查询:

          select EntryDate As Date, State, COUNT(ObjID) AS Count from table_name
          GROUP BY EntryDate , State 
          ORDER BY State
          

          【讨论】:

            【解决方案6】:

            你也可以用解析函数试试这个:

            Select
            Date,
            State,
            count(distinct obj) OVER (PARTITION BY EntryDate, State) count
            from table
            order by 1;
            

            【讨论】:

              【解决方案7】:

              选择 EntryDate 作为 Date、State、Count(Distinct ObjID) 作为 Count From Table_1 按 EntryDate、State 分组

              【讨论】:

              • 我理解给出这个答案。我无法理解赞成票。显然,这与 OP 所要求的完全不同。
              【解决方案8】:

              因为我比较熟悉,所以使用 SQL SERVER,但这是我目前所掌握的:

              小提琴示例(SQL SERVER,但唯一的区别应该是我认为的日期函数......):http://sqlfiddle.com/#!3/8b9748/2

              WITH zeroThruNine AS (SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9), 
              nums AS (SELECT 10*b.n+a.n AS n FROM zeroThruNine a, zeroThruNine b), 
              Dates AS (
                  SELECT DATEADD(d,n.n,(SELECT MIN(t.EntryDate) FROM @tbl t)) AS Date
                  FROM nums n
                  WHERE DATEADD(d,n.n,(SELECT MIN(t.EntryDate) FROM @tbl t))<=(SELECT MAX(t.EntryDate) FROM @tbl t)
              ), Data AS (
                  SELECT d.Date, t.ObjID, t.State, ROW_NUMBER() OVER (PARTITION BY t.ObjID, d.Date ORDER BY t.EntryDate DESC) as r
                  FROM Dates d, @tbl t
                  WHERE d.Date>=t.EntryDate
              )
              SELECT t.Date, t.State, COUNT(*)
              FROM Data t
              WHERE t.r=1
              GROUP BY t.Date, t.State
              ORDER BY t.Date, t.State
              

              首先,开始制作一个数字表(参见http://web.archive.org/web/20150411042510/http://sqlserver2000.databases.aspfaq.com/why-should-i-consider-using-an-auxiliary-numbers-table.html)以获取示例。在不同的数据库中创建数字表有不同的方法,所以我创建的前两个 WITH 表达式只是创建数字 0 到 99 的视图。我相信还有其他方法,你可能需要更多只有 100 个数字(代表您提供的第一个和最后一个日期之间的 100 个日期)

              所以,一旦你到达 Dates CTE,主要部分就是 Data CTE

              它从 Dates cte 中找到每个日期,并将其与 @tbl 表(您的表)的值与在所述日期之后记录的任何状态配对。它还以降序标记了哪些状态/每个 objid 的顺序。这样,在最终查询中,我们可以只使用 WHERE t.r=1 来获取每个 objid 每个日期的最大状态

              一个问题,这会获取所有日期的数据,即使是那些没有记录任何内容的日期,但对于零计数,它不会返回任何内容。如果你愿意,你可以离开加入这个结果并查看不同的状态,并在没有加入时取 0

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-03-07
                • 2021-04-09
                • 1970-01-01
                相关资源
                最近更新 更多