【问题标题】:MySQL: Is it possible to 'fill' a SELECT with values without a table?MySQL:是否可以用没有表的值“填充”一个 SELECT?
【发布时间】:2011-07-23 11:49:20
【问题描述】:

我需要显示每年和每月的“订单”总数。但是有几个月没有数据,但我确实想显示那个月(总值为零)。我可以为每年 12 条记录创建一个可帮助的“月份”,但有没有办法在不引入新表的情况下获得一系列月份?

类似:

SELECT [all year-month combinations between january 2000 and march 2011] 
FROM DUAL AS years_months

有人知道如何做到这一点吗?您可以使用带有某种公式的 SELECT 来动态“创建”数据吗?!

更新:

我自己发现了这个: generate days from date range

这个问题的公认答案是我正在寻找的。也许不是最简单的方法,但它可以满足我的要求:根据公式用数据填充选择......

使用过去 10 年的所有月份动态“创建”一个表:

SELECT CONCAT(MONTHNAME(datetime), ' ' , YEAR(datetime)) AS YearMonth,
       MONTH(datetime) AS Month,
       YEAR(datetime) AS Year 
FROM (
    select (curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) MONTH) as datetime
    from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
    cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
    LIMIT 120
) AS t
ORDER BY datetime ASC 

我必须承认,这是非常奇特的,但它确实有效......

我可以使用此选择将其与我的“订单”表连接起来,并获取每个月的总计,即使某个月份没有数据。

但使用“数字”或“日历”表可能是最好的选择,所以我将使用它。

【问题讨论】:

  • 在 SQL Server 中,您可以使用递归 CTE 或交叉连接 CTE 来执行此操作。我想您可以从足够大的表中进行选择并使用用户变量。
  • 你能提供更多关于你的桌子的信息吗?
  • @Jonny 基本上只有 1 个表:订单,其中包含一个日期字段。现在我需要一份从 2000 年 1 月起每个月有多少订单的清单。但有几个月,根本没有订单。但我确实希望这几个月出现在列表中。所以我需要第二张表,它包含 2000 年 1 月以来的所有月份,然后将其与订单表连接。

标签: mysql date select


【解决方案1】:

查询静态数据 MySQL。

您可以通过此查询从带有表的硬编码列表中选择静态数据

SELECT *
FROM (
    values row('Hamza','23'), row('Ali', '24')
) t1 (name, age);

