90975


Oracleにて複数レコードinsertしたい時、なぜ最後にDUALテーブルをSELECTしないといけないのでしょうか？

[code]

    insert all 
    insert into table() values()
       .....
    insert into table() values()
    select * from dual
    
[/code]


CC BY-SA 4.0
2022-09-07T05:32:36.723
90972
2022-09-07T06:48:33.053
2022-09-07T06:48:33.053
3060
30328
question
oracle
Oracle の insert all 後、なぜ DUAL テーブルを select しないといけない？


`INSERT ALL`の構文がサブクエリを必要とするため、必ずSELECT文が必要であることが主な理由です。  
特にサブクエリの値を使わない場合の慣例としてDUAL表を使用します。

公式ドキュメントの[Syntax](https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#i2111652)から`multi_table_insert
::=`を確認すると、`INSERT ALL`の構文は必ず末尾にサブクエリ(subquery)を必要としていて省略できません。  
[subquery](https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#sthref9453)の説明を見ても省略できない理由は書かれていないので、[Example](https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_9014.htm#i2126076)からOracleが元々想定しているサブクエリの使い方を推測してみます。

ExampleのSQLを抜粋して加工したものが下記です。

[code]

    INSERT ALL
          INTO sales (prod_id, cust_id, time_id, amount)
          VALUES (product_id, customer_id, weekly_start_date, sales_sun)
          INTO sales (prod_id, cust_id, time_id, amount)
          VALUES (product_id, customer_id, weekly_start_date+1, sales_mon)
          INTO sales (prod_id, cust_id, time_id, amount)
          VALUES (product_id, customer_id, weekly_start_date+2, sales_tue)
          -- 以下略
       SELECT product_id, customer_id, weekly_start_date, sales_sun,
          sales_mon, sales_tue, sales_wed, sales_thu, sales_fri, sales_sat
          FROM sales_input_table;
    
[/code]

上記のINSERT文の例では、`sales_input_table`に3レコード入っています。  
`weekly_start_date`に0日、1日、2日を加算して`sales`テーブルに入れているので、日付のみインクリメントしながら計9レコードを`sales`テーブルに追加します。

[code]

    INSERT ALL
       WHEN order_total < 1000000 THEN
          INTO small_orders
       WHEN order_total > 1000000 AND order_total < 2000000 THEN
          INTO medium_orders
       WHEN order_total > 2000000 THEN
          INTO large_orders
       SELECT order_id, order_total, sales_rep_id, customer_id
          FROM orders;
    
[/code]

上記の例では、`order_total`カラムの値によってINSERTする先のテーブルを`small_orders`,`medium_orders`,`large_orders`に振り分けています。

Oracleとしてはテーブルの値を元にして、レコードごとに値の変わらないカラムのコピーや日付/連番の加算、CASE文による挿入先の条件分岐を意図しているようです。  
個人の想像ですが、`values`に定数のみを指定する使い方は重視しておらず「値が決め打ちなら普通に`INSERT
INTO`を並べればいいじゃん」と考えたのかもしれません。

結論としては「構文上SELECT文を付けなければいけないので、仕方なく1行だけ値が返る最軽量のSQL文を書かなければいけない」となります。


CC BY-SA 4.0
2022-09-07T06:26:59.187
90975
2022-09-07T06:26:59.187
9820
90972
answer