プロが教えるわが家の防犯対策術!

初歩的なSQLの質問で申し訳ありません。

以前にも同じような内容で投稿させて頂きましたが、教えて下さい。

下記のような、ある異なる名前の同項目を持つ2つのテーブルが存在します。

《テーブル》

Aテーブル 

 社員番号、日付、内容、所属、時間、結果

Bテーブル

 社員番号、日付、内容、理由

 ※ 内容に関しては、AテーブルとBテーブルで全く違います。

これらの2つのテーブルをマージさせ、社員番号毎の内容毎、日付順に
したいと考えています。

以前に以下のようなUNIONを教えてもらいましたが、所属、時間、結果、理由という
項目全てを出力するとした場合、どのように記載すれば良いでしょうか?

UNIONの場合、項目が異なるとエラーとなってしまうかと、、、

《UNIONのSQL》

SELECT *
FROM (
SELECT 社員番号, 日付, 内容
FROM Aテーブル
UNION
SELECT 社員番号, 日付, 内容
FROM Bテーブル
)
ORDER BY 日付, 社員番号, 内容



教えて下さい。

よろしくお願いします。

A 回答 (3件)

そもそもがUNIONの用途として正しくない気がしますしね。


複数のテーブルの同一キーのデータを簡単に取れないというのは、設計がよくないのではないかという気がします。

>1つ1つNULLを設定するのは難しいかと、、、
とありますが、ただ面倒だということですよね?
ひとつひとつ項目を列挙していくだけなので難しいことはないと思うのですが。
たしかに、テーブルや項目が多ければ大変だとは思いますが、やらなければいけないことならやるしかないと思います。

役に立つか分かりませんが、複数のテーブルの全項目をUNIONで取得するSQLを組み立てるSQLを考えてみました。
--全角スペースでインデントしています
select
  case
    when COL_NUM = 1 then
      case
        when TAB_NUM > 1
        then 'UNION ALL' ||CHR(10)
      end
      || 'SELECT' || CHR(10)
    else ','
  end
|| coalesce(P_COLUMN_NAME, 'NULL') || ' AS ' || L_COLUMN_NAME
|| case
    when max(COL_NUM) over() = COL_NUM then
      CHR(10) || 'FROM ' || TABLE_NAME
  end
from
  (
    select
      TABS.TABLE_NAME
     , COLS.COLUMN_NAME L_COLUMN_NAME
     , TAB_COLS.COLUMN_NAME P_COLUMN_NAME
     , dense_rank() over(order by TABS.TABLE_NAME) TAB_NUM
     , ROW_NUMBER() over(partition by TABS.TABLE_NAME order by COLS.COLUMN_NAME) COL_NUM
    from
      (
        select distinct COLUMN_NAME
        from USER_TAB_COLS
        where TABLE_NAME in ('Aテーブル', 'Bテーブル') --対象のテーブルを列挙してください
      ) COLS
    cross join
      (
        select TABLE_NAME
        from USER_TABLES
        where TABLE_NAME in ('Aテーブル', 'Bテーブル') --対象のテーブルを列挙してください
      ) TABS
    left join USER_TAB_COLS TAB_COLS
    on
      (
        TAB_COLS.TABLE_NAME = TABS.TABLE_NAME
      and TAB_COLS.COLUMN_NAME = COLS.COLUMN_NAME
      )
  )
order by
  TAB_NUM
 , COL_NUM
;

これを実行すると、たとえば今回提示されているテーブル定義であれば、
SELECT
内容 AS 内容
,所属 AS 所属
,日付 AS 日付
,時間 AS 時間
,NULL AS 理由
,社員番号 AS 社員番号
,結果 AS 結果
FROM Aテーブル
UNION ALL
SELECT
内容 AS 内容
,NULL AS 所属
,日付 AS 日付
,NULL AS 時間
,理由 AS 理由
,社員番号 AS 社員番号
,NULL AS 結果
FROM Bテーブル
というSQLが出来ます。

これをインラインビューにして、SELECTする項目順を入れ替えたり、不要な項目を削除したりすれば少しは楽になるかもしれません。
    • good
    • 0

すみません。

質問をきちんと読めていませんでした・・・
Bテーブルだけに存在する項目もあるのですね。
であれば、AテーブルのSELECT文でNULLを設定すればいいです。

SELECT 社員番号, 日付, 内容, 所属, 時間, 結果, NULL AS 理由
FROM Aテーブル
UNION
SELECT 社員番号, 日付, 内容, NULL, NULL, NULL, 理由
FROM Bテーブル
ORDER BY 社員番号, 内容, 日付;

でいかかでしょうか。
    • good
    • 0
この回答へのお礼

yamada_g様、ご回答ありがとうございます。

返事が遅くなりまして申し訳ありません。

教えて頂いた件、問題なくできました!

ちなみに、もしこれらのテーブルが複数存在し、社員番号、日付、内容は
キーになるものの、それ以外の項目を、テーブルがある分だけ表示させるような
場合はどのようにするのが良いのでしょうか?

1つ1つNULLを設定するのは難しいかと、、、

よろしくお願いします。

お礼日時:2013/10/02 09:53

存在しない項目にNULLを設定すればUNIONすることができます。



SELECT 社員番号, 日付, 内容, 所属, 時間, 結果
FROM Aテーブル
UNION
SELECT 社員番号, 日付, 内容, NULL, NULL, NULL
FROM Bテーブル
ORDER BY 社員番号, 内容, 日付;

もし、それぞれのテーブルが社員番号と日付で一意になり、それらのデータは1行にしたいということであれば完全外部結合してもいいと思います。

SELECT
NVL(A.社員番号, B.社員番号) AS 社員番号,
NVL(A.日付, B.日付) AS 日付,
A.内容 AS A内容,
B.内容 AS B内容,
A.所属,
A.時間,
A.結果
FROM Aテーブル A
FULL OUTER JOIN Bテーブル B ON (A.社員番号 = B.社員番号 AND A.日付 = B.日付)
ORDER BY 社員番号, A.内容, B.内容, 日付;

みたいな感じです。
    • good
    • 0
この回答へのお礼

yamada_g 様、ご回答ありがとうございます。

NULLの件ですが、Bテーブルの方も社員番号、日付、内容だけでなく、理由
も出力したいと考えています。
その場合は、NULLでは難しいですよね、、、

また、残念ながら両テーブルは、社員番号と日付では一意にならず、N:Nの
関係となっています。

何もわからず申し訳ありませんが、もう少し教えて頂ければ幸いです。

よろしくお願いします。

お礼日時:2013/10/01 17:32

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

関連するカテゴリからQ&Aを探す