MAGAZINE

ルーターマガジン

インフラ/運用

PostgreSQLのスキーマ作成とsearch_path設定の実践例(MySQLユーザー向け)

2024.11.18
Pocket

ルーターエンジニアのtoyamaです。今回はポスグレのスキーマについてです。
ずっとMySQLを使ってきたため、PostgreSQLのスキーマについてあまり理解せずつかっていたのですが、ようやく理解できてきたので情報をまとめておきたいと思います。
スキーマとは、データベースの中にさらにグループを作ることができる機能で、異なるスキーマの下に同じ名前のテーブルなどをつくることができます。
以下、実例を交えながら説明していきます。

データベースの作成

まず例としてtest_dbというデータベースを作成してみます。

$ psql -U postgres
postgres=# CREATE DATABASE test_db;

作成したら早速、test_dbに接続して、スキーマ一覧を確認しましょう。
\cでデータベースの切り替え、\dnで現在接続しているデータベースのスキーマをみれます。

postgres=# \c test_db
test_db=# \dn
      List of schemas
  Name  |       Owner       
--------+-------------------
 public | pg_database_owner
(1 row)

スキーマは特に作成していないのにpublicというスキーマが作成されています。
これはデータベース作成時にスキーマを指定しない場合デフォルトで作成されるスキーマです。

スキーマの作成

続いてtest_dbにtest_schemaという名前のスキーマを作成してみましょう。
ここでスキーマはデータベースの下に作られることに注意してください。
ポスグレへの接続時、データベースを指定しない場合、postgresというデータベースに接続します。
postgeesに接続した状態でスキーマを作成すると、postgresデータベースの下にスキーマが作られてしまいます。
なので、DBを指定してログインするか、ログイン後にDBを切り替えましょう。

$ psql -U postgres -d test_db
または
$ psql -U postgres
postgres=# \c test_db

test_db=# CREATE SCHEMA test_schema;
CREATE SCHEMA
test_db=# \dn
         List of schemas
    Name     |       Owner       
-------------+-------------------
 public      | pg_database_owner
 test_schema | postgres

無事test_schemが作成されました。
では続いて、idのみのテストテーブルを作成してみましょう。

test_db=# CREATE TABLE test_table (id bigint);
CREATE TABLE
test_db=# \dt
           List of relations
 Schema |    Name    | Type  |  Owner   
--------+------------+-------+----------
 public | test_table | table | postgres

publicスキーマにtest_tableが作成されました。
ではtest_schemaにテーブルを作成する場合はどうすればよいでしょうか?
簡単なやり方として主に2つあります。

  1. 修飾語を使う
    テーブル名の前に「スキーマ名.」をつけることでスキーマを指定できます。 MySQLでデータベースを指定するときに使用していたやり方ですね。

    test_db=# CREATE TABLE test_schema.test_table (id bigint);
    CREATE TABLE
    test_db=# \dt test_schema.*
                 List of relations
      Schema    |    Name    | Type  |  Owner   
    -------------+------------+-------+----------
    test_schema | test_table | table | postgres
    (1 row)
  2. search_pathを設定する
    search_pathとは、1つ目の方法でスキーマを指定しなかったときに接続するスキーマを決定する設定値です。 現在のsearch_pathはshow search_path;で確認できます。
    search_pathのデフォルト値は"$user", publicで”$user”は現行ユーザを表します。psql -U postgresの場合は$userの値はpostgresになります。
    下の場合、クエリ中に修飾語による指定がない場合、postgresスキーマ、publicスキーマの順に評価されていきます。このため最初にテーブルを作成したときにpostgresスキーマはないため、publicスキーマにテーブルが作成されたのです。

    test_db=# show search_path;
      search_path   
    -----------------
    "$user", public
    (1 row)

    search_pathはset search_path TO スキーマ名1,スキーマ名2…;という形で変更できます。これはセッション終了とともにデフォルトにもどります。

    test_db=# set search_path = test_schema;
    SET
    test_db=# show search_path;
    search_path 
    -------------
    test_schema
    (1 row)
    
    # 再ログイン
    test_db=# \q
    test_db=# show search_path;
      search_path   
    -----------------
    "$user", public
    (1 row)

    この設定を行ったセッション内であれば、修飾語がなくてもtest_shemaスキーマに対してテーブル作成などを行うことができます。

    test_db=# CREATE TABLE test_table (id bigint);
    CREATE TABLE
    test_db=# \dt 
                 List of relations
      Schema    |    Name    | Type  |  Owner   
    -------------+------------+-------+----------
    test_schema | test_table | table | postgres
    (1 row)

実験

test_schema,publicの2つのスキーマにtest_tableがある状態ですが、ここでsearch_pathに両方を指定した場合どんな挙動になるか調べてみました。

test_db=# set search_path = test_schema,public;
SET
test_db=# \dt
              List of relations
   Schema    |    Name    | Type  |  Owner   
-------------+------------+-------+----------
 test_schema | test_table | table | postgres
(1 row)

test_db=# show search_path;
     search_path     
---------------------
 test_schema, public
(1 row)

test_db=# set search_path = public,test_schema;
SET
test_db=# \dt
           List of relations
 Schema |    Name    | Type  |  Owner   
--------+------------+-------+----------
 public | test_table | table | postgres
(1 row)

同じテーブルが存在する場合、search_pathの最初で指定されたスキーマのテーブルで評価されていることがわかります。 2つのテーブルを1つのクエリで指定したいときは、修飾語を使わないといけないようです。

test_db=# select * from test_table
join test_schema.test_table 
on public.test_table.id = test_schema.test_table.id
;
 id | id 
----+----
(0 rows)

search_pathの初期値の変更方法

  1. postgresql.cnfで設定する サーバーの設定ファイルで設定できますが、すべてのDBで設定されてしまうのでおすすめできないです。

    #search_path = '"$user",public' # schema names
  2. ALTER DATABASEで設定する ALTER DATABASE文でそのデータベースに対して、search_pathのデフォルトを設定できます。 もしschemaを複数作った場合は、以下のように「,」で並列に設定できます。

    test_db=# ALTER DATABASE test_db set search_path = test_schema,public;
    ALTER DATABASE
    
    # 一度ログアウト
    test_db=# \q
    $ psql -U postgres -d test_db
    test_db=# show search_path;
        search_path     
    ---------------------
    test_schema, public
    (1 row)
    
    # 設定値の確認
    test_db=# select pd.datname, pdrs.setconfig from pg_catalog.pg_db_role_setting pdrs 
    join pg_catalog.pg_database pd 
    on pdrs.setdatabase = pd.oid
    ;
    datname |              setconfig              
    ---------+-------------------------------------
    test_db | {"search_path=test_schema, public"}
    (1 row)

まとめ

MySQLと同じイメージで使うなら、特にスキーマは作成せず、デフォルトのpublicを使うのが一番しっくりきそうです。
もし自分でスキーマを作成してポスグレを使うなら、修飾語やsearch_pathの認識は必須ですね。
この記事でポスグレのスキーマのイメージを掴んでくれれば幸いです。

Pocket

CONTACT

お問い合わせ・ご依頼はこちらから