質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

1回答

4584閲覧

postgresqlのCONFLICT(UPSERT)のRETURNING句で更新前の値を取りたい

ms5025

総合スコア292

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2021/10/21 02:45

編集2021/10/21 04:46

前提・実現したいこと

更新前の値を取りたい

発生している問題・エラーメッセージ

postgresqlのCONFLICT(UPSERT)でテーブルを更新したのち、
更新前の値をRETURNING句で取りたく、以下のようにしましたがこのサブクエリが
複数行を返すようで、エラーが出てしまいます

INSERT INTO test VALUES ('001','hoge1', 'hage1'),('002','hoge2', 'hage2'),('003','hoge3', 'hage3') ON CONFLICT ON CONSTRAINT testsss DO UPDATE SET code = EXCLUDED.code, hoge = EXCLUDED.hoge, hage = EXCLUDED.hage, RETURNING id,                                 hage,                                  COALESCE((SELECT hoage FROM test WHERE id = id), '') AS before_hoge
ERROR: more than one row returned by a subquery used as an expression at

例えば以下のようにid決め打ち(111)で行うと、正しく表示されます

INSERT INTO test {$column} VALUES {$holder} ON CONFLICT ON CONSTRAINT testsss DO UPDATE SET code = EXCLUDED.code, hoge = EXCLUDED.hoge, hage = EXCLUDED.hage, RETURNING id, hage,                                  COALESCE((SELECT hoage FROM test WHERE id = 111), '') AS before_hoge

idに別名をつけてみたり色々しましたが正しく取得できません

postgres (PostgreSQL) 13.4

です

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

更新前の値をRETURNING句で取りたく

そもそもRETURNINGは更新の結果を取得するものです。
更新前なら、更新する前にselectしておけば良いだけですから。

また、RETURNINGは更新対象となった結果を複数行で返却するので、更新対象テーブルのデータを取得するなら、サブクエリーは不要です。

どうしてもRETURNINGで取得したいなら、更新前の項目をテーブルに追加し、DO UPDATE SETでその項目を更新するようにすれば良いでしょう。

投稿2021/10/21 03:28

編集2021/10/21 03:43
sazi

総合スコア25174

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ms5025

2021/10/21 04:25 編集

回答ありがとうございます。 このパターンだと例えば100件のUPSERTを行う場合、 いくつかはinsertでいくつかはupdateです。 もちろん、更新のなかった行もあります。 なので事前に取っておいて判定することなく、更新対象のあったRETURNINGで取りたいです。 上記 COALESCE((SELECT hoage FROM test WHERE id = id), '') AS before_hoge について、どこに間違いがあるか教えていただけないでしょうか? 欲しいデータは 更新対象となった複数のid と、その更新前hogeと、更新後のhageです。 事前に取っておくやり方以外、returnでとる方法はありますでしょうか? (テーブルに項目追加以外で)
sazi

2021/12/21 01:32 編集

サブクエリーで取得できた値は、更新前の情報でしたか?
ms5025

2021/10/21 04:26

そうです!
sazi

2021/10/21 04:31

相関指定できれば良さそうですので、以下の様にしてみてください。 COALESCE((SELECT hoage FROM test t WHERE t.id = id), '') AS before_hoge
sazi

2021/10/21 04:34

> いくつかはinsertでいくつかはupdateです。もちろん、更新のなかった行もあります。なので 対象とするデータは値を指定するんですから、事前にselectして取得できたものが更新対象だと判断できます。
ms5025

2021/10/21 04:39

相関指定しましたが、できないんです・・ 全く同じエラーです ERROR: more than one row returned by a subquery used as an expression at
sazi

2021/10/21 05:01 編集

returningに名称が付けられれば良いんですけど、そういった指定は無いようですから、insert into selectに変更してそこで、更新前値を項目にすれば、returnningで取得できるかもしれません。 多分returnningの対象はinsertに指定しているテーブルが対象だと思うので駄目な気もします。
sazi

