postgreSQL その25 SQL データ型 その1のつづき
postgreSQL その8 課題リストは2018/4/30時点でのオイラの所見
[P.230]
■連番
連番を表現する型は、下表のように2種類
連番
|
型名 |
サイズ |
範囲 |
|---|---|---|
|
serial |
4バイト |
-2147483648 から +2147483647 (NULLは不可) |
|
bigserial |
8バイト |
-9223372036854775808 から +9223372036854775807 (NULLは不可) |
serial型で定義された列を作成した際(つまり、テーブルの作成時やserial型の列の追加時)に、serial/bigserial型に対応するシーケンスが自動的に作成される。
serial/bigserial型に定義された列は、作成されたシーケンスから1から順番に自動的に正の整数を取得していく。
serial型として定義された列は、ユーザがデータ型の範囲を超えない値を任意に入力することもできる。ただし、自動で採番される値と重複する恐れがあり望ましくない。
[準備]
testdb=# create table serial_test (id serial,name text);
CREATE TABLE
testdb=# \d serial_test
Table "testschema.serial_test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+-----------------------------------------
id | integer | | not null | nextval('serial_test_id_seq'::regclass)
name | text | | |
[例]
testdb=# insert into serial_test(name) values ('hoge');
INSERT 0 1
testdb=# insert into serial_test(name) values ('hoge2');
INSERT 0 1
testdb=# insert into serial_test(name) values ('hoge3');
INSERT 0 1
testdb=# select * from serial_test;
id | name
----+-------
1 | hoge
2 | hoge2
3 | hoge3
(3 rows)
・serial型のidカラムに「4」を手でinser
testdb=# insert into serial_test values (4,'hoge4');
INSERT 0 1
testdb=# insert into serial_test(name) values ('hoge5');
INSERT 0 1
testdb=# select * from serial_test;
id | name
----+-------
1 | hoge
2 | hoge2
3 | hoge3
4 | hoge4 ←serial型のidカラムに「4」を手でinsertした行
4 | hoge5 ←text型のnameカラムにだけ値をinsertした行
(5 rows)
【分かったこと】
・連番は最後に自動インサートされた番号の次の番号が振られる
・連番に手動インサートした番号は自動インサートの採番では無視される
・連番に不連続な番号、既存の番号を手動インサートできる
・連番に負の整数をインサートできる
・連番に実数をインサートしたら四捨五入された整数値が挿入される
・連番に文字をインサートしたらエラーになる
・deleteで行を削除した場合、削除された行が自動採番、手動採番に関係なく、削除後の連番は最後に自動採番された番号の次の番号が振られる
■OID
OIDは、PostgreSQL独自位のデータ型で、主にオブジェクト(テーブルやインデックス、関数など)の識別に用いられるID番号。
システムテーブルやシステムビューでは、オブジェクトの名前ではなく、このOIDを使ってオブジェクトの情報が管理されている。
4バイトの数値で表現され、データベースクラスタ全体で一意の値を取る。
テーブルやインデックスに割り当てられているOIDは、pg_classなどのシステムテーブルに対し、次のようなSQLを発行することで確認できる。
OIDは隠し列であるため、SELECTの列目で指定しない限りは参照することができない。
select oid,relname from pg_class;
oid | relname
-------+-------------------------------------------
16396 | getdbsize
16406 | city
16461 | city2
16464 | city3
16467 | users
16470 | dept
16473 | tbl1
16476 | tbl2
16479 | tbl3
16519 | serial_test
・・・中略・・・
13837 | user_mapping_options
13820 | foreign_servers
13823 | _pg_foreign_tables
13830 | foreign_tables
13833 | _pg_user_mappings
13841 | user_mappings
(362 rows)
OIDは、システムテーブルやシステムビューから、特定のオブジェクトの情報を取得したい場合に用いられる。
前述のようにpg_classなどからOIDを得ることもできるが、オブジェクトの名称からOIDへ自動的に変換する便利なデータ型を使うことができる。
たとえば、テーブルやインデックスであれば、次のようにregclassというデータ型へのキャストを利用することで、テーブル名やインデックス名を自動的にOIDに変換してくれる。
testdb=# \x
Expanded display is on.
testdb=# select * from pg_class;
-[ RECORD 1 ]-------+------------------------------------------------------
relname | getdbsize
relnamespace | 16395
reltype | 16398
reloftype | 0
relowner | 10
relam | 0
relfilenode | 16396
reltablespace | 0
relpages | 0
reltuples | 0
relallvisible | 0
reltoastrelid | 0
relhasindex | f
relisshared | f
relpersistence | p
relkind | v
relnatts | 1
relchecks | 0
relhasoids | f
relhaspkey | f
relhasrules | t
relhastriggers | f
relhassubclass | f
relrowsecurity | f
relforcerowsecurity | f
relispopulated | t
relreplident | n
relispartition | f
relfrozenxid | 0
relminmxid | 0
relacl |
reloptions |
relpartbound |
-[ RECORD 2 ]-------+------------------------------------------------------
relname | city
relnamespace | 16395
reltype | 16408
reloftype | 0
relowner | 10
relam | 0
relfilenode | 16406
reltablespace | 0
relpages | 0
reltuples | 0
relallvisible | 0
reltoastrelid | 0
relhasindex | f
relisshared | f
relpersistence | p
relkind | r
relnatts | 4
relchecks | 0
relhasoids | f
relhaspkey | f
relhasrules | f
relhastriggers | f
relhassubclass | f
relrowsecurity | f
relforcerowsecurity | f
relispopulated | t
relreplident | d
relispartition | f
relfrozenxid | 565
relminmxid | 1
relacl | {postgres=arwdDxt/postgres,punisuke=arwdDxt/postgres}
reloptions |
relpartbound |
・・・後略・・・
・テーブル名を自動的にOIDに変換
testdb=# select oid,relname from pg_class where oid = 'pg_type'::regclass;
oid | relname
------+---------
1247 | pg_type
(1 row)
testdb=# select oid,relname from pg_class where oid = 'serial_test'::regclass;
oid | relname
-------+-------------
16519 | serial_test
(1 row)
・テーブル名からOIDの取得
testdb=# select oid,relname from pg_class where relname = 'serial_test';
oid | relname
-------+-------------
16519 | serial_test
(1 row)
■配列
PostgreSQLでは、配列型をサポートしている。 通常、列は単一の数値や日付データ、あるいは文字列などを格納しているが、配列はこれらの複数の値を格納することが可能。
配列型はすべてのデータ型に対して使用でき、次のように定義する。
[書式]
create table array_tbl (c1 text[], c2 int[], c3 timestamp[], ・・・ );
int[3][4] のように配列の次元数と要素数を指定できるが、実際には次元数、要素数ともに可変長となる。すなわち、int[3][4] のように要素3×4の2次元配列として宣言しても、実際には3次元以上の配列にすることもできるし、要素数を5や6に増やすこともできる。
・配列の挿入
[例]
・配列カラムをもったテーブルを作成
testdb=# create table arry_table (c1 int[]);
CREATE TABLE
testdb=# \d arry_table
Table "testschema.arry_table"
Column | Type | Collation | Nullable | Default
--------+-----------+-----------+----------+---------
c1 | integer[] | | |
・要素3個をもった1次元配列を挿入
testdb=# insert into arry_table values ('{1,2,3}');
INSERT 0 1
testdb=# select * from arry_table;
c1
---------
{1,2,3}
(1 row)
・要素4個をもった2次元配列を挿入
testdb=# insert into arry_table values ('{{4,5,6,7},{8,9,10,11}}');
INSERT 0 1
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{4,5,6,7},{8,9,10,11}}
(2 rows)
・要素2個をもった2次元配列と、要素2個持った1次元配列を同時に挿入
testdb=# insert into arry_table values ('{{12},{13}}'),('{14,15}');
INSERT 0 2
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{4,5,6,7},{8,9,10,11}}
{{12},{13}}
{14,15}
(4 rows)
・間違った配列挿入
testdb=# insert into arry_table values ('{{1,2,3},{4,5}}'); ←要素数がちぐはぐな2次元配列
ERROR: malformed array literal: "{{1,2,3},{4,5}}"
LINE 1: insert into arry_table values ('{{1,2,3},{4,5}}');
^
DETAIL: Multidimensional arrays must have sub-arrays with matching dimensions.
testdb=# insert into arry_table values ('{{1,2,3},{4,5,0}}');
INSERT 0 1
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{4,5,6,7},{8,9,10,11}}
{{12},{13}}
{14,15}
{{1,2,3},{4,5,0}}
(5 rows)
・配列の検索
[例]
・配列カラムを持ったテーブルの作成
testdb=# create table array2 (c1 text[]);
CREATE TABLE
testdb=# \d array2
Table "testschema.array2"
Column | Type | Collation | Nullable | Default
--------+--------+-----------+----------+---------
c1 | text[] | | |
・1元配列を2行挿入
testdb=# insert into array2 values ('{"AAA","BBB","CCC"}');
INSERT 0 1
testdb=# insert into array2 values ('{"XXX","YYY","ZZZ"}');
INSERT 0 1
testdb=# select * from array2;
c1
---------------
{AAA,BBB,CCC}
{XXX,YYY,ZZZ}
(2 rows)
・配列の1番目から2番目の要素を取得
testdb=# select c1[1:2] from array2;
c1
-----------
{AAA,BBB}
{XXX,YYY}
(2 rows)
testdb=# insert into array2 values ('{"lll","mmm","nnn","ooo"}');
INSERT 0 1
testdb=# insert into array2 values ('{"PPP"}');
INSERT 0 1
testdb=# select c1[1:2] from array2;
c1
-----------
{AAA,BBB}
{XXX,YYY}
{lll,mmm}
{PPP}
(4 rows)
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{4,5,6,7},{8,9,10,11}}
{{12},{13}}
{14,15}
{{1,2,3},{4,5,0}}
(5 rows)
testdb=# select c1[1:2] from arry_table;
c1
-------------------------
{1,2}
{{4,5,6,7},{8,9,10,11}}
{{12},{13}}
{14,15}
{{1,2,3},{4,5,0}}
(5 rows)
testdb=# select c1[1:2][1:2] from arry_table;
c1
---------------
{}
{{4,5},{8,9}}
{{12},{13}}
{}
{{1,2},{4,5}}
(5 rows)
testdb=# select c1[1:2][3:4] from arry_table;
c1
-----------------
{}
{{6,7},{10,11}}
{}
{}
{{3},{0}}
(5 rows)
testdb=# select c1[1][3:4] from arry_table;
c1
---------
{}
{{6,7}}
{}
{}
{{3}}
(5 rows)
testdb=# select c1[2][3:4] from arry_table;
c1
-----------------
{}
{{6,7},{10,11}}
{}
{}
{{3},{0}}
(5 rows)
・配列の2番目の要素が「YYY」の行を選択
testdb=# select * from array2;
c1
-------------------
{AAA,BBB,CCC}
{XXX,YYY,ZZZ}
{lll,mmm,nnn,ooo}
{PPP}
{PPP}
(5 rows)
testdb=# select * from array2 where c1[2] = 'YYY';
c1
---------------
{XXX,YYY,ZZZ}
(1 row)
・配列の2次元目2番目の要素が「9」の行を選択
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{4,5,6,7},{8,9,10,11}}
{{12},{13}}
{14,15}
{{1,2,3},{4,5,0}}
(5 rows)
testdb=# select * from arry_table where c1[2][2] = 9;
c1
-------------------------
{{4,5,6,7},{8,9,10,11}}
(1 row)
・配列の1次元目4番目の要素が「7」の行を選択
testdb=# select * from arry_table where c1[1][4] = 7;
c1
-------------------------
{{4,5,6,7},{8,9,10,11}}
(1 row)
・配列の更新
・第一要素が「AAA」の行の第4要素に「DDD」を追加
testdb=# select * from array2;
c1
-------------------
{AAA,BBB,CCC}
{XXX,YYY,ZZZ}
{lll,mmm,nnn,ooo}
{PPP}
{PPP}
(5 rows)
testdb=# update array2 set c1[4] = 'DDD' where c1[1] = 'AAA';
UPDATE 1
testdb=# select * from array2;
c1
-------------------
{XXX,YYY,ZZZ}
{lll,mmm,nnn,ooo}
{PPP}
{PPP}
{AAA,BBB,CCC,DDD}
(5 rows)
・第一要素が「AAA」の行の第4要素を「EEE」に更新
testdb=# update array2 set c1[4] = 'EEE' where c1[1] = 'AAA';
UPDATE 1
testdb=# select * from array2;
c1
-------------------
{XXX,YYY,ZZZ}
{lll,mmm,nnn,ooo}
{PPP}
{PPP}
{AAA,BBB,CCC,EEE}
(5 rows)
testdb=# update array2 set c1[4] = 'FFF' where 'AAA' = any(c1);
UPDATE 1
testdb=# select * from array2;
c1
-------------------
{XXX,YYY,ZZZ}
{lll,mmm,nnn,ooo}
{PPP}
{PPP}
{AAA,BBB,CCC,FFF}
(5 rows)
・1次元目が1、2次元目が4の値が7の行の1次元目が2、2次元目が4の値を12に更新
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{4,5,6,7},{8,9,10,11}}
{{12},{13}}
{14,15}
{{1,2,3},{4,5,0}}
(5 rows)
testdb=# update arry_table set c1[2][4] = 12 where c1[1][4] = 7;
UPDATE 1
testdb=# select * from arry_table;
c1
-------------------------
{1,2,3}
{{12},{13}}
{14,15}
{{1,2,3},{4,5,0}}
{{4,5,6,7},{8,9,10,12}}
(5 rows)
■NULL
NULLは、不定を表すデータ。 他の数値や文字列などと異なり、特殊な扱いをする。
・NULLは通常の演算子で比較などができない
数値や文字列には等号「=」や不等号「<、>」などの演算子を使うことができるが、NULLには適用できない。NULLであるかどうかを確認する場合は、「列名=NULL」ではなく、
「列名 IS [NOT] NULL」を用いる。
次の例では、IS NULLとIS NOT NULLを使用して検索する例
[例]
testdb=# select * from tb1;
c1 | c2 | c3 | c4
-----+-----+------------+-----
10 | BBB | 2018-05-19 | BBB
20 | | | AAA
-30 | CCC | | AAA
50 | EEE | 2018-05-19 | EEE
-40 | -40 | | AAA
(5 rows)
testdb=# select * from tb1 where c2 is null;
c1 | c2 | c3 | c4
----+----+----+-----
20 | | | AAA
(1 row)
testdb=# select * from tb1 where c3 is NULL;
c1 | c2 | c3 | c4
-----+-----+----+-----
20 | | | AAA
-30 | CCC | | AAA
-40 | -40 | | AAA
(3 rows)
testdb=# select * from tb1 where c3 is 'NULL'; ←NULLをシングルクォートで囲っちゃいけない
ERROR: syntax error at or near "'NULL'"
LINE 1: select * from tb1 where c3 is 'NULL';
testdb=# select * from tb1 where c3 is not NULL;
c1 | c2 | c3 | c4
----+-----+------------+-----
10 | BBB | 2018-05-19 | BBB
50 | EEE | 2018-05-19 | EEE
(2 rows)
・NULLを含む列のソートはNULLS FIRST/NULLS LASTを活用する
NULLは不定であるため、NULLを含む列のソートを行う場合は、NULLをどう扱うかを明示的に指定すると安全。 NULL値は、すべての非NULL値より大きい値として扱われるため、通常の昇順ソートでは、非NULL値の後に出力される。
そのため、昇順でソートしたい場合に、NULLである列を先に出力させたい場合は、
「ORDER BY 列名 NULLS FIRST」を使う。逆に、NULLである列を後に出力させたい場合は、
「ORDER BY 列名 NULLS LAST」とする。
[例]
testdb=# select * from tb1;
c1 | c2 | c3 | c4
-----+-----+------------+-----
10 | BBB | 2018-05-19 | BBB
20 | | | AAA
-30 | CCC | | AAA
50 | EEE | 2018-05-19 | EEE
-40 | -40 | | AAA
(5 rows)
testdb=# select * from tb1 order by c3 nulls first;
c1 | c2 | c3 | c4
-----+-----+------------+-----
20 | | | AAA
-30 | CCC | | AAA
-40 | -40 | | AAA
10 | BBB | 2018-05-19 | BBB
50 | EEE | 2018-05-19 | EEE
(5 rows)
testdb=# select * from tb1 order by c3;
c1 | c2 | c3 | c4
-----+-----+------------+-----
10 | BBB | 2018-05-19 | BBB
50 | EEE | 2018-05-19 | EEE
20 | | | AAA
-30 | CCC | | AAA
-40 | -40 | | AAA
(5 rows)
testdb=# select * from tb1 order by c3 nulls last;
c1 | c2 | c3 | c4
-----+-----+------------+-----
10 | BBB | 2018-05-19 | BBB
50 | EEE | 2018-05-19 | EEE
20 | | | AAA
-30 | CCC | | AAA
-40 | -40 | | AAA
(5 rows)
■キャスト
キャストとは、あるデータ型を別のデータ型に変換する処理。
文字列型の列から取得した値を数値型の値と比較する場合や、異なるデータ型の数値どうしの剰余算を行いたい場合に使用する。
キャストには下記の2つの方法がある
[書式1]
データ::変換後のデータ型
[書式2]
CAST (データ AS 変換後のデータ型)
[例]
testdb=# \d tb1
Table "testschema.tb1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+-------------
c1 | integer | | |
c2 | text | | |
c3 | date | | |
c4 | text | | | 'AAA'::text
testdb=# select * from tb1;
c1 | c2 | c3 | c4
-----+-----+------------+-----
10 | BBB | 2018-05-19 | BBB
20 | | | AAA
-30 | CCC | | AAA
50 | EEE | 2018-05-19 | EEE
-40 | -40 | | AAA
(5 rows)
testdb=# select * from tb1 where c1 = -40;
c1 | c2 | c3 | c4
-----+-----+----+-----
-40 | -40 | | AAA
(1 row)
・int型のカラムは整数演算できる
testdb=# select c1 + 1 from tb1 where c1 = -40;
?column?
----------
-39
(1 row)
・文字列型カラムは整数演算できない
testdb=# select c2 + 1 from tb1 where c1 = -40;
ERROR: operator does not exist: text + integer
LINE 1: select c2 + 1 from tb1 where c1 = -40;
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
・キャストしてみる
testdb=# select cast(c2 as int) + 1 from tb1 where c1 = -40;
?column?
----------
-39
(1 row)
testdb=# select c2::int + 1 from tb1 where c1 = -40;
?column?
----------
-39
(1 row)
testdb=# \d tb1
Table "testschema.tb1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+-------------
c1 | integer | | |
c2 | text | | |
c3 | date | | |
c4 | text | | | 'AAA'::text