イースター島1で、世界で最後に残っているユニコーンを観察している多くの研究者がいます。研究者は毎日、どのユニコーンを目撃したか、目撃した日付、それぞれのユニコーンの赤ちゃんの数、目撃時に酔っていたかどうかを記録します。これらは中央の場所に個別にアップロードされ、毎日すべての新しい観測結果のフラット ファイルが送信されます。
情報を含む次のようなテーブルがあります。
create table unicorn_observations (
observer_id number not null
, unicorn_id number not null
, created date not null -- date the record was inserted into the database
, lastseen date not null -- date the record was last seen
, observation_date date not null
, no_of_babies number not null
, drunk varchar2(1) not null
, constraint pk_uo primary key ( observer_id, unicorn_id, created )
, constraint chk_uo_babies check ( no_of_babies >= 0 )
, constraint chk_uo_drunk check ( drunk in ('y','n') )
);
observer_id
テーブルは、unicorn_id
、observation_date
またはで個別に一意 lastseen
です。
時々、データの出力を管理するCobold [sic] が少し間違って、同じデータを 2 回再出力することがあります。この状況ではlastseen
、新しいレコードを作成する代わりに更新します。これは、すべての列が同じである場合にのみ行います
残念ながら、研究者は第 3 正規形を完全には認識していません。毎月、新しい観測が行われていない場合でも、いくつかのユニコーンの前月の観測をアップロードします。これはnew observation_date
で行います。つまり、新しいレコードがテーブルに挿入されます。
研究者がいくつかの観察を遅れて提出することがあるので、私は完全な追跡可能性のために別のものを持っていcreated
ます。lastseen
これらはデータベースによって作成され、提出された情報の一部ではありません。
サンプル データを次に示します (スクロール バーなしで収まるように、列名を部分的に変更しています)。
+--------+--------+----------+-----------+--------- --+---------+-------+ | | OBS_ID | UNI_ID | 作成されました | 最後に見た | OBS_DATE | #赤ちゃん | 酔っ払い | +--------+--------+----------+-----------+--------- --+---------+-------+ | | 1 | 1 | 2011 年 11 月 1 日 | 2011 年 11 月 1 日 | 2011 年 10 月 31 日 | 10 | n | | | 1 | 2 | 2011 年 11 月 1 日 | 2011 年 11 月 1 日 | 2011 年 10 月 31 日 | 10 | n | | | 1 | 3 | 2011 年 11 月 1 日 | 2011 年 11 月 1 日 | 2011 年 10 月 31 日 | 10 | n | | | 1 | 6 | 2011 年 11 月 10 日 | 2011 年 11 月 10 日 | 2011 年 11 月 7 日 | 0 | n | | | 1 | 1 | 2011 年 11 月 17 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | | | 1 | 2 | 2011 年 11 月 17 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | | | 1 | 3 | 2011 年 11 月 17 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | | | 1 | 6 | 2011 年 11 月 17 日 | 2011 年 11 月 17 日 | 2011 年 11 月 17 日 | 0 | n | | | 1 | 6 | 2011 年 12 月 1 日 | 2011 年 12 月 1 日 | 2011 年 12 月 1 日 | 0 | n | | | 1 | 6 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 3 | n | | | 1 | 6 | 2012 年 2 月 1 日 | 2012 年 2 月 1 日 | 2012 年 2 月 1 日 | 0 | n | | | 1 | 6 | 2012 年 3 月 1 日 | 2012 年 3 月 1 日 | 2012 年 3 月 1 日 | 0 | n | | | 1 | 6 | 2012 年 4 月 1 日 | 2012 年 4 月 1 日 | 2012 年 4 月 1 日 | 0 | n | | | 1 | 1 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | | | 1 | 2 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | | | 1 | 3 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | | | 1 | 6 | 2012 年 5 月 1 日 | 2012 年 5 月 1 日 | 2012 年 5 月 1 日 | 0 | n | +--------+--------+----------+-----------+--------- --+---------+-------+
これらの観測結果を部分的に非正規化して、新しいレコードが同じobserver_id
、unicorn_id
、no_of_babies
およびdrunk
(ペイロード)で受信された場合、新しいレコードを挿入する代わりに、テーブルの新しい observation_date
列を更新するようにしたいと思います。この状況でlast_observation_date
も更新します。lastseen
このテーブルに参加する複雑なユニコーン関連のクエリが多数あるため、これを行う必要があります。研究者は月に約 1000 万回、新しい日付の古い観測をアップロードし、私は月に約 900 万の真に新しい記録を受け取ります。私は 1 年間ランニングを続けており、すでに 225m のユニコーンを観測しています。各ペイロードの組み合わせの最後の観測日を知る必要があるだけなので、テーブルのサイズを大幅に縮小し、フルスキャンに多くの時間を節約したいと考えています。
これは、テーブルが次のようになることを意味します。
create table unicorn_observations (
observer_id number not null
, unicorn_id number not null
, created date not null -- date the record was inserted into the database
, lastseen date not null -- date the record was last seen
, observation_date date not null
, no_of_babies number not null
, drunk varchar2(1) not null
, last_observation_date date
, constraint pk_uo primary key ( observer_id, unicorn_id, created )
, constraint chk_uo_babies check ( no_of_babies >= 0 )
, constraint chk_uo_drunk check ( drunk in ('y','n') )
);
テーブルに格納されたデータは次のようになります。last_observation_date
観測が一度だけ「見られた」場合は、 nullかどうかは関係ありません。現在のテーブルを部分的に非正規化してこのようにするだけで、データの読み込みに助けは必要ありません。
+--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | OBS_ID | UNI_ID | 作成されました | 最後に見た | OBS_DATE | #赤ちゃん | 酔っ払い | LAST_OBS_DT | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | 1 | 6 | 2011 年 11 月 10 日 | 2011 年 12 月 1 日 | 2011 年 11 月 7 日 | 0 | n | 2011 年 12 月 1 日 | | | 1 | 1 | 2011 年 11 月 1 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | 2011 年 10 月 31 日 | | | 1 | 2 | 2011 年 11 月 1 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | 2011 年 10 月 31 日 | | | 1 | 3 | 2011 年 11 月 1 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | 2011 年 10 月 31 日 | | | 1 | 6 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 3 | n | | | | | 1 | 6 | 2012 年 2 月 1 日 | 2012 年 5 月 1 日 | 2012 年 2 月 1 日 | 0 | n | 2012 年 5 月 1 日 | | | 1 | 1 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | | | | | 1 | 2 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | | | | | 1 | 3 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | | | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+
明白な答え
select observer_id as obs_id
, unicorn_id as uni_id
, min(created) as created
, max(lastseen) as lastseen
, min(observation_date) as obs_date
, no_of_babies as "#BABIES"
, drunk
, max(observation_date) as last_obs_date
from unicorn_observations
group by observer_id
, unicorn_id
, no_of_babies
, drunk
2012 年1月1 日のユニコーン 6 の 3 つのユニコーンの赤ちゃんの単一の観察を無視するため、機能しません。これは、11 月 10 日に作成されたレコードの が正しくないことを意味しlastseen
ます。
+--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | OBS_ID | UNI_ID | 作成されました | 最後に見た | OBS_DATE | #赤ちゃん | 酔っ払い | LAST_OBS_DT | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | 1 | 1 | 2011 年 11 月 1 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | 2011 年 10 月 31 日 | | | 1 | 2 | 2011 年 11 月 1 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | 2011 年 10 月 31 日 | | | 1 | 3 | 2011 年 11 月 1 日 | 2011 年 11 月 17 日 | 2011 年 4 月 9 日 | 10 | n | 2011 年 10 月 31 日 | | | 1 | 6 | 2011 年 11 月 10 日 | 2012 年 5 月 1 日 | 2011 年 11 月 7 日 | 0 | n | 2012 年 5 月 1 日 | | | 1 | 6 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 3 | n | 2012 年 1 月 1 日 | | | 1 | 1 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | 2012 年 4 月 19 日 | | | 1 | 2 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | 2012 年 4 月 19 日 | | | 1 | 3 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 2012 年 4 月 19 日 | 7 | y | 2012 年 4 月 19 日 | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+
私は現在、いくつかの手続き型ロジック、つまりループなしでこれを行う方法を見ていません。created
225m 行のテーブルを 260 回 (個別の日付の数) フルスキャンする必要があるため、この状況でのループは避けたいと思います。lag()
andを使用してもlead()
、ユニコーンごとに不確定な量の観測があるため、再帰的である必要があります。
単一の SQL ステートメントでこのデータセットを作成する方法はありますか?
テーブル仕様とサンプル データもSQL Fiddleにあります。
より良い説明を試みました:
問題は、何かが真実だったときを維持することです。2012 年 1 月 1 日、ユニコーン 6 に 3 人の赤ちゃんが生まれました。
GROUP BY によって作成された「テーブル」で unicorn 6 だけを見てみます。1月1 日の赤ちゃんの数を調べようとすると、矛盾する 2 つのレコードが返されます。
+--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | OBS_ID | UNI_ID | 作成されました | 最後に見た | OBS_DATE | #赤ちゃん | 酔っ払い | LAST_OBS_DT | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | 1 | 6 | 2011 年 11 月 10 日 | 2012 年 5 月 1 日 | 2011 年 11 月 7 日 | 0 | n | 2012 年 5 月 1 日 | | | 1 | 6 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 3 | n | 2012 年 1 月 1 日 | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+
ただし、2 番目のテーブルのように、1 行だけが必要です。ここでは、ユニコーン 6 の赤ちゃんが 0 だった 2 つの期間が、3 だった日によって 2 つの行に分けられているため、任意の時点で最大 1 つの「正しい」値があります。
+--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | OBS_ID | UNI_ID | 作成されました | 最後に見た | OBS_DATE | #赤ちゃん | 酔っ払い | LAST_OBS_DT | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+ | | 1 | 6 | 2011 年 11 月 10 日 | 2011 年 12 月 1 日 | 2011 年 11 月 7 日 | 0 | n | 2011 年 12 月 1 日 | | | 1 | 6 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 2012 年 1 月 1 日 | 3 | n | | | | | 1 | 6 | 2012 年 2 月 1 日 | 2012 年 5 月 1 日 | 2012 年 2 月 1 日 | 0 | n | 2012 年 5 月 1 日 | +--------+--------+----------+-----------+--------- --+---------+-------+-------------+
1.モアイの周りで放牧