ポスグレでの幾何学データ配列の使い方について質問です。

このQ&Aのポイント
  • ポスグレでの幾何学データ配列の使い方について質問です。
  • ポスグレのテーブルに幾何学データ配列を作成し、特定の座標が含まれているかを検索したい。
  • 現在のSQL文では検索できないため、正しい演算子や方法が知りたい。
回答を見る
  • ベストアンサー

ポスグレでの幾何学データ配列の使い方について質問です。

ポスグレでの幾何学データ配列の使い方について質問です。 create table test_table1 (id serial, test_lseg lseg[]); insert into test_table1(test_lseg) values (ARRAY[lseg'((0,44),(0,88))',lseg'((1,44),(1,88))',lseg'((2,44),(2,88))',lseg'((3,44),(3,88))',lseg'((4,44),(4,88))',lseg'((5,44),(5,88))'] ); という感じのテーブルを作成し、test_lsegカラムのArray内にある線分に特定の座標が含まれているかを調べるような事をしたいのですが、ARRAY内の要素を検索する際の演算子を指定する方法がよく分かりません。とりあえず以下のようなSQL文では検索できないようです。 SELECT * from test_table1 WHERE test_lseg && ARRAY[lseg'((0,80),(0,80))']; SELECT * from test_table1 WHERE lseg'((0,80),(0,80))' && any (test_lseg); test_lsegカラムのArray内にある線分に特定の座標が含まれているかが分かり、出来ればIndexが張れるようなやり方がありましたらご教授ください。

質問者が選んだベストアンサー

  • ベストアンサー
  • mitoneko
  • ベストアンサー率58% (469/798)
回答No.1

 大概のRDBMSで、サポートされ、一応使える配列型ですが、実は、SQLとは人一倍相性が悪いです。ついでに、インデックスとはもっと相性が悪いです。  そもそも、リレーショナルデータベースを学ぶ時に、テーブル設計の一番最初の方で学ぶことの一つに  「繰り返し項目を無くしましょう。」  というのがあります。  繰り返し項目の直接的な表現が配列であり、その変形が、「線分1」「線分2」「線分3」とフィールドを並べていくやり方です。両方とも、テーブルを設計する最初の段階で排除しなければならないデータ形式です。  なぜかという理由の一つが、あなたが今やろうとしている事が出来なくなるからです。  今回の事例で、線の集合が何を表すのかが今ひとつ、質問からは読み取れませんが、例えば、線の集合が一つの形を表すのだとすれば、  create table shape_tbl (id serial, note varchar(20) , primary key (id)) ;  create table line_tbl (shape_id integer, line_id serial, line_coordinates lseg              primary key(shape_id, line_id));  と構成するべきかと思います。  これなら、質問の検索は、単純に  select * from line_tbl where line_coordinates && lseg((0,80),(0,80));  で検索できますし、形に関する情報も添付する必要があるのであれば、shape_tblをshape_idをキーとして連結すればOKです。  形毎の座標を列挙するのも、shape_idで結合すれば、別に複雑な話ではありません。  インデックスも、line_tblのline_coordinatesに対してなら、問題なく張れます。(ただし、線分に含まれる座標のインデックスは・・・・無理です。線分に含まれる点の数が∞ですから。両端の座標に対してなら、幾何関数を使ってインデックスを張ればいけるかな。)  使ってはいけないのなら、なぜ、配列型をサポートするのか。ごくごくまれに、配列で表現することが素直なデータも存在するからです。でも、これを使う時には、十分な配慮と考慮が必要です。  間違っても、配列の要素をスキャンして検索するSQLなんて事は考えてはいけません。配列で表現することが素直であると宣言した限りは、そのデータは原則として一群のものとして取り扱われるはずですし、一部を扱う際には、そのための関数が用意されているはずです。演算が必要なら、その演算のための関数も用意されているべきでしょう。少なくとも、SQL文で、配列の要素にアクセスすることは無いはずです。(例えば、integer型において、各桁のどこかに1が含まれているかどうか・・・なんてSQLは書きませんよね?文字列において、部分文字列を検索する時には、専用の演算子と関数が用意されていますね?そういう事です。)  テーブル構造を変えることが、何らかの事情で出来ないのであれば、配列内の座標を扱うための一連のユーザー関数を定義するしかないと思われます。例えば、is_exist_point(座標群, 検索座標)とか言った形の関数ですね。これがあれば、配列の中に特定の座標があるかどうかが、SQLで検索できます。がやっぱり、泥縄ですね。

ledled
質問者

お礼

