PostgreSQLの最近のブログ記事

CSV を読み込んで DB に登録している処理で、今は単純に INSERT を行っているのだが、それだとキーが重複したときに

Caused by: java.sql.BatchUpdateException: バッチ 0 INSERT INTO t_user_summary (
    user_id,
    user_name,
    user_address1,
    user_address2,
    user_phone_number,
    anniversary,
    note
) VALUES (
    ('1'::int8),
    ('MASA SHIMURA'),
    ('岩国市黄金町黄金1928-11'),
    (''),
    ('090-0000-1111(携帯)'),
    ('19650824'),
    ('誕生日(2回目)')
)
 はアボートしました: ERROR: 重複したキー値は一意制約"t_user_summary_pkey"違反となります
  詳細: キー (user_id, anniversary)=(1, 19650824) はすでに存在します。 このバッチの他のエラーは getNextException を呼び出すことで確認できます。

このように更新に失敗する。(このケースは、user_id が '1' で anniversary が '19650824' のデータがすでに存在している)

そこで、まず存在チェックを行って、存在していれば UPDATE、存在していなければ INSERT を行うようにしたい。
そのために、SELECT 処理を埋め込みたいが、Spring Batch の Chunk 方式の場合、どこにその処理を入れればきれいなのか?と Gemini に気いてみたら「SQL 直せばいいだけです」と言われた。

そうなん?いやあ、俺も、SQL をきちんと(専門書を買うなどして)一から勉強したことなくて・・・。長年の経験から相当複雑な SQL も組めるけど、あるステートメントのオプションなんかは知らないものもちょこちょこある。

Gemini が示した SQL の、

INSERT INTO t_user_summary (
                user_id,
                user_name,
                user_address1,
                user_address2,
                user_phone_number,
                anniversary,
                note
            ) VALUES (
                :user_id,
                :user_name,
                :user_address1,
                :user_address2,
                :user_phone_number,
                :anniversary,
                :note
            )
            ON CONFLICT (user_id, anniversary) DO UPDATE SET
                user_name = EXCLUDED.user_name,
                user_address1 = EXCLUDED.user_address1,
                user_address2 = EXCLUDED.user_address2,
                user_phone_number = EXCLUDED.user_phone_number,
                anniversary = EXCLUDED.anniversary,
                note = EXCLUDED.note

ON CONFLICT () DO もそうだし、EXCLUDED も何?って感じ(笑)

ON CONFLICT () DO は「キー重複時の動きを指定する PostgreSQL 独自の句(オプション)」なんじゃね(SQLite でも書き方は違うが使える)。EXCLUDED は「INSERT に失敗したデータが一時的に保管されている DB」を表すんだそうな。

もう、各 RDBMS 独自の構文になってくるとわけわからん(笑)。なもんで、この業界には「Oracle しか使えん」っていう Oracle 独自構文にどっぷり浸かって抜け出せない Oracle じいさんがたくさん存在してるけどな(笑)

というわけで、ItemWriter 内の SQL を上記に置き換え。

test01=> SELECT user_id, anniversary, note FROM t_user_summary WHERE
user_id=1 AND anniversary='19650824';
 user_id | anniversary |  note
---------+-------------+--------
       1 | 19650824    | 誕生日
(1 行)

が、

test01=> SELECT user_id, anniversary, note FROM t_user_summary WHERE
user_id=1 AND anniversary='19650824';
 user_id | anniversary |       note
---------+-------------+------------------
       1 | 19650824    | 誕生日(2回目)
(1 行)

と、ちゃんと更新されていてバッチグー(死語)であった。
trust 認証のままというのもよくないだろうから、postgres ユーザのパスワードを変更して scram-sha-256 認証に戻すことにする。

まず、postgres ユーザのパスワードを 'grespost' に変更(SQL Shell (psql) で接続し作業)

postgres=# ALTER ROLE postgres WITH PASSWORD 'grespost';
ALTER ROLE

次に、設定ファイルを編集。
(例)C:\Program Files\PostgreSQL\13\data\pg_hba.conf

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     scram-sha-256
# IPv4 local connections:
host    all             all             127.0.0.1/32            scram-sha-256
# IPv6 local connections:
host    all             all             ::1/128                 scram-sha-256
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     scram-sha-256
host    replication     all             127.0.0.1/32            scram-sha-256
host    replication     all             ::1/128                 scram-sha-256

