MySQL

MySQL

【SQL】縦方向に保存されている進捗の様なレコードを条件づけて抽出する方法

業務によくある「申請」のようなテーブルレコードと、その申請に対し複数の決裁者が「未処理」「承認」「否決」といったステータスを埋めるというようなテーブルレコードがあった際、現状のステータスのレコードを検索するSQLをどうやって書くのか悩んだので、私なりに考えた方法を備忘録としてポストしておきたいと思います。

先に方法を書いておくと、以前ポストした内容でもある「GROUP_CONCAT」を使って実現させました。

1.テーブル作成

申請マスタテーブル

「出張先」と「出張する人」を登録するテーブルを作成。

実際には期間や目的など色々と持たせるべき項目はあると思いますが、サンプルなので最低限のカラム構成とします。

レコードを登録します。

決裁テーブル

先程の申請マスタを外部キーに持つ「決裁」用テーブルを作成。
ステータスは「0:未処理」「1:承認」「2:否決」となるようにします。

以下のレコードを作成します。
案件としては7つ。それぞれ縦方向のレコードとして決裁ステータスと決裁者を設定します。

要約すると

  • 純粋に未処理な案件:3
  • 否決された案件:4、5、7
  • 承認された案件:1、2、6

となっています。
「否決」された後のレコードには未処理のステータスが登録されたままとなります。

2.抽出していく

2-1.全て出力

単純にLEFT JOINして表示。20レコードが並んでいます。

2-2.GROUP_CONCATでステータス値を文字列としてstsカラムに結合する

決裁テーブルのbusinesstrip_id(申請マスタの外部キー)を GROUP_CONCAT でグループ化しながら、ステータスを「sts」カラムに文字列として結合した結果を表示します。

結果

案件別に7レコードが表示され、縦方向にあった決裁ステータスが文字列で結合されました。GROUP_CONCATさまさまです。

再度整理しておくと、
stsが「全て1」で構成されているのが、承認済みの案件。
「2」を**含む**のが否決された案件。
「0」を含む案件は「2」の有無で純粋な「処理待ち」か、最終決裁者までに否決された案件となります。

ここまで来るとあとは何とかなりそうな予感がしますが、ステップアップしながら解説します。

2-3.「1」のみレコード(承認済)を抽出

全て承認されたレコードを抽出してみます。
条件としては「全て1」なので、裏返して「0も2も含まない」としてSQL化します。

GROUP BY しているので HAVING 句でフィルタします。

結果

↑ 想定通り、案件1、案件2、案件6が抽出されました。

否決された案件を抽出する場合は、HAVINGの内容を以下のようにします。

↑ 想定通り、案件4、案件5、案件7が抽出されました。

同様に、処理待ちのもの(2を含まず、0を含むもの)は以下のようにします。

↑ 想定通り、案件3のみが抽出されました。

2-4.(2-3)の結果から「businesstrip_id」のみを抽出

先程の結果から businesstrip_id カラムのみを抽出してみます。
ここがなかなか悩むところでしたが、SELECTした結果を別名のテーブル(テーブル名:r2)となるようサブクエリにしてSQL化します。

結果

ばっちり取り出せました。

2-5.最終形

では、これまでのステップを踏まえたSQLでほしいレコード(案件番号、出張する人、場所)を抽出してみます。

結果

2-4のSQLを「INの条件」としてさらに入れ子にして抽出する必要があるのでちょいややこしいですが、実現することができました。

ステータスを縦方向のレコードとして持たせてもきちんと抽出でき一安心。これは他でも使えそうな技だと思うので、ここに書いたことだけを覚えておこう(笑)

-MySQL
-,