2021/10/21 04:59 編集

values内でサブクエリーの指定が出来ればいいんですが、項目が合わなくなりますので。
sazi

2021/10/21 05:12 編集

testsssの内容はどのようなものでしょう? idをvaluesの内容に含められるなら、with式を多段的に使えば可能かもしれません。
ms5025

2021/10/21 05:26

testsssの内容はcodeとhageでユニークみたいな感じです
sazi

2021/10/21 05:32 編集

適当なコメントでは適当にしか回答出来ませんよ。 リファレンスを見ると、 > RETURNING句を使用するには、RETURNINGで使用するすべての列に対するSELECT権限が必要です。 queryを使用して問い合わせ結果を元に行を挿入する場合は当然ながら、その問い合わせ内で使われる全てのテーブルまたは列に対してSELECT権限を持っている必要があります。 とありますので、insert into selectでreturnningで更新前値は取得出来そうですね。
ms5025

2021/10/21 05:37

適当にしてるつもりはないのですが。 実テーブルの列名を書くわけにはいかないですし 実際の列数はかなりあります。 よくある、ユニーク制約としか言えないです。 質問のテーブルはあくまで一例です。
ms5025

2021/10/21 05:44 編集

すみません、 insert into selectの意味がよくわかりません。このselectは何のselectでしょうか。 上記SQLはtestテーブルに ('001','hoge1', 'hage1'),('002','hoge2', 'hage2'),('003','hoge3', 'hage3') といった 配列データ(これは非テーブルで配列群です) をtestsss 制約の元になければinsert あればupdateする という認識なのですが合ってますでしょうか?
sazi

2021/10/21 06:06 編集

> 実テーブルの列名を書くわけにはいかないですし それは良いんですが、質問がそれを整合性を付けて編集できていません。 valuseの項目がテーブルの何の項目に対比されているか分かりませんし、hoageとIDとかいきなりで出てきて、 CONFLICT使用しているのに、制約の内容も記載がありませんよね 推測するしかありませんので、回答も的外れになるかもしれないと言っています。 せめて仮のテーブルの定義位は質問に追記して下さい。
sazi

2021/10/21 07:51 編集

> insert into selectの意味がよくわかりません。このselectは何のselectでしょうか。 例示したSQLで回答しようとしたけど、上記理由で回答していません。
dameo

2021/10/22 08:07

一応docker-composeのあるlinux環境でdocker権限を持つユーザーが実行可能なshell scriptを書いてみました。 #!/bin/sh cat >docker-compose.yml <<EOF version: '3' services:  db:   image: postgres:13.4   volumes:    - ./data:/var/lib/postgresql/data   environment:    POSTGRES_PASSWORD: example   ports:    - 5432:5432 EOF docker-compose up -d sleep 5 # 適度に待つ docker-compose exec -T db bash -c "psql --user postgres" <<EOF \set AUTOCOMMIT off create table test(code varchar(100) primary key, hoge varchar(100)); insert into test values ('001', 'hoge1'),('002', 'hoge2'); commit; insert into test     values ('001', 'hage1'),('002', 'hage2'),('003','hage3')     on conflict (code) do update set         hoge = excluded.hoge     returning         code,         (select old_test.hoge from test old_test where old_test.code = test.code) ; commit; EOF docker-compose down ※全角空白2個で半角空白2個を表現しています。 私の環境では以下のような実行結果になりました。 Creating network "tmp_default" with the default driver Creating tmp_db_1 ... done CREATE TABLE INSERT 0 2 COMMIT code | hoge ------+------- 001 | hoge1 002 | hoge2 003 | (3 rows) INSERT 0 3 COMMIT Stopping tmp_db_1 ... done Removing tmp_db_1 ... done Removing network tmp_default ※tmpという名前はスクリプト実行時のカレントディレクトリがtmpなだけです 上記では更新前の値を取れているようですが、確実にそうなるものなのか、判断できないクエリですね。 ご参考までに。横から失礼しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問