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

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に必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

2回答

2719閲覧

[PostgreSQL] 特殊なケースにおける配列抽出の実現方法

Gorilland

総合スコア8

PostgreSQL

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

SQL

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2021/10/11 08:53

編集2021/10/11 10:20

前提・実現したいこと

array_agg()関数で集計して得られた配列に対して以下の条件のもと、値を抽出したいと考えています。
(array_agg()で得られた配列[0, 1, 2]は、配列の定義ALLAY[0, 1, 2]と同じ)

  • 昇順にソートした配列の中から、値が欠損する前までの最大値を取得したい。

例) [0, 1, 2, 4, 5]という配列の場合、「3」が欠損しているため、欠損する前までの最大値である「2」を出力する。

例) [0, 1, 4]の場合も上と同様、「2」、「3」が欠損しているため、欠損する前の最大値である「1」を出力

例) [0, 1, 2, 3, 4, 5]である場合は、欠損している箇所がないため、出力は最大値の「5」となる。

※欠損の基準は、整数値を0から順に並べたときに欠けている整数のことを指します。(1つめの例の「3」)

追記

また、配列長は現状最大30個まであります。
つまり、[0,1,...,30]までの長さの配列が存在します。

ですが、これから先その最大長が更新される可能性があるため、可変長であることを仮定したプログラムを作成できたらな、と考えております。

該当の配列の例

--aggregate
--[0,1,2,4,5]
--[0,1,2,3]
--[0,1,5]
--[0,1]
--[0,1,2,5]

最終的に欲しい結果の例

--aggregateresult
--[0,1,2,4,5]2
--[0,1,2,3]3
--[0,1,5]1
--[0,1]1
--[0,1,2,5]2

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

仕様ツール:redash

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

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

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

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

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

sazi

2021/10/11 09:26 編集

欠損をチェックする最大の配列は定義できますか? 例えば、[0,1,2,3,4,5]が最大の配列だとすると、これに対しての欠損の最小値が求められるので。
Gorilland

2021/10/11 09:45

その部分についての記載を忘れていました。 最大の配列長は現状30です。つまり[0, 1,...,30]まであります。 しかしながら現状は最大ですが、これから先更新される可能性があるため、できるだけ可変長であると仮定していただければ幸いです。
guest

回答2

0

配列がソートされている前提で、1からの連番であるordinalityを利用して欠番までの最大値で求める。
※0が欠番の場合はNullになります。必要ならcoalesce(max(i), 0)とでもして下さい。

SQL

1with t as ( 2 select * 3 from (values 4 ( 1, array[0,1,2,4,5] ::int[]) 5 , ( 2, array[0,1,2,3] ::int[]) 6 , ( 3, array[0,1,5] ::int[]) 7 , ( 4, array[0,1] ::int[]) 8 , ( 5, array[0,1,2,5] ::int[]) 9 , ( 6, array[1,2,3] ::int[]) 10 ) t ( id, _aggregate ) 11) 12select * 13 , (select max(i) 14 from unnest(_aggregate) with ordinality as w(i, seq) 15 where i = seq - 1 16 ) result 17from t

ただ、

array_agg()関数で集計して得られた配列に対して以下の条件のもと、値を抽出したいと考えています。

**array_agg()**する前に求めた方が効率的だと思いますよ。

投稿2021/10/15 00:53

編集2021/10/15 00:56
sazi

総合スコア25327

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

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

sazi

2021/10/15 01:01

mayu-さんの「配列をSELECT句で展開」と同じで、ちょっと表現を変えました。
guest

0

ベストアンサー

※欠損の基準は、整数値を0から順に並べたときに欠けている整数のことを指します

配列長は現状最大30個までですが、これから先その最大長が更新される可能性があるため、
可変長であることを仮定したプログラム

以下でどうでしょう。

※ データサンプルは FROM...VALUES...( a, b ) の箇所です

SQL

1SELECT a _group 2 , b _aggregate 3 , coalesce( min( case when d - c != 1 then e end ) 4 , max(c) 5 ) _result 6FROM 7( 8 SELECT a, b, c, d 9 , lag( c, 1, 0 ) over ( partition by a order by c ) e 10 FROM 11 ( 12 VALUES 13 ( 1, array[0,1,2,4,5]::int[] ) 14 , ( 2, array[0,1,2,3]::int[] ) 15 , ( 3, array[0,1,5]::int[] ) 16 , ( 4, array[0,1]::int[] ) 17 , ( 5, array[0,1,2,5]::int[] ) 18 , ( 6, array[1,2,3]::int[] ) 19 ) t1 ( a, b ) 20 , LATERAL unnest( b ) with ordinality t2 ( c, d ) 21) q 22GROUP BY a, b 23ORDER BY 1 24;

■ 結果

