一般的なアイデア
MySQL でデータを生成するには、主に 2 つの方法があります。1 つはクエリの実行時にその場でデータを生成することで、もう 1 つはデータベースにデータを保持し、必要に応じて使用することです。もちろん、クエリを頻繁に実行する場合は、最初のクエリよりも 2 番目のクエリの方が高速です。ただし、2 つ目はデータベースにテーブルを必要とし、その目的は欠落データを生成することだけです。また、そのテーブルを作成するのに十分な権限を持っている必要があります。
動的データ生成
このアプローチには、UNION
s を作成して、実際のテーブルを結合するために使用できる偽のテーブルを生成することが含まれます。ひどい繰り返しのクエリは次のとおりです。
select aDate from (
select @maxDate - interval (a.a+(10*b.a)+(100*c.a)+(1000*d.a)) day aDate 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) a, /*10 day range*/
(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) b, /*100 day range*/
(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) c, /*1000 day range*/
(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) d, /*10000 day range*/
(select @minDate := '2001-01-01', @maxDate := '2002-02-02') e
) f
where aDate between @minDate and @maxDate
とにかく、見た目よりも簡単です。派生テーブルのデカルト積を10
数値で作成するため、結果には、クエリ内の派生テーブルの量である10^X
行が含まれます。X
この例では10000
日の範囲があるため、何年にもわたる期間を表すことができます27
。さらに必要な場合はUNION
、クエリに別の値を追加して間隔を更新します。それほど多くの値が必要ない場合はUNION
、派生テーブルから s または個々の値を削除できます。明確にするために、WHERE
句 on @minDate
and@maxDate
変数を使用してフィルターを適用することにより、日付期間を微調整できます (ただし、デカルト積で作成したものよりも長い期間を使用しないでください)。
静的データの生成
このソリューションでは、データベースにテーブルを生成する必要があります。アプローチは前のものと似ています。最初にそのテーブルにデータを挿入する必要があります。必要な最大範囲は から までの整数の範囲です1
。繰り返しますが、不明な場合は、値を挿入するだけで、何年にもわたる日の範囲を作成できます。したがって、整数シーケンスを取得したら、次のように日付範囲に変換できます。X
X
100000
273
select '2012-01-01' + interval value - 1 day aDay from seq
having aDay <= '2012-01-05'
seq
という名前の列を持つ名前のテーブルを想定していますvalue
。上が開始日、下が終了日です。
これを有用なものに変える
さて、これで日付期間が生成されましたが、データを照会して欠損値を実際の値として表示する方法がまだありません0
。これがleft join
救助に来るところです。すべてが同じページにあることを確認するために、aleft join
は an に似ていますinner join
が、1 つだけ違いがあります。右側のテーブルに一致するレコードがあるかどうかに関係なく、結合の左側のテーブルからすべてのレコードが保持されます。 . つまり、inner join
は結合で一致しない行をすべて削除し、 は左側のテーブルの行left join
を保持し、右側のテーブルに一致するレコードがない左側のレコードについては、left join
その「スペース」を埋めます。null
値で。
そのため、ドメイン テーブル (「欠落している」データを含むテーブル) を新しく生成されたテーブルに結合し、後者を結合の左側に、前者を右側に配置して、すべての要素がその存在に関係なく考慮されるようにする必要があります。ドメインテーブルで。
たとえば、domainTable
フィールドを含むテーブルがあり、1 日あたりの最初の数日間ID, birthDate
のすべての数を確認したい場合、および数がその値を表示する場合は、次のクエリを実行できます。birthDate
5
2012
0
select allDays.aDay, count(dt.id) from (
select '2012-01-01' + interval value - 1 day aDay from seq
having aDay <= '2012-01-05'
) allDays
left join domainTable dt on allDays.aDay = dt.birthDate
group by allDays.aDay
これにより、必要なすべての日を含む派生テーブルが生成され (静的データ生成を使用していることに注意してください) left join
、ドメイン テーブルに対して a が実行されるため、ドメイン テーブルに一致する値があるかどうかに関係なく、すべての日が表示されます。また、値はカウントされないため、値count
を持つフィールドで実行する必要があることに注意してください。null
注意事項
1)クエリを使用して、コードに小さな変更を加えて他の間隔(月、年)をクエリできます
2) クエリできる日付とドメイン テーブルの値をハードコーディングする代わりに、次のmin
ようmax
にします。
select (select min(aDate) from domainTable) + interval value - 1 day aDay
from seq
having aDay <= (select max(aDate) from domainTable)
これにより、必要以上のレコードが生成されることを回避できます。
実際にあなたの質問に答える
自分がやりたいことをする方法をすでに理解しているはずだと思います。とにかく、他の人もそれらの恩恵を受けることができるように、ここに手順があります. まず、整数テーブルを作成します。次に、次のクエリを実行します。
select allDays.aDay, count(mt.id) aCount from (
select (select date(min(created_at)) from my_table) + interval value - 1 day aDay
from seq s
having aDay <= (select date(max(created_at)) from my_table)
) allDays
left join my_table mt on allDays.aDay = date(mt.created_at)
group by allDays.aDay
私created_at
は日時だと思うので、そのように連結しています。ただし、これは MySQL がネイティブに日付を格納する方法であるため、日付フィールドでグループ化しcreated_at
、実際のdate
データ型にキャストしています。このfiddleを使用して遊ぶことができます。
そして、データを動的に生成するソリューションは次のとおりです。
select allDays.aDay, count(mt.id) aCount from (
select @maxDate - interval a.a day aDay 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) a, /*10 day range*/
(select @minDate := (select date(min(created_at)) from my_table),
@maxDate := (select date(max(created_at)) from my_table)) e
where @maxDate - interval a.a day between @minDate and @maxDate
) allDays
left join my_table mt on allDays.aDay = date(mt.created_at)
group by allDays.aDay
ご覧のとおり、クエリの骨組みは前のものと同じです。唯一の変更点は、派生テーブルのallDays
生成方法です。これで、派生テーブルの生成方法も以前に追加したものとは少し異なります。これは、例の filddle では -day10
範囲のみが必要だったためです。1000
ご覧のとおり、日の範囲を追加するよりも読みやすいです。これは、動的ソリューションのフィドルであり、それで遊ぶこともできます。
お役に立てれば!