76

日付とスコアの 2 つの列を持つテーブルがあります。過去 30 日間のそれぞれについて、最大 30 のエントリがあります。

date      score
-----------------
1.8.2010  19
2.8.2010  21
4.8.2010  14
7.8.2010  10
10.8.2010 14

私の問題は、いくつかの日付が欠落していることです-私は見たいです:

date      score
-----------------
1.8.2010  19
2.8.2010  21
3.8.2010  0
4.8.2010  14
5.8.2010  0
6.8.2010  0
7.8.2010  10
...

1 つのクエリから必要なのは、19,21,9,14,0,0,10,0,0,14 を取得することです。つまり、欠落している日付は 0 で埋められます。

私はすべての値を取得する方法を知っており、サーバー側の言語で日付を繰り返し、空白を逃しています。しかし、これはmysqlで実行できるので、結果を日付でソートして不足している部分を取得します。

編集: このテーブルには UserID という名前の別の列があるため、30.000 人のユーザーがいて、そのうちのいくつかはこのテーブルにスコアがあります。各ユーザーの過去 30 日間のスコアが必要なため、日付が 30 日未満の場合は毎日日付を削除します。その理由は、過去 30 日間のユーザー アクティビティのグラフを作成しており、グラフをプロットするには、30 個の値をコンマで区切る必要があるためです。したがって、クエリで USERID=10203 アクティビティを取得すると、クエリは過去 30 日間のそれぞれについて 1 つずつ、30 のスコアを取得すると言えます。私は今、より明確になったことを願っています。

4

6 に答える 6

59

MySQL には再帰機能がないため、NUMBERS テーブル トリックを使用する必要があります。

  1. 増加する数値のみを保持するテーブルを作成します - auto_increment を使用して簡単に実行できます:

    DROP TABLE IF EXISTS `example`.`numbers`;
    CREATE TABLE  `example`.`numbers` (
      `id` int(10) unsigned NOT NULL auto_increment,
       PRIMARY KEY  (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    
  2. 以下を使用してテーブルにデータを入力します。

    INSERT INTO `example`.`numbers`
      ( `id` )
    VALUES
      ( NULL )
    

    ...必要な数の値に対して。

  3. DATE_ADDを使用して日付のリストを作成し、NUMBERS.id 値に基づいて日数を増やします。"2010-06-06" と "2010-06-14" をそれぞれの開始日と終了日に置き換えます (ただし、YYYY-MM-DD という同じ形式を使用します) -

    SELECT `x`.*
      FROM (SELECT DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY)
              FROM `numbers` `n`
             WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` -1 DAY) <= '2010-06-14' ) x
    
  4. 時間部分に基づいて、データのテーブルに LEFT JOIN します。

       SELECT `x`.`ts` AS `timestamp`,
              COALESCE(`y`.`score`, 0) AS `cnt`
         FROM (SELECT DATE_FORMAT(DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY), '%m/%d/%Y') AS `ts`
                 FROM `numbers` `n`
                WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY) <= '2010-06-14') x
    LEFT JOIN TABLE `y` ON STR_TO_DATE(`y`.`date`, '%d.%m.%Y') = `x`.`ts`
    

日付形式を維持したい場合は、DATE_FORMAT 関数を使用します。

DATE_FORMAT(`x`.`ts`, '%d.%m.%Y') AS `timestamp`
于 2010-08-21T20:49:16.123 に答える
16

You can accomplish this by using a Calendar Table. That's a table which you create once and fill with a date range (e.g. one dataset for each day 2000-2050; that depends on your data). Then you can make an outer join of your table against the calendar table. If a date is missing in your table, you return 0 for the score.

于 2010-08-21T20:58:44.100 に答える
4

マイケル・コナードの答えは素晴らしいですが、15分ごとに時間が常に開始する必要がある15分の間隔が必要でした:

SELECT a.Days 
FROM (
    SELECT FROM_UNIXTIME( FLOOR( UNIX_TIMESTAMP() / (15 * 60) ) * (15 * 60)) - INTERVAL 15 * (a.a + (10 * b.a) + (100 * c.a)) MINUTE AS Days
    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
) a
WHERE a.Days >= curdate() - INTERVAL 30 DAY

これにより、現在の時刻が前のラウンドの 15 分に設定されます。

FROM_UNIXTIME( FLOOR( UNIX_TIMESTAMP() / (15 * 60) ) * (15 * 60))

そして、これは15分のステップで時間を削除します:

- INTERVAL 15 * (a.a + (10 * b.a) + (100 * c.a)) MINUTE

もっと簡単にできる方法があれば教えてください。

于 2019-05-28T02:50:32.863 に答える