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

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

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

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

Q&A

解決済

4回答

11941閲覧

[PostgreSQL]更新順序を指定するUPDATE処理について

mikan_s4n

総合スコア377

PostgreSQL

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

0グッド

1クリップ

投稿2018/08/27 05:54

編集2018/08/27 08:14

Postgresql での質問です。
以下の変更情報TBLをもとにお客様TBLを更新する処理を行いたいと考えています。
変更情報テーブルは必ずしも変更日の順序で入っているわけではないため、
更新の順序を指定できなければ、変更後にID1001,連番1,カード番号1001001
とならなくなる可能性があります。

記載のSQLのままだと更新順序(ORDER BYのような)指定が出来ていないため、2018/08/01のデータで更新がされてしまう可能性があるのではと思っています。
MySQLだとUPDATEにORDER BYが使えるとの記述があったのですが、PostgreSQLのため難航中です。

SQLのサンプルや実装方法の提案等頂けたら助かります。

変更情報TBL

ID連番カード番号旧ID旧連番旧カード番号変更日
10011100100310012100100032018/08/20 12:00:00
10012100100300013000100012018/08/01 11:00:00
10055100100500054000100102018/08/20 13:00:00

修正)
2列目旧連番3→連番2 ⇒ 1列目旧連番2(実態は2列目の連番2)→連番1
変更日で見た場合、3→2 2→1 の様になるため、更新順序の指定が出来るのであれば、
変更日で順序を指定し、お客様TBLの2列目が「1001」「1」「1001003」
と出来ないかと考えています。
この場合は変更日が最も新しいモノという条件では追えなくなるため、
全レコードを順に更新しなければなりません。

お客様TBL

ID連番カード番号性別名前
000110001001M太郎
000130001001M太郎
000540001010F花子

lang

1UPDATE customer SET 2 customer.ID = changes.ID 3 ,customer.連番 = changes.連番 4 ,customer.カード番号 = changes.カード番号 5FROM お客様TBL AS customer 6INNER JOIN 変更情報TBL AS changes ON 7 customer.カード番号 = changes.旧カード番号 AND 8 customer.連番 = changes.旧連番;

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

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

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

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

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

guest

回答4

0

変更情報テーブルが変更対象のキーの変遷であり、全ての変遷の初めと終わりを見たときに整合性が担保(キー重複が発生しない)という条件なら、WITH RECURSIVEを使用すれば開始から終端までを1行に纏めることができるので、それを元に更新すれば良いかと思います。

投稿2018/08/27 14:26

sazi

総合スコア25184

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

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

0

ベストアンサー

変更情報TBLの結合条件orWHERE句で、「旧連番、旧カード番号が同じデータの中で最大の変更日」のデータを指定する条件を追加すると、旧連番が3旧カード番号が00010001のデータも変更日2018/08/20のデータで更新することができます。

SQL

1INNER JOIN 変更情報TBL AS changes ON 2 customer.カード番号 = changes.旧カード番号 AND 3 customer.連番 = changes.旧連番 4 changes.変更日 = (「旧連番、旧カード番号が同じデータの中で最大の変更日」を取得するサブクエリ);

サブクエリの書き方は色々とあると思うので、ご自身で考えてみて下さい。


↓↓↓追記↓↓↓
sazi様の回答にあるようにWITH RECURSIVEを使用する方法で解決できそうです。

SQL

1WITH RECURSIVE OKYAKUSAMA AS ( 2 SELECT '0001' AS ID, 1 AS RENNBANN, '0001001' AS CARDNO 3 UNION 4 SELECT '0001' AS ID, 3 AS RENNBANN, '0001001' AS CARDNO 5 UNION 6 SELECT '0005' AS ID, 4 AS RENNBANN, '0001010' AS CARDNO 7) 8, HENNKOU AS ( 9 SELECT '1001' AS ID, 1 AS RENNBANN, '1001003' AS CARDNO, '1001' AS KYUUID, 2 AS KYUURENNBANN, '1001003' AS KYUUCARDNO, TO_TIMESTAMP('2018/08/20 12:00:00', 'YYYY/MM/DD HH24:MI:SS') AS HENKOUBI 10 UNION 11 SELECT '1001' AS ID, 2 AS RENNBANN, '1001003' AS CARDNO, '0001' AS KYUUID, 3 AS KYUURENNBANN, '0001001' AS KYUUCARDNO, TO_TIMESTAMP('2018/08/01 11:00:00', 'YYYY/MM/DD HH24:MI:SS') AS HENKOUBI 12 UNION 13 SELECT '1005' AS ID, 5 AS RENNBANN, '1001005' AS CARDNO, '0005' AS KYUUID, 4 AS KYUURENNBANN, '0001010' AS KYUUCARDNO, TO_TIMESTAMP('2018/08/20 13:00:00', 'YYYY/MM/DD HH24:MI:SS') AS HENKOUBI 14) 15, TAISYO(ID, RENNBANN, CARDNO, MOTOID, MOTORENNBANN, MOTOCARDNO, DEPTH) AS ( 16 SELECT CHANGES.ID, CHANGES.RENNBANN, CHANGES.CARDNO, CUSTOMER.ID AS MOTOID, CUSTOMER.RENNBANN AS MOTORENNBANN, CUSTOMER.CARDNO AS MOTOCARDNO, 1 AS DEPTH 17 FROM OKYAKUSAMA AS CUSTOMER 18 INNER JOIN HENNKOU AS CHANGES 19 ON CUSTOMER.ID = CHANGES.KYUUID 20 AND CUSTOMER.RENNBANN = CHANGES.KYUURENNBANN 21 AND CUSTOMER.CARDNO = CHANGES.KYUUCARDNO 22 UNION ALL 23 SELECT CHANGES.ID, CHANGES.RENNBANN, CHANGES.CARDNO, TAISYO.MOTOID, TAISYO.MOTORENNBANN, TAISYO.MOTOCARDNO, DEPTH + 1 24 FROM HENNKOU AS CHANGES, TAISYO 25 WHERE TAISYO.ID = CHANGES.KYUUID 26 AND TAISYO.RENNBANN = CHANGES.KYUURENNBANN 27 AND TAISYO.CARDNO = CHANGES.KYUUCARDNO 28) 29SELECT SUB_TAISYO.ID, SUB_TAISYO.RENNBANN, SUB_TAISYO.CARDNO 30FROM OKYAKUSAMA, ( 31 SELECT SUB.* 32 FROM (SELECT TAISYO.*, MAX(TAISYO.DEPTH) OVER(PARTITION BY TAISYO.MOTOID, TAISYO.MOTORENNBANN, TAISYO.MOTOCARDNO) MAX_DEPTH FROM TAISYO) SUB 33 WHERE SUB.DEPTH = SUB.MAX_DEPTH 34 ) SUB_TAISYO 35WHERE OKYAKUSAMA.ID = SUB_TAISYO.MOTOID 36 AND OKYAKUSAMA.RENNBANN = SUB_TAISYO.MOTORENNBANN 37 AND OKYAKUSAMA.CARDNO = SUB_TAISYO.MOTOCARDNO

