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

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

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

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

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

Q&A

解決済

1回答

2653閲覧

【SQL】【Oracle】自己結合したテーブルからデータを取得した時に、一つしかないはずのデータが重複する。

maru-omochi

総合スコア11

Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

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

0グッド

0クリップ

投稿2020/11/04 11:54

編集2020/11/04 14:05

前提・実現したいこと

 お疲れ様です。

■人情報が入っているテーブルからその人名と、その人が休暇を取得した場合に、
休暇情報が入ったテーブルから休暇行使日を取得した休暇別に取得したいです。
加えて、上詰めで表示で来てたら尚うれしいです。
最悪上詰めで無くてもよいです。

【発生している問題点】
・本来は入っていない重複したデータが取得されてしまいます。
実際に取得されたイメージを下に掲載いたしました。

【テーブルの構成】
「person」

・人情報が入っているテーブル=「person」のカラムはそれぞれ、
person_id int primary key
person_code varchar2(10) not null
person_name varchar2(50) not null
で、入っているデータは仮に、

person_idperson_codeperson_name
1001A
2002B
3003C

とします。

「holiday」

・休暇の取得情報が入っているテーブル=「holiday」のカラムはそれぞれ、
holiday_id int primary key
person_code varchar2(10) not null
holiday_code varchar2(4) not null
holiday_name varchar2(30) not null
used_date char(10 char)
で、

今回問題が発生したデータが以下、
|holiday_id|person_code|holiday_code|holiday_name|used_date|
|:--:|:--:|:--:|:--:|
|1|001|1000|代休|2020/10/01|
|1|001|1000|代休|2020/10/02|
|1|001|2000|有給|2020/10/03|
|1|001|1000|怪我病欠|2020/10/04|
|2|002|2000|有給|2020/10/01|
|2|002|1000|代休|2020/10/03|
|2|002|1000|代休|2020/10/05|
|3|003|3000|怪我病欠|2020/10/03|
|3|003|3000|怪我病欠|2020/10/04|
|3|003|3000|怪我病欠|2020/10/05|

とします。

これら二つのテーブルを、取得したい休暇情報分自己結合したのち、以下のようなイメージでデータを取得したいと考えております。

person_name代休有休怪我病欠
A2020/10/012020/10/032020/10/04
A2020/10/02nullnull
B2020/10/032020/10/01null
B2020/10/04nullnull
Cnullnull2020/10/06
Cnullnull2020/10/07
Cnullnull2020/10/08

しかし実際に出てきたデータがこちら

person_name代休有休怪我病欠
A2020/10/012020/10/032020/10/04
A2020/10/022020/10/032020/10/04
B2020/10/032020/10/01null
B2020/10/042020/10/01null
Cnullnull2020/10/03
Cnullnull2020/10/04
Cnullnull2020/10/05

太字の個所のように、本来はない重複データが取得されてしまいます。
原因もさっぱりわからず、これを実現するためにどのようにネットでサーチしたらよいかもさっぱりでして、
また助けていただけるとありがたいです。

試したこと

前回の質問の時に教えていただいた、下のクエリで集計いたしました。

SELECT p.person_name , h1.used_date 代休, h2.used_date 有給, h3.used_date 怪我病欠 FROM PERSON p left JOIN HOLIDAY h1 ON h1.holiday_code='1000' and p.person_code = h1.person_code left JOIN HOLIDAY h2 ON h2.holiday_code='2000' and p.person_code = h2.person_code left JOIN HOLIDAY h3 ON h3.holiday_code='3000' and p.person_code = h3.person_code

その時に、クロス集計することになるだろうとはおっしゃっていただいたのですが、
調べた際に出てきたクロス集計では
「SELECT p.person_name
, h1.used_date 代休, h2.used_date 有給, h3.used_date 怪我病欠」
この部分をcase文(式?)で集計するというものは見かけたのですが、
システム上ここはいじることができない為、結合する部分でなんとか実現できないものかと思っております。

結合する際に、FULL OUTER JOINを用いまして、且つサブクエリで

(SELECT ROW_NUMBER() OVER(PARTITION BY person_code, holiday_code ORDER BY used_date ASC) NUM, person_code, used_date FROM holiday where holiday_code ='1000') t1 ON person.person_code = t1.person_code FULL OUTER JOIN (SELECT ROW_NUMBER() OVER(PARTITION BY person_code, holiday_code ORDER BY used_date ASC) NUM, person_code, used_date FROM holiday where holiday_code ='2000') t2 ON t1.NUM = t2.NUM AND t1.person_code = t2.person_code ※以下同様にもう一テーブル結合

とやってみたところ、今回のケースの場合うまくいくのですが、