詳しくご回答いただきながら、レス遅くなり大変申し訳ありません。 このデータ構造の発端は、  ・1レコードに時間の範囲を複数含める。  ・検索キーを1時刻としてその範囲内に含まれているかを高速に抽出したい。  ・マスタデータは膨大なので別テーブルに範囲データを切り出したくない。  ・時間型では遅すぎるので数値系比較で高速化したい。 というのが目的でした。 手っ取り早く考えたのは、時間をintでブロック分割してarrayで持たせIndexを貼ることでしたが、時間範囲が広ければ広いほどデータ量が非効率ですから、線分利用でデータ量を抑えられないかの検討をしていて、当該検索がうまくいかなかったために質問させていただきました。 単なるC言語であればbit配列のマトリックスが一番早いのですが、SQLでは結局数値比較されるのでそう簡単に速度効率が出ず…。 RDBMSの有用性は分かりながら、速度特化させるには比較自体も単純化したいわけでその恩恵をうけられない場合はやはり泥臭くなってしまうのですね…。なんとか、DBエンジン自体の保持機能でやりたかったのですが…。 ユーザー関数を作る方向では検討していますが、それはそれで汎用さを欠くので目下変更保留中です。 ご回答ありがとうございました。

その他の回答 (1)

  • yamada59
  • ベストアンサー率74% (29/39)
回答No.2

参考 URL の幾何データ型に対する演算子を使えばできます。 SELECT * FROM test_table1 WHERE point '(0,80)' <@ ANY (test_lseg); インデックスを使うのは難しい、というかできないと思います。

参考URL:
http://www.postgresql.jp/document/8.4/html/functions-geometry.html