WITH句のOKYAKUSAMAとHENNKOUはお客様TBLと変更情報TBLをインラインで表現しているだけなので読み飛ばして下さい。(当方の環境にテーブル作成するのが面倒であったので了承下さい。。)
TAISYOの部分がWITH RECURSIVEの構文を使用している部分です。
まず、非再帰的表現部分でお客様TBLのキーと変更情報TBLの旧キーが同じデータを抽出しています。ここで、お客様情報のキーを元キー(MOTOID, MOTORENNBANN, MOTOCARDNO)として保持しています。さらに、深さを表すDEPTHを1として定義しています。
再帰的表現部分では、非再帰的表現部分で取得できた結果の変更情報TBLのキーが変更情報TBLの旧キーと同じデータを抽出しており、これを繰り返します。繰り返すたびDEPTHは加算されます。
その結果は以下のようになります。

SQLの結果

1ID;RENNBANN;CARDNO;MOTOID;MOTORENNBANN;MOTOCARDNO;DEPTH 2"1001";2;"1001003";"0001";3;"0001001";1 3"1001";1;"1001003";"0001";3;"0001001";2 4"1005";5;"1001005";"0005";4;"0001010";1

WITH以降のSELECT句でこの結果からお客様TBLのキーと元キーが同じ、かつ、深さが最大のデータを取得すると更新対象とその更新値が取得できます。

他にもやり方はあるかと思いますが、私のつたないSQLではこれが限界でした。。。
わかりづらい部分が多いと思いますし、やり方はこれだけではないはずなので、ご自身に合った方法を見つけて課題解決下さい。

投稿2018/08/27 06:16

編集2018/08/28 06:39
SE-studying-now

総合スコア351

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

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

mikan_s4n

2018/08/27 08:20

回答頂いてからで非常に申し訳ないのですが、変更情報TBLのデータに認識違いがあり、修正致しました。 丁寧に書いていただき、ありがとうございます。
mikan_s4n

2018/09/03 00:10

sazi様の回答から理解しやすいように例を作って下さりありがとうございます。 再帰的処理はなかなかイメージが掴めなかったのでとても助かりました。 sazi様とどちらをBAにするかは迷いましたが、よりイメージのしやすいこちらをBAにさせてもらいます。 ありがとうございました。
SE-studying-now

2018/09/03 01:42

解決してよかったです。 BAに選んで頂きありがとうございます! 私も勉強になった次第です!お互い頑張りましょう!
guest

0

一括で update するのではなく、変更情報TBL を逐次処理しなければならない、のでは?

マニュアルの V サーバプログラミング の、SQL 手続き言語とかカーソルとかをご確認ください。

投稿2018/08/27 06:14

tacsheaven

総合スコア13703

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

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

mikan_s4n

2018/08/27 08:18

仰る通り、変更情報テーブルを1レコードずつ逐次処理していくのが確実だとは思います。 単一のSQLで更新順序を指定すれば逐次処理のようなことが出来ないかと思ったのですが、指摘のように手続き言語等で制御するのがよいのですかね……。
guest

0

基本的には「余計な情報は省いたデータでupdateする」ことになるかと。
つまり「旧連番ごとに最終変更日を持つ行を求める」という作業を先に行います。
やり方は結構いろいろあります。

・「ウィンドウ関数」を使う
・「join lateral」を使う
stackoverflow

他には未検証ですけど

「同じ旧連番同士で自分の変更日よりあとの変更日を持たない」データを検索

sql

1select * 2from 変更情報TBL as A 3where not exists( 4 select 0 5 from 変更情報TBL as B 6 where B.旧連番=A.旧連番 7 and B.変更日>A.変更日 8)

あとは「同じ旧連番同士で最大の変更日と同じ変更日」のデータを検索などすればいいかと。

あとはこれらを元にしてupdateすればいいです。重複データさえ消えてしまえばこっちのもんです。

投稿2018/08/27 06:31

sousuke

総合スコア3828

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問