where holiday_code ='1000'

の部分が例えば

where holiday_code ='1001'

のように実際にないデータ、または休暇を取っている人がおらずにデータがない場合等、データを取得できない場合に結合できない為か、
その列だけすべてnullになればよいものが、データがぐちゃぐちゃになって集計されてしまいます。

データが問題が起きている現場のものと一緒でない為、もしかするとここに書いた情報だけでは特定できないということもあるかもしれませんが、可能性だけでもご教授いただけたら大変ありがたいです。

尚前回ご指摘いただいた修正依頼でデータをインサートする部分も記述せよとのことだったのですが、
私が現場に入った時からこの状態でしてどのようにクエリが発行されたかはわかりませんでした、、、

以上です。

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

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

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

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

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

szk.

2020/11/04 13:31

自己結合した表のイメージではなく明文化してください。 特に何で集約されたものかは必要。 (イメージされたものに明確な決まりが無いように見えます。) データを見た感覚ですが、代休/有給/怪我病欠を同じ日で登録できるシステムに問題があるように感じます。 person_codeとused_dateでUKを貼ればいい気がしますが、、、
maru-omochi

2020/11/04 13:59

すみません取り急ぎデータを修正いたしました。 おっしゃる通りで重複してデータは登録できないのが正解です。 私の記述ミスでした。 尚今回ローカルDBにてテストできるように新たにテーブルを作成しましてそれに基づきデータを修正しなおしました。最初のものと比べると大幅に変更されています。 ほかにも修正しなければいけないことがあるのですが取り急ぎデータの修正を行いました。 ご指摘ありがとうございました。
guest

回答1

0

ベストアンサー

実際結合する前のテーブルでは A の 有休 は1日しかなかったはずなのに、二つに増えてしまいました。

結合をperson_codeのみで行っているのだから、当然そうなります。
希望する形するには、結合する条件を作り出す必要があります。

内容を見ると、person_code, holiday_code毎にused_date順になっていれば良さそうなので、row_number()を用いて連番を作ります。

それを**person **と上記の連番を付加したものと結合する為に、連番付加したものを集計したものを用います。

冗長になるので、withを用いてますが、

「SELECT p.person_name, h1.used_date 代休, h2.used_date 有給, h3.used_date 怪我病欠」
この部分をcase文(式?)で集計するというものは見かけたのですが、
システム上ここはいじることができない為

良く分からない縛りがあるようですので、抵触するようならwith部分を展開して下さい。

SQL

1with hseq as ( 2 select holiday.* 3 , row_number() over(partition by person_code,holiday_code order by used_date) as seq 4 from holiday 5), h0 as ( 6 select person_code, seq 7 from hseq 8 group by person_code, seq 9) 10select p.person_name 11 , h1.used_date 代休, h2.used_date 有給, h3.used_date 怪我病欠 12from person p 13 left join h0 14 on p.person_code = h0.person_code 15 left join hseq h1 16 on h1.holiday_code='1000' 17 and h0.person_code = h1.person_code 18 and h0.seq = h1.seq 19 left join hseq h2 20 on h2.holiday_code='2000' 21 and h0.person_code = h2.person_code 22 and h0.seq = h2.seq 23 left join hseq h3 24 on h3.holiday_code='3000' 25 and h0.person_code = h3.person_code 26 and h0.seq = h3.seq 27order by p.person_code, h0.seq

投稿2020/11/04 14:03

編集2020/11/04 14:05
sazi

総合スコア25327

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

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

maru-omochi

2020/11/04 14:11

前回同様今回も迅速に回答いただき誠にありがとうございます! 今回もおかげさまで希望通りに集計することができました。本当に感謝です! まだいまいち内容を消化しきれていないのですが、いただいたコードを元にしっかりと勉強して吸収したいと思います。
sazi

2020/11/04 14:21

縦を横にするのがクロス表で、それを集計したものがクロス集計です。 出力が最大や最小の日付なら、クロス表を経由せずに集計すれば良いだけでしたが、クロス表のままの出力というのが面倒な点です。
maru-omochi

2020/11/04 23:35

そういうことなのですね。承知いたしました。 ようやくなんとなくわかってきたような気がしています。 いろいろテストをして落とし込んでいきたいと思います。いつもありがとうございます。感謝です。
sazi

2020/11/05 00:05

連番で集計していますからクロス集計と言えなくもないですね。
maru-omochi

2020/11/13 05:38

すみません通知が届いておらず気が付きませんでした。 だいぶわかってきたと思うのですが、まだまだ自在に使いこなせるという状態ではないのでもっと使い込んで落とし込んでいきたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問