これはデータベースモデリングとPostgreSQLに関する質問です。これは post が質問に部分的に回答していますが、より技術的なもの、つまりデータを抽出するための列とクエリについてのアドバイスが必要です。
過去数年間の境界の動きを追跡できるようにするために、特定の領域内に管理境界の履歴を保存する必要があります。
境界がどのように移動するかの例を次に示します。
これが私がデータを保存する方法です:
_gid | code | name | change_date
-----+--------+-----------------+-------------
1 | 86001 | Martin Land | 2000-01-01
2 | 86002 | Bulwers Land | 2000-01-01
3 | 86002 | Martin-Bulwers | 2015-01-01
_
エリアの履歴状況、つまり最初の変更が発生する前のデフォルトの_change_date
_を_2000-01-01
_に設定しました。
それから、私は多くの異なるケースがあります:マージされていない領域、異なる日付で他のマージされた領域。これにより、次の例が生成されます。
_CREATE TEMPORARY TABLE foo AS
SELECT gid,code,area,change_date::date FROM ( VALUES
( 1,86001,'Martin Land' ,'2000-01-01' ),
( 2,86002,'Bulwers Land' ,'2000-01-01' ),
( 3,86002,'Martin-Bulwers','2015-01-01' ),
( 4,86003,'Coveral Land' ,'2000-01-01' ),
( 5,86004,'Big Tom Area' ,'2000-01-01' ),
( 6,86005,'Small Tom Area','2000-01-01' ),
( 7,86004,'Tom Land' ,'2016-01-01' )
) AS t(gid,code,area,change_date);
_
次に、指定された年の地域のリストを返すクエリを作成することが困難です。私はDISTINCT ON()
句に依存しようとしましたが、これは私が必要とする仕事ではありません...
たとえば、次のクエリは次のテーブルを返します。
_SELECT DISTINCT ON (code) code, area, change_date
FROM myTable WHERE change_date < '2016-01-01'
ORDER BY code, change_date DESC ;
-- Result:
code | area | change_date
-------+-----------------+-------------
86001 | Martin Land | 2000-01-01
86002 | Martin-Bulwers | 2015-01-01
86003 | Coveral Land | 2000-01-01
86004 | Big Tom Area | 2000-01-01
86005 | Small Tom Area | 2000-01-01
_
これは、以前のように部分的に正しいです_2016-01-01
_、ビッグトムエリアおよびスモールトムエリアは一緒にマージされませんでしたがMartin LandとBulwers Landが持っていました! 2014年には5つの分野があり、2015年には4つの分野、2016年には3つの分野がありました。
実際、次の結果が必要です。
_code | area | change_date
-------+-----------------+-------------
86002 | Martin-Bulwers | 2015-01-01
86003 | Coveral Land | 2000-01-01
86004 | Big Tom Area | 2000-01-01
86005 | Small Tom Area | 2000-01-01
_
これらの情報を保存したり、クエリを書き込んだりする別の方法はありますか?
問題は、領域がマージされてそのコードが再利用されるとその情報が得られますが、領域がマージされてそのコードが非推奨になるとそれがなくなるということです。つまり、あるエリアが廃止されたという情報がありません。
今、私は2つの解決策を見ます。まず、スキーマをそのままにして、エリアが廃止されたときはいつでも、新しいエリアであるとして新しい行を追加し、それが廃止されていることだけを示します。
INSERT INTO myTable(code, area, change_date)
VALUES(86001, 'deprecated', '2015-01-01')
したがって、クエリでは'deprecated'
と表示されるか、フィルターで除外します。
2番目の、可能な最良のオプションは、エリアが非推奨になった時期(およびその場合)を示す新しい列を用意することです。
ALTER TABLE myTable ADD deprecated DATE;
UPDATE myTable SET deprecated = '2015-01-01' WHERE gid = 1;
したがって、クエリでフィルターを追加するだけです。
(deprecated IS NULL OR deprecated >= '2016-01-01')
完全なコード:
SELECT DISTINCT ON (code) code, area, change_date
FROM myTable
WHERE change_date < '2016-01-01'
AND (deprecated IS NULL OR deprecated >= '2016-01-01')
ORDER BY code, change_date DESC;
非推奨ではない領域の別のオプションとして、NULL
の代わりに'infinity'
として設定できます。これにより、deprecated IS NULL
条件は必要ありません。'infinity'
は常に他のnull以外の値以上である。
スキーマをクリーンアップすることをお勧めします。あなたが欲しいのはこのようなものです。
WITH RECURSIVE t(gid, code, area, change_date, parent, depth) AS ( SELECT gid, code, area, change_date, null::int, 0 FROM foo WHERE change_date = '2000-01-01'::date UNION ALL
SELECT null, null, foo.area, foo.change_date, t.code, depth+1
FROM t
JOIN foo ON (
(
( foo.code BETWEEN t.code AND t.code+1 )
AND foo.gid != t.gid
)
AND foo.change_date >= t.change_date
AND foo.change_date != '2000-01-01'
)
)
SELECT * FROM t AS t1;
そのクエリを実行すると、次のような結果セットが表示されます...
gid | code | area | change_date | parent | depth
-----+-------+----------------+-------------+--------+-------
1 | 86001 | Martin Land | 2000-01-01 | | 0
2 | 86002 | Bulwers Land | 2000-01-01 | | 0
4 | 86003 | Coveral Land | 2000-01-01 | | 0
5 | 86004 | Big Tom Area | 2000-01-01 | | 0
6 | 86005 | Small Tom Area | 2000-01-01 | | 0
| | Martin-Bulwers | 2015-01-01 | 86001 | 1
| | Martin-Bulwers | 2015-01-01 | 86002 | 1
| | Tom Land | 2016-01-01 | 86003 | 1
| | Tom Land | 2016-01-01 | 86004 | 1
WHERE
句を追加するだけで、必要なもの(またはより優れたバージョン)を取得できます。
この方法にはいくつかの問題はありませんが、それらはあなたのデータに固有のものであると思います。 Tom Land
のコードはSmall Tom Area
よりも小さく、Coveral Land
よりも新しいgidの日付が新しいです。
ここでは、あなたがchange_date
/2000-01-01
であると想定していますが、nullを受け入れることができない日付列がある可能性があります。テーブルを更新して
これを実行
UPDATE foo
SET date = NULL
WHERE dates = 2000-01-01
私はこれをするでしょう
infinity
は悪い考えです:必要に応じて、再帰CTEをさらに優れたビューに変えることができます。
とにかく、より良いスキーマを作成する必要があると思いますが、infinityはここでの答えではありません。 hierarchyが必要です。