【问题标题】:Filling In The Blanks On A SQL Query填写 SQL 查询中的空白
【发布时间】:2008-12-10 09:30:19
【问题描述】:

抱歉这个模糊的主题,但我想不出该说什么。

这是我的问题,我正在对一张表进行查询,该表返回与一天相关的项目计数。我想确保如果我对数据库进行查询,我总是会得到一定数量的行。例如,假设我有下表,其中包含人们何时登录网站的日志:

**WebsiteLogin**
id: Integer
login_date: Datetime

然后我可以通过执行以下操作来获取每个日期的登录次数:

SELECT DATE(login_date), COUNT(*) FROM WebsiteLogin GROUP BY DATE(login_date)

效果很好,可以返回我想要的数据。但是想象一下,我的网站在周末很不受欢迎。返回的数据如下所示:

2008-12-10, 100
2008-12-11, 124
2008-12-12, 151
2008-12-15, 141
2008-12-16, 111

缺少 13 日和 14 日,因为这些日期没有数据。有什么方法可以更改我的查询,以便获得包含我查询的所有日期的数据。例如

2008-12-10, 100
2008-12-11, 124
2008-12-12, 151
2008-12-13, 0
2008-12-14, 0
2008-12-15, 141
2008-12-16, 111

我想如果我设置一个包含一年中所有日期的表,然后使用左/右连接,我可以做到这一点,但这样做真的很麻烦。

那么有什么线索可以在 SQL 中做到这一点吗?还是以编程方式是我唯一的选择?为任何意见干杯。

【问题讨论】:

    标签: sql mysql


    【解决方案1】:

    为此,您需要编写一个返回表结果的存储过程。

    它将使用一个循环,该循环每天都会遍历并获取计数并将其存储在临时表的一行中,然后将该表作为结果集返回。

    这是一个循环的 MS SQL 服务器示例:

    http://www.databasejournal.com/features/mssql/article.php/3100621/T-SQL-Programming-Part-2---Building-a-T-SQL-Loop.htm

    【讨论】:

      【解决方案2】:

      我想如果我设置一个包含一年中所有日期的表,然后使用左/右连接,我可以做到这一点,但这样做真的很麻烦。

      不。这就是如何做到这一点。另一方面,您可以使用临时表并仅使用所需的日期范围填充它。

      如果只有 MS SQL 有虚拟表,你提供了一个生成器函数......

      【讨论】:

        【解决方案3】:

        您不需要创建一个临时表或类似的,您只需要一个具有足够行数的源来构造缺失的日期:

        我不知道mysql,但如果它支持“连接方式”,那么你可以这样做:

        (这是在 oracle 中)

        select d login_date, count(login_date) count
        from
            websitelogin wsl
            right outer join (
                select start_date+l-1 d from (select start_date, level l
                from (select min(login_date) start_date, max(login_date)-min(login_date)+1 num_days
                from websitelogin) connect by level <= num_days)) v on d=login_date
        group by d
        /
        

        如果 mysql 没有连接,你可以只加入一些具有足够行数的任意表,并将结果限制为所需的行数:

        select d login_date, count(login_date) count
        from
            websitelogin wsl
            right outer join (select start_date+rownum-1 d from
        (
        select 
            min(login_date) start_date, 
            max(login_date)-min(login_date)+1 num_days
        from websitelogin)v,all_objects
        where rownum<=num_days
        ) v on d=login_date
        group by d
        

        虽然没有那么整洁,但显然您需要知道驾驶表中有足够的行数。

        【讨论】:

        • 很不错的做法。我喜欢它。
        【解决方案4】:

        我知道它不是 mysql,但我在 MSSQL 中使用了以下函数(MySql 版本见下文):

        CREATE FUNCTION dbo.DatesBetween (@start_date datetime, @end_date datetime)
        RETURNS @DateTable TABLE (gen_date datetime)
        AS 
        BEGIN
            DECLARE @num_dates int
            DECLARE @tmpVal TABLE (a_count int identity(0,1))
        
            SELECT @num_dates = datediff(day, @start_date, @end_date)
            WHILE (select isnull(max(a_count), 0) from @tmpVal) < @num_dates
                INSERT @tmpVal DEFAULT VALUES
        
            INSERT @DateTable (gen_date) 
            SELECT dateadd(day, a_count, @start_date) FROM @tmpVal
        
            RETURN
        END
        

        因此,要在您的示例中使用它,我会尝试以下操作:

        DECLARE @min_date datetime, @max_date datetime
        SELECT @min_date = min(login_date), @max_date = max(login_date) 
        FROM WebsiteLogin
        
        SELECT m.gen_date 'login_date', isnull(l.num_visits, 0) 'num_visits'
        FROM dbo.DatesBetween(@min_date, @max_date) as d
        LEFT OUTER JOIN (SELECT DATE(login_date) 'login_date', COUNT(*) 'num_visits'
                     FROM WebsiteLogin 
                     GROUP BY DATE(login_date)) AS l ON d.gen_date = l.login_date
        

        或者,如果我的查询大大提高了速度,您可以调查这个blog entry,它与我上面的代码一样,但适用于所有版本的 SQL。

        他在那里解释得更多,但 SQL 是:

        DECLARE @LowDate DATETIME
        SET @LowDate = '01-01-2006'
        
        DECLARE @HighDate DATETIME
        SET @HighDate = '12-31-2016'
        
        SELECT DISTINCT DATEADD(dd, Days.Row, DATEADD(mm, Months.Row, DATEADD(yy, Years.Row, @LowDate))) AS Date
        FROM
        (SELECT 0 AS Row 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
         UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14
         UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
         UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24
         UNION ALL SELECT 25 UNION ALL SELECT 26 UNION ALL SELECT 27 UNION ALL SELECT 28 UNION ALL SELECT 29
         UNION ALL SELECT 30 -- add more years here...
        ) AS Years
        INNER JOIN
        (SELECT 0 AS Row 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
         UNION ALL SELECT 10 UNION ALL SELECT 11
        ) AS Months
        ON DATEADD(mm, Months.Row,  DATEADD(yy, Years.Row, @LowDate)) <= @HighDate 
        INNER JOIN
        (SELECT 0 AS Row 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
         UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14
         UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
         UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24
         UNION ALL SELECT 25 UNION ALL SELECT 26 UNION ALL SELECT 27 UNION ALL SELECT 28 UNION ALL SELECT 29
         UNION ALL SELECT 30
        ) AS Days
        ON DATEADD(dd, Days.Row, DATEADD(mm, Months.Row,  DATEADD(yy, Years.Row, @LowDate))) <= @HighDate
        WHERE DATEADD(yy, Years.Row, @LowDate) <= @HighDate
        ORDER BY 1
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-06
          • 1970-01-01
          • 2022-11-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多