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

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

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

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

Q&A

解決済

2回答

351閲覧

PostgreSQL 既存データに対して重複しなくなるまで加算してからインサートしたい

person

総合スコア224

PostgreSQL

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

0グッド

0クリップ

投稿2024/08/29 00:26

編集2024/08/29 00:32

実現したいこと

datetime型のカラムに対して、既存データに対して重複しなくなるまで秒を加算してからインサートしたい。
既存データには変更は加えない(DELETEやUPDATEはしない)。

'2024-08-29 09:00:00'をインサートしようとしたときに既に下記テーブルにデータがある。

|dt(timestamp without timezone)| |------------------------------------| |2024-08-29 09:00:00| |2024-08-29 09:00:01| |2024-08-29 09:00:02|

既存データと重複しなくなるまで秒を加算したいので、インサート後は下記のようにしたい。

|dt(timestamp without timezone)| |------------------------------------| |2024-08-29 09:00:00| |2024-08-29 09:00:01| |2024-08-29 09:00:02| |2024-08-29 09:00:03| <--- インサート

前提

テーブル構造
実際は他にもカラムがあるが、本質問では最低限の情報のみ提示したいためdatetime型(timestamp without timezone)列のみの構造とする。ただし、実際のテーブルでは他の列に主キーなどを設定しているため、主キー等は設定しない。(本当は主キーなどを設定して、重複しない構造にすべきなのでしょうが・・・。)

その他
Pythonのpsycopg2でSQLを使うつもり。
特に気にする必要はないと思いますが、一応。

バージョン

sql

1SELECT * FROM version(); 2 3PostgreSQL 14.4, compiled by Visual C++ build 1914, 64-bit

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

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

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

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

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

guest

回答2

0

ベストアンサー

テーブル定義は以下のようなものであるという認識です。

SQL

1CREATE TABLE TBL (val CHAR(1), dt timestamp without time zone); 2INSERT INTO TBL VALUES ('A', TIMESTAMP '2024-08-29 09:00:00'); 3INSERT INTO TBL VALUES ('B', TIMESTAMP '2024-08-29 09:00:01'); 4INSERT INTO TBL VALUES ('C', TIMESTAMP '2024-08-29 09:00:02');

datetime型のカラムに対して、既存データに対して重複しなくなるまで秒を加算してからインサートしたい。

やり方はいろいろありそうですが、たとえば以下のようなINSERT文で実現可能だと思います。

SQL

1WITH RECURSIVE R (dt) AS ( 2 SELECT TIMESTAMP '2024-08-29 09:00:00' 3 UNION ALL 4 SELECT dt + INTERVAL '1 SECONDS' 5 FROM R 6 WHERE EXISTS ( 7 SELECT * 8 FROM TBL AS T 9 WHERE R.dt = T.dt 10 ) 11) 12INSERT INTO TBL (val, dt) VALUES ('X', (SELECT MAX(dt) FROM R));

投稿2024/08/29 01:51

neko_the_shadow

総合スコア2351

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

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

person

2024/08/29 04:35

回答ありがとうございます。 RECURSIVEで再帰(反復?)処理したほうがSQLコスト低そうですかね。
shiketa

2024/08/30 05:31

すでに解決済みですが。 DBMSによっては、RECURSIVEに上限があるらしいです。 丸一日分(24h*60m*60s)86400件連続していたらどうなるか興味があるのでためしてみました。 insert into tbl (dt) select generate_serise('2024-08-29 09:00:00'::timestamp, '2024-08-30 09:00:00'::timestamp, interval '1 second'); PostgreSQLではこの程度では上限には達しないようです。上限の設定がないのかもしれません。 ただし、結果が帰るまでには相当な時間がかかりました(docker環境)。 どんなシステムなのか想像ができませんが、一か月連続して埋まっていたりすると、結構厳しいのかもしれませんね。 ちなみに、dtにindexを設定すると処理時間は相当改善されました。 参考まで。
guest

0

おもしろそうなのでためしてみた。

sql

1create table hoge (dt timestamp without time zone); 2 3insert into hoge values 4 ('2024-08-29 09:00:00'), 5 ('2024-08-29 09:00:01'), 6 ('2024-08-29 09:00:02'), 7 ('2024-08-29 09:02:22') 8 ; 9 10with 11 aaa as 12 ( 13 SELECT 14 generate_series(min(dt), max(dt), interval '1 second') as dt 15 from hoge 16 ), 17 bbb as 18 ( 19 select * from aaa 20 except 21 select * from hoge 22 ) 23insert into hoge select * from bbb 24; 25 26select * from hoge order by dt;

txt

12024-08-29 09:00:00.000 22024-08-29 09:00:01.000 32024-08-29 09:00:02.000 42024-08-29 09:00:03.000 52024-08-29 09:00:04.000 6... 72024-08-29 09:02:20.000 82024-08-29 09:02:21.000 92024-08-29 09:02:22.000

なるほど。

投稿2024/08/29 01:21

編集2024/08/29 01:29
shiketa

総合スコア4061

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

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

person

2024/08/29 02:22

回答ありがとうございます。 ご提示のSQLは、 aaa: テーブル内の最小値(2024-08-29 09:00:00)~最大値(2024-08-29 09:02:22)の1秒刻みデータ bbb: aaaにありhogeにないデータ(2024-08-29 09:00:03~2024-08-29 09:02:21) を取得してbbbをインサートしていると思います。 質問文に記載したように1つのデータだけインサートする場合は、dtをORDER BY で昇順ソートしてLIMIT1で1行のみSELECTしてINSERTするで合っていますか? > insert into hoge select * from bbb insert into hoge select * from bbb ORDER BY dt LIMIT 1;
shiketa

2024/08/29 04:33

最大値の次、であればこんな感じでもいいのでは? insert into hoge select max(dt) + interval '1 second' from hoge;
person

2024/08/29 07:35 編集

すみません、書き方が悪かったですね。 インサートしたい値は、 hogeにある最大値+1ではなく、 入れようとした値(2024-08-29 09:00:00)に重複しなくなるまで秒を足した値のデータを1件だけです。 2024-08-29 09:00:00.000 2024-08-29 09:00:01.000 2024-08-29 09:00:02.000 2024-08-29 09:00:22.000 ↓ 2024-08-29 09:00:00.000 2024-08-29 09:00:01.000 2024-08-29 09:00:02.000 2024-08-29 09:00:03.000 <--- insert 2024-08-29 09:00:22.000
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.34%

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

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

質問する

関連した質問