_group_aggregate_result
1{0,1,2,4,5}2
2{0,1,2,3}3
3{0,1,5}1
4{0,1}1
5{0,1,2,5}2
6{1,2,3}0

  
以下はコメントいただいたロジックや回答とは別の記述方法:

  • 配列をSELECT句で展開

SQL

1SELECT a _group 2 , b _aggregate 3 , ( 4 SELECT coalesce( 5 min( coalesce( b[ d - 1 ], 0 ) ) filter( where d - c != 1 ) 6 , max( c ) 7 ) 8 FROM unnest( b ) 9 with ordinality t ( c, d ) 10 ) _result 11FROM 12( 13 VALUES 14 ( 1, array[0,1,2,4,5]::int[] ) 15 , ( 2, array[0,1,2,3]::int[] ) 16 , ( 3, array[0,1,5]::int[] ) 17 , ( 4, array[0,1]::int[] ) 18 , ( 5, array[0,1,2,5]::int[] ) 19 , ( 6, array[1,2,3]::int[] ) 20) q ( a, b ) 21ORDER BY 1 22;

 

  • 配列同士の差分比較

SQL

1SELECT a _group 2 , b _aggregate 3 , ( 4 SELECT coalesce( min(i) - 1 + ( min(i) = 0 )::int 5 , b[ array_upper( b, 1 ) ] 6 ) 7 FROM generate_series( 0, b[ array_length( b, 1 ) ] ) 8 as nums(i) 9 WHERE NOT array[i] <@ b 10 ) _result 11FROM 12( 13 VALUES 14 ( 1, array[0,1,2,4,5]::int[] ) 15 , ( 2, array[0,1,2,3]::int[] ) 16 , ( 3, array[0,1,5]::int[] ) 17 , ( 4, array[0,1]::int[] ) 18 , ( 5, array[0,1,2,5]::int[] ) 19 , ( 6, array[1,2,3]::int[] ) 20) q ( a, b ) 21ORDER BY 1 22;

  

  • 配列をFROM句で展開してビューのネストを無くす

SQL

1SELECT a _group 2 , b _aggregate 3 , coalesce( 4 min( coalesce( b[ d - 1 ], 0 ) ) filter( where d - c != 1 ) 5 , max( c ) 6 ) _result 7FROM 8( 9 VALUES 10 ( 1, array[0,1,2,4,5]::int[] ) 11 , ( 2, array[0,1,2,3]::int[] ) 12 , ( 3, array[0,1,5]::int[] ) 13 , ( 4, array[0,1]::int[] ) 14 , ( 5, array[0,1,2,5]::int[] ) 15 , ( 6, array[1,2,3]::int[] ) 16) t1 ( a, b ) 17, LATERAL unnest( b ) with ordinality t2 ( c, d ) 18GROUP BY a, b 19ORDER BY 1 20;

投稿2021/10/11 11:51

編集2021/10/14 15:22
mayu-

総合スコア335

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

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

Gorilland

2021/10/11 13:08

可変長ケースにも対応していただきありがとうございました。 私自身色々と勉強になる丁寧なコードの書き方をしてくださっているのでとても助かります。 これを参考にしてやりたいことが実装できそうです!ありがとうございました。
sazi

2021/10/12 02:19

LATERAL ではなくサブクエリーだけで行う方法もありますね。 unnestによる展開とgroup byをサブクエリーで行った方がコストが断然下げられたケースを経験したことがあります。 質問のパターンに該当するかは記憶が曖昧ですが。
mayu-

2021/10/12 04:12

@saziさん コメント欄だとインデントが反映されませんので LATERAL→サブクエリだけに書き換えたSQLを回答に追記しました。 unnestによる展開を FROM句ではなくSELECT句で実施する と解釈しています。
sazi

2021/10/12 04:27

@mayu-さん 一番外側にgroup byが無いイメージでした。
mayu-

2021/10/12 07:43

@saziさん ものの見事に外していましたね。 unnest と group by を使わないとなると、、、回答にも再度追記しましたけど 配列をゴニョゴニョして結果を出すという認識で合っているでしょうか。
sazi

2021/10/12 08:26

@mayu-さん サブクエリー内ではunnestもgroup byも使っているイメージでした。 配列の要素数と配列最後が最大値である事を利用したんですね。 これはこれでアリですね。
mayu-

2021/10/12 22:54

@saziさん unnest + group by を利用するという理解は合っていたようで安心しました。 なので、(方向性が異なると感じて)一旦お蔵入りにしたSQL文も載せますね。 配列はFROM句で展開しておくと記述が楽でしたので LATERAL はそのまま使っていますけど ビューのネストは無くしました。
mayu-

2021/10/14 15:52

解釈が違っていた追記は消して 残したSQLについての概要を記述しました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問