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

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

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

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

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

2回答

795閲覧

Timestampと信号0/1のカラムのテーブルから信号が1状態の開始/終了時刻データに変換したい

hideki.

総合スコア31

PostgreSQL

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

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

0グッド

0クリップ

投稿2021/12/23 07:57

前提・実現したいこと

Timestampと3種類の信号の状態が0/1で格納されたテーブルtbl_dummyがあります。

ts col_a col_b col_c
2020/12/23 14:12 0 1 0
2020/12/23 14:13 0 0 1
2020/12/23 14:14 0 1 0
2020/12/23 14:15 0 1 0
2020/12/23 14:16 0 0 1
2020/12/23 14:17 0 0 1
2020/12/23 14:18 0 0 1
2020/12/23 14:19 0 0 1
2020/12/23 14:20 0 0 1
2020/12/23 14:21 1 0 1
2020/12/23 14:22 1 1 1
2020/12/23 14:23 1 1 0
2020/12/23 14:24 0 0 1
2020/12/23 14:25 0 0 1
2020/12/23 14:26 1 0 0

得たい結果は各信号がON(1)の区間の開始時刻(start_tm)、終了時刻(end_tm)、信号名(A/B/C)です。

start_tm end_tm on_signal
null 2020/12/23 14:13 B
2020/12/23 14:13 2020/12/23 14:14 C
2020/12/23 14:14 2020/12/23 14:16 B
2020/12/23 14:16 2020/12/23 14:23 C
2020/12/23 14:21 2020/12/23 14:24 A
2020/12/23 14:22 2020/12/23 14:23 B
2020/12/23 14:24 2020/12/23 14:26 C
2020/12/23 14:26 null A

以下のSQLを作成しましたが、望む結果が得られません。
どのようにすればよいか、アドバイスお願いします。

該当のソースコード

SQL

1SELECT 2 CASE WHEN col_a_dif = 1 THEN ts 3 WHEN col_b_dif = 1 THEN ts 4 WHEN col_c_dif = 1 THEN ts 5 END AS start_tm, 6 CASE WHEN col_a_dif = -1 THEN ts 7 WHEN col_b_dif = 1 THEN ts 8 WHEN col_c_dif = 1 THEN ts 9 END AS end_tm, 10 CASE WHEN col_a_dif = -1 THEN 'A' 11 WHEN col_b_dif = 1 THEN 'B' 12 WHEN col_c_dif = 1 THEN 'C' 13 END AS on_signal 14FROM( 15SELECT ts, 16 col_a - lag(col_a, 1) over(ORDER BY ts) as col_a_dif, 17 col_b - lag(col_b, 1) over(ORDER BY ts) as col_b_dif, 18 col_c - lag(col_c, 1) over(ORDER BY ts) as col_c_dif 19FROM tbl_dummy) as tbl_dif 20WHERE (col_a_dif | col_b_dif | col_c_dif) <> 0 21;

上記SQLの結果

イメージ説明

補足情報(FW/ツールのバージョンなど)

PostgreSQL バージョン10

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

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

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

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

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

guest

回答2

0

解決済みですが、参考までに

SQL

1WITH SUB(TS, SIGNAL, COL) AS ( 2-- 横もちを縦もちへ 3SELECT TS, UNNEST(ARRAY['A','B','C']), UNNEST(ARRAY[col_a,col_B,col_C]) FROM TBL_DUMMY 4UNION ALL -- 先頭にLowValueの日付を追加 5SELECT '-INFINITY', UNNEST(ARRAY['A','B','C']), -1 6UNION ALL -- 末尾にHighValueの日付を追加 7SELECT 'INFINITY', UNNEST(ARRAY['A','B','C']), -1 8 9), SUB2 AS ( 10-- 1行前の値の情報を追加 11SELECT * 12, LAG(COL) OVER (PARTITION BY SIGNAL ORDER BY TS) AS LAG_COL 13FROM SUB 14 15), ST AS ( 16-- 開始時間の一覧 17SELECT * 18, ROW_NUMBER() OVER (PARTITION BY SIGNAL ORDER BY TS) AS NUM 19FROM SUB2 20WHERE (LAG_COL, COL) IN((0,1),(-1,1)) 21 22), ET AS ( 23-- 終了時間の一覧 24SELECT * 25, ROW_NUMBER() OVER (PARTITION BY SIGNAL ORDER BY TS) AS NUM 26FROM SUB2 27WHERE (LAG_COL, COL) IN((1,0),(1,-1)) 28 29) 30 31-- 開始時間と終了時間をJOINさせる 32SELECT 33 CASE WHEN ST.LAG_COL = -1 THEN NULL ELSE ST.TS END AS START_TM -- 先頭行はNULL 34, CASE WHEN ET.COL = -1 THEN NULL ELSE ET.TS END AS END_TM -- 末尾行はNULL 35, ST.SIGNAL 36FROM ST INNER JOIN ET USING(SIGNAL, NUM) 37ORDER BY ST.TS, ET.TS

投稿2021/12/25 01:35

takanaweb5

総合スコア359

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

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

hideki.

2021/12/26 23:29

回答ありがとうございます。 コメントも入っているので、コードの考え方を理解するのに助かります。 neko_the_shadow様のコードでもそうですが、今まで全く知らなかったキーワードが出てきます。自分のカバーするSQLスキル範囲がまだほんの少しなのだと、実感しております。これから勉強していきます。
guest

0

ベストアンサー

たとえば以下のようなクエリになると思います (もうすこし単純に書けるような気もしますが)

SQL

1WITH R AS ( 2 SELECT ts, signal, col, last_col, ROW_NUMBER() OVER (PARTITION BY signal ORDER BY ts) AS rownum 3 FROM ( 4 SELECT ts, signal, col, LAG(col) OVER (PARTITION BY signal ORDER BY ts) AS last_col 5 FROM ( 6 SELECT ts, 'A', col_a FROM tbl_dummy 7 UNION ALL SELECT ts, 'B', col_b FROM tbl_dummy 8 UNION ALL SELECT ts, 'C', col_c FROM tbl_dummy 9 ) AS P (ts, signal, col) 10 ) AS Q 11 WHERE (last_col = 1 AND col = 0) OR (last_col = 0 AND col = 1) 12) 13 SELECT 14 ts, 15 (SELECT MIN(R2.ts) FROM R AS R2 WHERE R1.signal = R2.signal AND R1.ts < R2.ts AND R2.last_col = 1 AND R2.col = 0), 16 signal 17 FROM R AS R1 18 WHERE R1.last_col = 0 AND R1.col = 1 19UNION ALL 20 SELECT NULL, ts, signal FROM R WHERE rownum = 1 AND last_col = 1 AND col = 0 21

投稿2021/12/23 15:38

neko_the_shadow

総合スコア2349

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

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

hideki.

2021/12/24 00:25

回答ありがとうございます。 取り急ぎ、実行してみたところ、「得たい結果」の最初と最後の行以外は正しく処理されました。最初と最後の行は開始/終了時刻がセットにならないケースなので、その後の処理でどう扱うか検討しようと思っていたところなので、今回の提示SQLでも大変参考になります。コードの意味するところの理解はこれからとなりますが、SQLで無理ならPythonでDataFrameに読み込んで処理しようかと考えていたので、大変助かりました。実際は信号カラムの個数はもっと多いので面倒ですが、今回のコードを参考にアレンジしてみます。 どうも、ありがとうございました。
neko_the_shadow

2021/12/24 00:42

一応、サンプルデータを使って、「得たい結果」になっていることは確認したのですが、実データには何かしらのエッジケースがあるのかもしれません。メンテナンス性やテストのしやすさなどを考えると、複雑なSQLで処理するよりも、何らかのプログラミング言語で処理されたほうがよいかもしれません。
hideki.

2021/12/24 01:06

コメントありがとうございます。 SQLに不慣れなため、SQLでやるか、一般的なプログラミング言語でやるかの見極めが自分でできません。今回、経験豊富な方の意見を頂けたので、Pythonで組む方法と両方を検討してみます。
hideki.

2021/12/27 02:56

最初のコメントで「最初と最後の行以外は正しく処理されました」と書きましたが、私の勘違いで、元テーブルの最初のレコードの信号が1のケース、及び最終レコードの信号が1のケースを含め、正しい結果が得られました。お詫びして訂正いたします。また、信号カラム数を増やす場合の修正箇所も比較的分かりやすいので、Pythonで組まず、SQLで進めたいと思います(SQLコードの理解はまだ完全ではありませんが)。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問