## TYPE  DATABASE        USER            ADDRESS                 METHOD
#
## "local" is for Unix domain socket connections only
#local   all             all                                     trust
## IPv4 local connections:
#host    all             all             127.0.0.1/32            trust
## IPv6 local connections:
#host    all             all             ::1/128                 trust
## Allow replication connections from localhost, by a user with the
## replication privilege.
#local   replication     all                                     trust
#host    replication     all             127.0.0.1/32            trust
#host    replication     all             ::1/128                 trust


PostgreSQL を再起動する。(タスクマネージャーより)

20260717_postgres5.jpg

これで、pgAdmin でパスワード無しで postgres ユーザで DB に接続しようとすると、「fe_sendauth: no
password supplied」と怒られるようになる。

20260717_postgres6.jpg

ここで変更後の 'grespost' パスワードを入力すれば接続できる。もう面倒くさいんで Save Password にもチェックしちゃう(笑)

無事接続完了。
作業用に割り振られた PC で、いつ誰がセットアップしたのかわからない PostgreSQL 13 が動いているんだけど、当然のようにパスワードはわからない。

こういうときは定番の「強制的にログインしてしまう」方法がある。まあ、PostgreSQL 管理している人なら誰でも知ってる方法だけど、PostgreSQL の設定ファイルの認証方式を全て一旦「trust認証」に変えてしまうのである。
trust 認証は「パスワードの確認をせずに接続を許可する」認証方式(認証というのか、それ?(^^;;;)なので、これで psql でパスワードなしで PostgreSQL に接続できるようになる。

Windows だと以下の場所に設定ファイルはある。

C:\Program Files\PostgreSQL\13\data\pg_hba.conf

これの下の方に設定があるので、(本当は必要なところ(TYPE local のところ)だけ修正すればいいのだが(^^;)全部直しちゃう(笑)

# TYPE  DATABASE        USER            ADDRESS                 METHOD</div><div>#</div><div>## " local"="" is="" for="" unix="" domain="" socket="" connections="" only<="" div="">
#local   all             all                                     scram-sha-256
## IPv4 local connections:
#host    all             all             127.0.0.1/32            scram-sha-256
## IPv6 local connections:
#host    all             all             ::1/128                 scram-sha-256
## Allow replication connections from localhost, by a user with the
## replication privilege.
#local   replication     all                                     scram-sha-256
#host    replication     all             127.0.0.1/32            scram-sha-256
#host    replication     all             ::1/128                 scram-sha-256

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust

みたいに修正。

Windows であれば、サービス管理ツールから postgresql-x64-13-PostgreSQL Server 13 を停止/開始で再起動すれば OK。

その後、SQL Shell (psql) を起動したら、パスワードなしで PostgreSQL に接続できる。

20260717_postgres1.jpg

GUI ツールの pgAdmin 4 の場合は、最初に pgAdmin 自体を使えるようにするマスターパスワードの入力を促されるが(Unlock Saved Passeords 画面)、適当に 'postgres' って入れてみたら突破できた(笑)

20260717_postgres2.jpg

次に DB に接続しようとすると、Connect to Server 画面で postgres ユーザのパスワードを聞いてくる。trust 認証にする前はここを突破できなかった。

20260717_postgres3.jpg

trust 認証にしても、このパスワード入力画面は開く。ただし、Password に何も入力せず「OK」ボタンを押下すれば pgAdmin 4 画面が開く。

20260717_postgres4.jpg

実は、Oracle を使うという話だった現在取り組んでいる案件が PostgreSQL 利用に変わった。
PostgreSQL をセットアップしなきゃなあと思ってたのでちょうどよかった。バージョンは古い(13。現在の最新は 18)けど、正式な開発環境を作るまで、お試しで色々プログラム組んでみるのは 13 で十分だろう。
20260518_postgres.jpg

Windows版の psql を立ち上げると、新しいウィンドウが開き、サーバ名やデータベース名などを最初に聞かれるんだが、ここで Client Encoding で UTF8 を指定してしまい、テーブルを作成しようとして失敗し、情報も文字化けという悲しいことになった。

Server [localhost]:
Database [postgres]: rmdb
Port [5432]:
Username [postgres]: runmanager
Client Encoding [SJIS]: UTF8
ユーザー runmanager のパスワード:

psql (18.3)
"help"でヘルプを表示します。

rmdb=> CREATE TABLE t_receive_log (
rmdb(>
rmdb(>  id               int NOT NULL, -- レースID
rmdb(>  log_no           int NOT NULL, -- レース内ログ・ファイル番号
rmdb(>  log_path         varchar(1024) NOT NULL, -- ログファイルのフルパス
rmdb(>  note             varchar(128), -- ログ・ファイルの説明
rmdb(>  cdate            timestamp without time zone,
rmdb(>  udate            timestamp without time zone,
rmdb(>
rmdb(>   CONSTRAINT t_receive_log_pkey PRIMARY KEY (
rmdb(>     id, log_no
rmdb(>   )
rmdb(> );
ERROR:  invalid byte sequence for encoding "UTF8": 0x83
rmdb=> \d
                  リレーション一覧
 \x83X\x83L\x81[\x83} | \x96\xBC\x91O |     \x83^\x83C\x83v     | \x8F\x8A\x97L\x8E
----------------------+---------------+-------------------------+-------------------
 public               | m_race        | \x83e\x81[\x83u\x83\x8B | runmanager
(1 行)


そう。Windows の端末文字コードって、Windows11の今でも Shift_JIS なんやね。
まあ、俺は DB のエンコードのことかと完全に認識が間違ってたんだけど(笑)

というわけで、Client Encoding を Shift_JIS に修正してもう一度 TABLE CREATE を実行。
問題なくテーブルが作成された(まあ、CREATE文の日本語でのコメントを消せば実行できたんだけど(笑)。それは本質的な解じゃないからな(笑))

rmdb=> set client_encoding to SJIS;
SET
rmdb=> CREATE TABLE t_receive_log (
rmdb(>
rmdb(>  id               int NOT NULL, -- レースID
rmdb(>  log_no           int NOT NULL, -- レース内ログ・ファイル番号
rmdb(>  log_path         varchar(1024) NOT NULL, -- ログファイルのフルパス
rmdb(>  note             varchar(128), -- ログ・ファイルの説明
rmdb(>  cdate            timestamp without time zone,
rmdb(>  udate            timestamp without time zone,
rmdb(>
rmdb(>   CONSTRAINT t_receive_log_pkey PRIMARY KEY (
rmdb(>     id, log_no
rmdb(>   )
rmdb(> );
CREATE TABLE
rmdb=> \d
                 リレーション一覧
 スキーマ |     名前      |  タイプ  |   所有者
----------+---------------+----------+------------
 public   | m_race        | テーブル | runmanager
 public   | t_receive_log | テーブル | runmanager
(2 行)

ばっちり。
まあ、A5:SQL Mk-2 に限った話じゃないんだけど、テーブルの内容を表形式で表示した画面で、誤って一番下の行の下に新しい行を追加する状態になっちゃうことあるじゃん。

Access や SQL Server Management Studio (SSMS) なんかでもあるよね。
頻繁にとは言わないけど、結構そういうことあるじゃん。

で、急にテーブルの Not Null 項目に値を入れろ系のエラーが出てそれに気づくんだけど、問題なのが「この間違って追加状態になったレコードを簡単に消せない」ってことなんよね。

正常なレコードを誤って削除しないようにということで「わかりやすい操作では削除できない」ようにしてあるんだろうけど、それでも「空のレコードは簡単に消せろや」って思うのよね。

A5:SQL Mk-2 だと Ctrl + DELキーでレコード削除できるんだけど、色々なツール使ってると「レコード削除、なんだっけ?」と思うことも。
空のレコードでフォーカスが別のレコードに移ったら空のレコードは自動で消えるとか、その状態なら右メニューで「空のレコードの削除」とかが出てくれるとか、そういう方が「自然な操作性」だと思うんだけど、どうよ。
Homebrew をインストールしたので、早速 PostgreSQL のインストールをしてみる。

まずは、brew でインストール可能な PostgreSQL を探す

% brew search postgresql
==> Formulae
postgresql@11    postgresql@12    postgresql@13    postgresql@14    postgresql@15    postgresql@16    postgresql@17    qt-postgresql    postgrest

==> Casks
navicat-for-postgresql                                                        posture-pal

If you meant "postgresql" specifically:
postgresql breaks existing databases on upgrade without human intervention.

See a more specific version to install with:
  brew formulae | grep postgresql@

postgres だけ指定したら最新バージョンで現行システム上書くでってことかな?
それで構わないので、今回は postgres だけ指定。

% brew install postgresql
Warning: Formula postgresql was renamed to postgresql@14.
==> Downloading https://ghcr.io/v2/homebrew/core/postgresql/14/manifests/14.13_3
<略>
This formula has created a default database cluster with:
  initdb --locale=C -E UTF-8 /opt/homebrew/var/postgresql@14

To start postgresql@14 now and restart at login:
  brew services start postgresql@14
Or, if you don't want/need a background service you can just run:
  /opt/homebrew/opt/postgresql@14/bin/postgres -D /opt/homebrew/var/postgresql@14
shinoda@shinodamasanorinoMacBook-Air smart_lap % brew services list     
Name          Status User File
postgresql@14 none  

これでインストール終了。

メッセージに起動方法が書かれているので、起動してみる。

% /opt/homebrew/opt/postgresql@14/bin/postgres -D /opt/homebrew/var/postgresql@14 &
[1] 34533
shinoda@shinodamasanorinoMacBook-Air smart_lap % 2024-11-13 20:40:08.668 JST [34533] LOG:  starting PostgreSQL 14.13 (Homebrew) on aarch64-apple-darwin24.1.0, compiled by Apple clang version 16.0.0 (clang-1600.0.26.4), 64-bit
<略>

クライアントソフトの psql も実行してみる。

% psql --version
psql (PostgreSQL) 14.13 (Homebrew)

うむ。ちゃんとインストールされているな。

ちなみに、Homebrew でインストールした場合は、brew services コマンドでの起動もできるようだ。

起動して、すぐに停止してみる。

% brew services start postgresql
Warning: Formula postgresql was renamed to postgresql@14.
==> Successfully started `postgresql@14` (label: homebrew.mxcl.postgresql@14)
% brew services stop postgresql
Warning: Formula postgresql was renamed to postgresql@14.
Stopping `postgresql@14`... (might take a while)
==> Successfully stopped `postgresql@14` (label: homebrew.mxcl.postgresql@14)

問題なし。
さて、続いて PostgreSQL の設定をするか。
俺も「軽い」macOS ユーザなので、アプリのインストールは DMG ファイルから行ったり、ZIP ファイルを解凍してアプリケーションフォルダにコピーしたり、そういうやり方しか知らなかった。パッケージマネージャなど使ったことがなかった。

しかし、DBMS(今回入れるのは PostgreSQL)のインストールにはパッケージマネージャから入れておいたほうが後々バージョンアップとか楽だということなので、今回 Homebrew を入れてみた。

とりあえずターミナル上で、brew を実行してみると、

% brew --version
zsh: command not found: brew

とエラーが。うむ。新規インストールせねば。

Homebrew の公式サイトに載っている手順通りに実施。
サイトから curl で最新のインストーラを取ってきて実行。

% /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
==> Checking for `sudo` access (which may request your password)...
Password:<macOS ユーザのパスワード>
==> This script will install:
/opt/homebrew/bin/brew
/opt/homebrew/share/doc/homebrew
/opt/homebrew/share/man/man1/brew.1
<以下略>

これだけでインストールは終了。

ただ、これだけだと brew のパスが通ってないので、ターミナルに出力されているインストールメッセージの Next Step に書かれているコマンドを実行しろと言われる。

Warning: /opt/homebrew/bin is not in your PATH.
  Instructions on how to configure your shell for Homebrew
  can be found in the 'Next steps' section below.

言われたとおりに実行してみる。

% echo >> /Users/hogehoge/.zprofile
% echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/hogehoge/.zprofile
% eval "$(/opt/homebrew/bin/brew shellenv)"

その後、brew を実行してみる。

% brew --version
Homebrew 4.4.4

バッチリ(笑)
20241107_sql_error.jpg

PostgreSQL でカラム(列)追加するときって、

ALTER TABLE table1 ADD col1 CHARACTER VARYING(10)

で OK なんだが、ADD の後ろに COLUMN って付けたらダメじゃったんかいねえ?

AS:SQL Mk-2 の SQL 窓で

ALTER TABLE table1 ADD COLUMN col1 CHARACTER VARYING(10)

ってすると、「キーワード 'COLUMN' 付近に不適切な構文があります」ってエラーになっちゃう。

久しぶりなんで記憶が今一つ曖昧だけど・・・

仕事で SQLServer、MySQL、PostgreSQL を使っているんだが、最近は MySQL が多かったんで記憶がごっちゃになってたけど、PostgreSQL では ADD COLUMN はエラーだけど、MySQL は ADD だけでも ADD COLUMN でもどっちでもいいんやな。

もう、すっかり ADD COLUMN って打つのが癖になってたわ。
つーか、PostgreSQL も ADD COLUMN くらい許せよ(^^;;;
うーん・・・

LibreOffice の Python マクロで FeliCa カードの連続読み込みができない。

こういうソース(無限ループしてるけど、テストで break 処理書くのが面倒くさかっただけなんで(^^;;)をマクロ登録して、CardRead 関数を実行する。

# -*- coding: utf-8 -*-
# LibreOffice 用マクロ

import uno

def CardRead(*args):

    import nfc
    from nfc.clf import RemoteTarget

    def on_connect(tag):
        doc = XSCRIPTCONTEXT.getDocument()
        sheet = doc.getSheets().getByName('CardMst')
        cell = sheet.getCellByPosition(1,1)
        cell.String = str(tag)

    def main():
        while True:
            with nfc.ContactlessFrontend('usb') as clf:
                clf.connect(rdwr={'on-connect': on_connect})

    main()

そうすると、一枚目はちゃんと読み込んでシートの左上に ID が表示されるんだけど、二枚目のカードを読むと

com.sun.star.uno.RuntimeException: Error during invoking function CardRead in module file:///C:/Program%20Files/LibreOffice/share/Scripts/python/nfc_get_card_info.py (<class 'usb1.USBErrorAccess'>: LIBUSB_ERROR_ACCESS [-3]
  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 907, in invoke
    ret = self.func( *args )
<略>
  File "C:\Users\hoge\AppData\Roaming\Python\Python35\site-packages\usb1\__init__.py", line 125, in raiseUSBError
    raise __STATUS_TO_EXCEPTION_DICT.get(value, __USBError)(value)
)

こんなエラーが出ちゃう。これ以降、一切カードは読めない。

どうも、権限の無いアクセスを USB デバイスにしたってことらしい(ちょっとググったが英語のページしかヒットしなかったので意訳(^^; 違ってたら正解を教えてくれナンス>識者の皆様)

ちなみに、このスクリプトをマクロではなく普通の Python スクリプトとして実行すると、そういうエラーは出ない。マクロ独自の症状のようだ。

結局、どうしてもこのエラーを解決できなかったので、外部の Python スクリプトとして実行しておいて、カード情報を読み込んだら内容を CSV ファイルに吐いて、LibreOffice Calc からその CSV ファイルを一定時間毎に読み込む形にしようと思ってるところだけど、「お前、馬鹿だなあ。こう直したら一発で解決だべ」って情報をお持ちの識者の方がいらっしゃったら、ぜひご教示くださいませ。
昨夜、お客さんの DB データのメンテナンスをした。最新情報を CSV ファイルで欲しいということだったので、COPY コマンドでエクスポートしようとしたんだけど、

hogedb=# COPY m_hoge TO '/tmp/m_hoge_20180511.csv' WITH CSV DELIMITER ',' NULL AS '' HEADER FORCE QUOTE *;
ERROR:  syntax error at or near "*"
LINE 1: ...csv' WITH CSV DELIMITER ',' NULL AS '' HEADER FORCE QUOTE *;

て具合にエラーになる。FORCE QUOTE のワイルドカード文字 '*' が引っかかっているようだ。

うーん、なんで?
全カラムをダブルクォーテーションで囲むのであれば、FORCE QUOTE * じゃなかったっけ?

取り敢えず、

hogedb=# COPY m_hoge TO '/tmp/m_hoge_20180511.csv' WITH CSV DELIMITER ',' NULL AS '' HEADER FORCE QUOTE id,name,kubun,hoge1,hoge2,note,cdate,udate;
COPY 84

みたいに全項目名を書けば上手くいったけど、項目数が何十ってあるテーブルだってあるわけで、いちいちそれを羅列するの?・・・って話だなあ。

なんでワイルドカード使えんのやろ?

バージョンは PostgreSQL 8.4.20 でちょっと古いんだけど、8 の時ってワイルドカード、使えんかったっけ?

確かに、PostgreSQL 9.0.4 だったらエラーにならないなあ。

このアーカイブについて

このページには、過去に書かれたブログ記事のうちPostgreSQLカテゴリに属しているものが含まれています。

前のカテゴリはMySQLです。

次のカテゴリはSQLです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

月別 アーカイブ

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら