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

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

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

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

PHP

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

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

Q&A

4回答

6742閲覧

あればUpdateなければInsert同じデータなら何もしないをsql一本で書きたい

ms5025

総合スコア292

PostgreSQL

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

PHP

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

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

0グッド

3クリップ

投稿2020/01/15 04:59

編集2020/01/15 06:06

前提・実現したいこと

前提
postgreSQL
言語php
フレームワーク Laravel

DBのデータ

id=1 name=ぶどう last_update_time=2019/01/01 01:01:01 id=2 name=ぶどう last_update_time=2019/01/01 01:01:01 id=3 name=ぶどう last_update_time=2019/01/01 01:01:01

更新用データ

id=1 name=ぶどう last_update_time=2019/01/01 01:01:01 id=2 name=バナナ last_update_time=2019/01/31 10:01:01 id=3 name=みかん last_update_time=2019/01/31 10:01:01 id=4 name=なし last_update_time=2019/01/31 10:01:01

■実現したいこと
・更新用のデータでDBの値をUpdate/Insertしたい
・last_update_timeが同じデータは更新処理もしたくない
・上記の例で言えば更新用データ→DBデータに対する挙動は
id=1 何もしない
id=2 update
id=3 update
id=4 insert

■聞きたいこと
実現する為にSQL一本で可能かどうか

■試したこと・考え
INSERTとUPDATEをCONFLICT句で
と思ったんですが、
同じデータの場合はたとえデータの変更がなくてもUpdate句が走ってしまいます。
よって更新用データでループして、last_update_timeがDBの値と異なればUpdateデータがなければInsert
という処理をループする、しか思いつきませんでした。
しかし処理件数は1000 件を超えることもあり、かつ数分に一度走る監視用のような処理のため
できればループはしたくありません。(殆どが変更のないデータである可能性が高いため)

SQL1本で上記の処理は実現可能でしょうか?
また、一本は無理でも、上記処理より良い処理はあるでしょうか

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2020/01/15 05:21

「あればInsertなければUpdate」←逆じゃない?
ms5025

2020/01/15 05:27

逆ですね。すいません。修正します
sazi

2020/01/15 06:03 編集

(削除)
sazi

2020/01/15 06:03 編集

(削除)
sazi

2020/01/15 06:03 編集

(削除)
退会済みユーザー

退会済みユーザー

2020/01/20 02:53

Laravelを使っているなら、updateOrCreate() は試したのでしょうか?
guest

回答4

0

SQL

1with sel as (select * from (values 2 (1, 'ぶどう', '2019/01/01 01:01:01'::date), 3 (2, 'バナナ', '2019/01/31 10:01:01'::date), 4 (3, 'みかん', '2019/01/31 10:01:01'::date), 5 (4, 'なし', '2019/01/31 10:01:01'::date) 6 ) as w(id, name, last_update_time)), 7with upd as (update table set name=sel.name 8 last_update_time=sel.last_update_time 9 from sel 10 where table.id = sel.id 11 and table.last_update_time <> sel.last_update_time 12 returning sel.id) 13insert into table select * from sel 14 where not exists (select id from upd) 15on conflict on constraint pkey 16do nothing

動作としては、selで更新データを作成して、updidが同じでlast_update_timeが異なるものを更新しています。(2,3が対象)
更新対象がなければ、insertになりますが、constraintで弾いてます。(4はOK)
idprimary keyがあると想定していますので、1は弾かれ、何も処理はしません。

性能評価などはしていないため、コストかかって運用にあっていなければスミマセン。

投稿2020/01/15 09:14

編集2020/01/15 09:32
Masakin

総合スコア192

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

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

ms5025

2020/02/19 00:54

ありがとうございます。 参考になりました。 こちらの方法で実現できました。 ありがとうございます。
guest

0

実現する為にSQL一本で可能かどうか

with式とreturningを活用すれば可能です。

下記は、対象のテーブルをtest、データをワーク(一時テーブルなど)に格納した更新用データをtest2として記述しています。

SQL

1 with upd AS ( 2 UPDATE test tgt SET 3 name=val.name 4 , last_update_time=val.last_update_time 5 FROM test2 val 6 WHERE tgt.id=val.id 7 and (tgt.name!=val.name or tgt.last_update_time!=val.last_update_time) 8 returning tgt.* 9 ) 10 INSERT INTO test tgt SELECT * FROM test2 src 11 WHERE id NOT IN (SELECT id FROM upd) 12 and not exists(select 1 from test where id=src.id)

ですが、レスポンスを考えるなら、updateとinsertをそれぞれで行った方が高速です。

SQL

1UPDATE test tgt SET 2 name=val.name 3, last_update_time=val.last_update_time 4FROM test2 val 5WHERE tgt.id=val.id 6 and (tgt.name!=val.name or tgt.last_update_time!=val.last_update_time) 7; 8INSERT INTO test tgt SELECT * FROM test2 src 9WHERE not exists(select 1 from test where id=src.id) 10;

高速にするポイントは、更新対象データをテーブルデータにする事です。
一時テーブルを利用すれば排他も気にする必要はありません。

SQL

1create temp table test2(id int, name text, last_update_time timestamp) on commit drop; 2insert into test2 values 3 (1, 'ぶどう', '2019/01/01 01:01:01') 4 ,(2, 'バナナ', '2019/01/31 10:01:01') 5 ,(3, 'ミカン', '2019/01/31 10:01:01') 6 ,(4, 'なし' , '2019/01/31 10:01:01') 7; 8create index test_index on test2(id, name, last_update_time);

投稿2020/01/15 06:36

編集2020/01/15 06:57
sazi

総合スコア25195

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

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

sazi

2020/01/15 07:02

ワークテーブルでは無く、with式に更新用データを展開する方がお手軽かもしれません。 但し、動的SQLにする必要がありますが。
guest

0

単純にUPDATEにWHEREで条件を追加してみてはダメでしょうか?
PosgreSQL10 ですが、実行したところWHERE区の条件に一致しない場合は更新されませんでした。
(update_atは更新が走ったかどうか確認する為のカラムなので無視してください)

INSERT INTO fruits (id, name, last_update_time, update_at) VALUES (1,'ぶどう','2019/01/01 01:01:01',now()) ON CONFLICT ON CONSTRAINT fruits_pkey DO UPDATE SET name='ぶどう',last_update_time='2019/01/01 01:01:01',update_at=now() WHERE fruits.id=1 and fruits.last_update_time < '2019/01/01 01:01:01';

WHEREのカラムにはテーブル名を明記しないとSQL実行時に曖昧なカラム参照エラーになったので付けています。

投稿2020/01/15 06:31

編集2020/01/15 06:40
storm3

総合スコア330

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

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

storm3

2020/01/15 08:37

>同じデータの場合はたとえデータの変更がなくてもUpdate句が走ってしまいます。 失礼しました。私の回答は上記の内容を汲んでいませんでした。 空振りであっても更新のSQL自体は走ってしまいますね。
guest

0

UPSERT by テーブルがやりたくて書いてみました。
シェルでvalue区を作成する様に考えました1000件は多いですど。

sql

1CREATE TEMP TABLE hoge (id int, name varchar, last_update_time timestamp, 2CONSTRAINT upst_pkey PRIMARY KEY(id)); 3INSERT INTO hoge (id, name, last_update_time) 4values(1, 'ぶどう', '2019/01/01 01:01:01'), 5 (2, 'ぶどう', '2019/01/01 01:01:01'), 6 (3, 'ぶどう', '2019/01/01 01:01:01'); 7 8 9--UPSERT by TABLE 10WITH hoge2 as (SELECT * FROM 11(values 12 (1,'ぶどう','2019/01/01 01:01:01'::timestamp), 13 (2,'バナナ','2019/01/31 10:01:01'::timestamp), 14 (3,'みかん','2019/01/31 10:01:01'::timestamp), 15 (4,'なし', '2019/01/31 10:01:01'::timestamp) 16) t1 17WHERE NOT EXISTS (SELECT 1 FROM hoge WHERE id=column1 AND name=column2 AND last_update_time=column3)) 18INSERT INTO hoge (id, name, last_update_time) 19SELECT * from hoge2 20ON CONFLICT ON CONSTRAINT upst_pkey 21DO UPDATE set name=excluded.name, last_update_time=excluded.last_update_time; 22

投稿2020/01/17 07:15

編集2020/01/17 08:26
amura

総合スコア333

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問