【讨论】:

    【解决方案2】:

    你可以试试这样的

    select * from 
        (select 2000 as year union
         select 2001 as year union
         select 2009
        ) as years, 
        (select 1 as month union 
         select 2 as month union 
         select 3 as month union 
         select 4 as month union 
         select 5 as month union 
         select 6 as month union 
         select 7 as month union 
         select 8 as month union 
         select 9 as month
         )
        AS months 
        WHERE year between 2001 AND 2008 OR (year=2000 and month>0) OR (year = 2009 AND month < 4) 
        ORDER by year,month
    

    【讨论】:

      【解决方案3】:

      如果您使用的是 PostgreSQL,您可以结合使用 date_truncgenerate_series 来进行一些非常有趣的分组和系列生成。

      例如,您可以使用它来生成包含去年所有日期的表格:

      SELECT current_date - s.a as date 
      FROM generate_series(0,365,1) as s(a);
      

      然后,您可以使用 date_trunc 获取月份并按 date_trunc'ed 字段分组:

      SELECT date(date_trunc('month', series.date)) as month, COUNT(*) as days 
      FROM (SELECT current_date - s.a as date 
      FROM generate_series(0,365,1) as s(a)) series 
      GROUP BY month;
      

      【讨论】:

        【解决方案4】:

        我执行以下查询以在给定的时间间隔内生成月份。就我而言,它会生成从 2013 年 5 月到现在的月份列表。

        SELECT date_format(@dt:= DATE_ADD( @dt, INTERVAL 1 MONTH),'%M %Y') date_string, 
           @dt as date_full 
        FROM (SELECT @dt := DATE_SUB(CAST(DATE_FORMAT('2013-05-01' ,'%Y-%m-01') AS DATE),
           INTERVAL 1 MONTH) ) vars,
           your_tables 
        WHERE @dt<NOW()
        

        问题是,它应该与包含足够行数的表连接,以提供您预期的月份数。例如。如果您需要生成特定年份的所有月份,则需要一个至少包含 12 行的表。

        对我来说,这有点直截了当。我将它与我的配置表一起加入,包含大约 370 行。所以它可以在一年中产生几个月,或者如果我需要它在一年中产生几天。从月间隔更改为天间隔会很容易,因为我只需要将间隔从 MONTH 更改为 DAY。

        【讨论】:

          【解决方案5】:

          如果可能,请尽量避免动态生成数据。它使非常简单的查询变得异常复杂,但最重要的是:它让优化器无所适从。

          如果您需要一系列整数,请使用静态整数表。如果您需要一系列日期、月份或其他信息,请使用日历表。除非您正在处理一些真正特殊的要求,否则静态表是可行的方法。

          我在this answer 中举例说明了如何创建数字表和最小日历表(仅限日期)。

          如果您有这些表,解决您的查询将变得很容易。

          1. 将订单数据汇总到 MONTH。
          2. 右联接到月份表(或与日期表不同的 MONTH)

          【讨论】:

          • 我同意我的“解决方案”非常复杂......我想我会使用“数字”或“日历”表。很奇怪,虽然这么多年来 SQL 无法创建简单的数据系列。
          【解决方案6】:

          我会这样做:

          SELECT COUNT(Order.OrderID)
          FROM Orders
          WHERE YEAR(Order.DateOrdered) > 2000
          GROUP BY MONTH(Order.DateOrdered)
          

          这将为您提供按月分组的订单数量。 然后在您的应用程序中,只需将零分配给没有返回数据的月份

          希望对你有帮助

          【讨论】:

          • 当某月没有数据时,此方法无效。届时将不会显示该月份。
          • 我知道这就是为什么我说“那么在您的应用程序中,只需将零分配给没有返回数据的月份”。更清洁的 IMO。
          【解决方案7】:

          您绝对应该在应用程序而不是数据库层中执行此操作。只需为时间范围创建一个日期数组,然后将实际数据与您预先创建的空日期合并。见this answer to similar question

          【讨论】:

          • 我不同意...我认为在 SQL 中执行此操作要干净得多(如果可能的话)。我正在使用一个标准的 javascript 网格,它从执行查询并生成 JSON 数据的标准 php 脚本中获取数据。我不会编写单独的 php 脚本来为我的应用程序中的每个查询获取数据(我的应用程序中有数百个查询)
          • 为什么需要为每个查询编写单独的脚本?
          • 我在夸大其词。我只是说我认为使用 PHP(或其他类型的服务器端脚本)来操作您想要显示的数据并不干净。
          • 然而,无论如何您都将使用脚本语言来执行查询、对其进行 JSON 编码并输出结果。各有千秋,但在 DB 层这样做就像通过 SQL 查询构建 HTML 一样毫无意义。
          • 一点也不无意义。在我的解决方案中,我可以一遍又一遍地使用相同的脚本来显示数据。每次唯一不同的是数据库查询,我将其存储在单独的文件中。
          【解决方案8】:

          创建一个包含所有 12 个月的表(例如 tblMonths),并在其上使用 LEFT JOIN(或 RIGHT JOIN)以及您的部分源数据。

          查看the referencethis tutorial 了解其工作原理。

          【讨论】:

          • 谢谢,我知道如何加入。我只是想知道是否可以在 SQL 中使用公式动态“创建”数据,但显然这是不可能的。奇怪...
          • 当然,您可以使用虚拟表 dual 使用公式创建数据,但我不认为这是解决这个问题的最简单方法。
          【解决方案9】:

          您可以在应用程序逻辑中完成查询后填写缺失的月份。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-08-09
            • 1970-01-01
            • 2023-03-21
            • 1970-01-01
            • 2020-08-20
            • 1970-01-01
            • 2021-11-25
            相关资源
            最近更新 更多