関連するQ&A

  • データ内の「\」や「'」を抽出するには

    データ内に「\」や「'」が含まれているデータを抽出するには SELECT * FROM table WHERE column LIKE '%\%' SELECT * FROM table WHERE column LIKE '%'%' で良いのでしょうか?

    • ベストアンサー
    • MySQL
  • 配列データに対する、要素の追加・削除方法

    PostgreSQLの配列に対して、要素の追加及び削除を行いたいのですが、下記のような動作をさせるには、※1と※2で、どのようなSQLを実行すれば良いでしょうか? よろしくお願いいたします。 >>> CREATE TABLE test ( id integer, data integer[] ); INSERT INTO test VALUES (1, '{0,1,2,3,4}'); INSERT INTO test VALUES (2, '{5,6,7,8,9}'); SELECT * FROM test; id | data ----+------------- 1 | {0,1,2,3,4} 2 | {5,6,7,8,9} (2 rows) UPDATE test [data配列に10を追加] WHERE id = 1; /* ※1 */ UPDATE test [data配列から6を除去] WHERE id = 2; /* ※2 */ SELECT * FROM test; id | data ----+------------- 1 | {0,1,2,3,4,10} 2 | {5,7,8,9} (2 rows) <<<

  • SQL文について

    テーブルの結合について教えてください。 (DBはオラクルです) select * from table1,table2 where table1.カラム名=table2.カラム名; で、結合が出来ることは分かりました。 やりたいことはテーブルの結合と、ある期間のデータを持ってきたいのです。 select カラム名 from table1 where カラム名 between '20020213' and '20020218'; で期間を決めて引き出すやり方も分かっています。 一度に結合と期間を決めて引き出すやりかたってどうすればよいですか? それとも不可能ですか? 教えてください。

  • 複数テーブルからデータを取り、updateする

    http://oshiete1.goo.ne.jp/kotaeru.php3?q=1211685 にも同じような質問があったのですが、同様の処理でうまくupdateされません。分かる方がいらっしゃいましたら教えてください。 今、テーブルが2つ(table1、table2)があり、それぞれ2つのカラム(column1、column2)を持っています。ここで、それぞれのカラム2が等しく、カラム1が指定した値のレコードのみ値をupdateしたいです。 update table1 set column1 = 'AAA' from table1,table2 where table1.column2 = table2.column2 and table1.column1 = 'BBB' and table2.column1 = 'CCC' としたところ、「from table1,table2 where 」の辺りに間違いがあると表示されます。 上の例とどう違うのか分かりません。 よろしくお願いします。m(__)m

  • 入力値と外部キーをINSERTするには

    追加したいカラムが3つあるとしまして、そのうち2つはフォームからの入力値で、残り1つは別テーブルのIDをWHEREで引っ張ってきてINSERTしたい場合、INSERT...VALUES()とINSERT...SELECT構文を組み合わせないとダメかと思うのですが、組み合わせるとうまくいきません。2つの文に分けるしかないのでしょうか? やりたいことは下の感じのSQLです。が、解釈してくれません。 INSERT INTO room(A, B, C) VALUES (1, 2, SELECT other_table.id FROM other_table WHERE other_table.id = 1");

    • ベストアンサー
    • MySQL
  • まったく別のテーブルの結果MYSQL

    table_1 id name age table_2 id book title city day SELECT * FROM table_1 WHERE id=? SELECT book,title FROM table_2 WHERE city=? このようにまったく別のテーブル カラム数も違う WHEREの?も違う これを一つのSQL文にまとめたいのですが。 UNIONなどを使ってもうまくいきませんでした。 ご教授お願いいたします。

    • ベストアンサー
    • MySQL
  • ある条件の最大値+1を初番するにはロックが必要ですか?

    以下のテーブルでcolumn毎に連番を振る場合、 テーブルロックが必要でしょうか? テーブル test id column 1 a 2 a 1 b 新規データ登録手順 1.トランザクション 2.select max(id)+1 from test where column = b for update 3.insert test into (id,column) values (selectで取得した値,b) 4.トランザクション終了 これで、column毎に登録されているIDの最大値+1で 重複せずにデータのINSERTが保障されるでしょうか?

    • ベストアンサー
    • MySQL
  • データベースの設計について

    朝からデータベースの設計について悩んでいます。 テーブルにしたいデータがあるのですが、 それぞれカテゴリーが違うデータがあります。 構造的には少ししか違わないのですが、 これらのデータを1つのテーブルとしてまとめるか、 それとも、それぞれ1つずつのテーブルにするか迷っています。 迷っている理由として: ・同時にアクセスがあった場合、全て一つのテーブルにまとめていると、障害がないか? ・全てを1つのテーブルにすると、多少は構造が違うので、必要のないフィールドが出てしまう。 それぞれを1つのテーブルで分割するということも考えたのですが、 例えば、全てのデータからある特定のデータの検索をかける場合に 不都合なのではないか?と考えてしまいます。 こういう場合には: select * from table_A where field="検索したいデータ"; select * from table_B where field="検索したいデータ"; select * from table_C where field="検索したいデータ"; とテーブルの分だけSQLを実行するしかないのでしょうか? どちらを選択しても、それぞれ一長一短のようで、混乱しています。 よろしくお願いします。

    • ベストアンサー
    • MySQL
  • 測定結果が決められた範囲内か判定するSQL文

    決められた範囲内に測定結果が入っているかを判定するSQL文(またはストアドプロシージャ)を PostgreSQL 14.0を使って作ろうとしていますが、正しい結果が得られません。 具体的には、ケーブル・テーブルとスペック・テーブルで一致するテスト項目同士(例えば、'Test_1'同士)だけを比較したいのですが、他のテスト項目も引っ掛けてしまいます。 あと、下記のwhere test_item = 'Test_1', 'Test_2', 'Test_3'の部分を select test_item from cable; から一つ一つ取り出してループで回して代入し、最終的には id | test_item | test ----+-----------+------ 1 | Test_1 | 1.5 2 | Test_1 | 1.8 2 | Test_2 | 2.5 という、まとまった出力を得たいのですが、その方法を教えていただけないでしょうか? 自作のテーブルとSQL文は以下になります: create table cable( id integer default 0 not null ,test_item varchar(30) not null ,test numeric(3, 1) ,primary key (id, test_item) ); insert into cable values(1, 'Test_1', 1.5); insert into cable values(1, 'Test_2', 4.5); insert into cable values(1, 'Test_3', 2.5); insert into cable values(2, 'Test_1', 1.8); insert into cable values(2, 'Test_2', 2.5); insert into cable values(3, 'Test_3', 2.5); create table spec( test_item varchar(30) not null , lower_spec numeric(3, 1) , upper_spec numeric(3, 1) , primary key(test_item) ); insert into spec values('Test_1', 1.0, 2.0); insert into spec values('Test_2', 2.0, 3.0); insert into spec values('Test_3', 3.0, 4.0); postgres=# select * from cable c WHERE c.test_item = test_item and test < (select upper_spec from spec where test_item = 'Test_1') and test > (select lower_spec from spec where test_item = 'Test_1'); id | test_item | test ----+-----------+------ 1 | Test_1 | 1.5 2 | Test_1 | 1.8 (2 行) postgres=# select * from cable c WHERE c.test_item = test_item and test < (select upper_spec from spec where test_item = 'Test_2') and test > (select lower_spec from spec where test_item = 'Test_2'); id | test_item | test ----+-----------+------ 1 | Test_3 | 2.5 ←'Test_3'なので引っ掛けたくない 2 | Test_2 | 2.5 3 | Test_3 | 2.5 ←'Test_3'なので引っ掛けたくない (3 行) postgres=# select * from cable c WHERE c.test_item = test_item and test < (select upper_spec from spec where test_item = 'Test_3') and test > (select lower_spec from spec where test_item = 'Test_3'); id | test_item | test ----+-----------+------ (0 行) 必要であれば補足します。 では、よろしくお願いします。

  • 日付型項目のNULLについて(PostgreSQL)

    よろしくお願いします。 下記内容のtest_tableを作成して ---- CREATE TABLE "test_table" ( "id" varchar(10) NOT NULL, "fdate" date ); ---- 下記内容のデータを登録しました。 ---- insert into test_table(id,fdate) values ('abc',NULL); ---- 下記のsqlでデータを取得できませんでした。 どうしてでしょうか? select * from test_table where fdate = NULL;