What is the most straightforward way to pad empty dates in sql results (on either mysql or perl end)?

后端 未结 9 1755
伪装坚强ぢ
伪装坚强ぢ 2020-11-22 06:12

I\'m building a quick csv from a mysql table with a query like:

select DATE(date),count(date) from table group by DATE(date) order by date asc;
9条回答
  •  花落未央
    2020-11-22 07:04

    I think the simplest general solution to the problem would be to create an Ordinal table with the highest number of rows that you need (in your case 31*3 = 93).

    CREATE TABLE IF NOT EXISTS `Ordinal` (
      `n` int(10) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`n`)
    );
    INSERT INTO `Ordinal` (`n`)
    VALUES (NULL), (NULL), (NULL); #etc
    

    Next, do a LEFT JOIN from Ordinal onto your data. Here's a simple case, getting every day in the last week:

    SELECT CURDATE() - INTERVAL `n` DAY AS `day`
    FROM `Ordinal` WHERE `n` <= 7
    ORDER BY `n` ASC
    

    The two things you would need to change about this are the starting point and the interval. I have used SET @var = 'value' syntax for clarity.

    SET @end = CURDATE() - INTERVAL DAY(CURDATE()) DAY;
    SET @begin = @end - INTERVAL 3 MONTH;
    SET @period = DATEDIFF(@end, @begin);
    
    SELECT @begin + INTERVAL (`n` + 1) DAY AS `date`
    FROM `Ordinal` WHERE `n` < @period
    ORDER BY `n` ASC;
    

    So the final code would look something like this, if you were joining to get the number of messages per day over the last three months:

    SELECT COUNT(`msg`.`id`) AS `message_count`, `ord`.`date` FROM (
        SELECT ((CURDATE() - INTERVAL DAY(CURDATE()) DAY) - INTERVAL 3 MONTH) + INTERVAL (`n` + 1) DAY AS `date`
        FROM `Ordinal`
        WHERE `n` < (DATEDIFF((CURDATE() - INTERVAL DAY(CURDATE()) DAY), ((CURDATE() - INTERVAL DAY(CURDATE()) DAY) - INTERVAL 3 MONTH)))
        ORDER BY `n` ASC
    ) AS `ord`
    LEFT JOIN `Message` AS `msg`
      ON `ord`.`date` = `msg`.`date`
    GROUP BY `ord`.`date`
    

    Tips and Comments:

    • Probably the hardest part of your query was determining the number of days to use when limiting Ordinal. By comparison, transforming that integer sequence into dates was easy.
    • You can use Ordinal for all of your uninterrupted-sequence needs. Just make sure it contains more rows than your longest sequence.
    • You can use multiple queries on Ordinal for multiple sequences, for example listing every weekday (1-5) for the past seven (1-7) weeks.
    • You could make it faster by storing dates in your Ordinal table, but it would be less flexible. This way you only need one Ordinal table, no matter how many times you use it. Still, if the speed is worth it, try the INSERT INTO ... SELECT syntax.

提交回复
热议问题