【Oracle23ai新機能】FROM DUALを省略したSELECTの使い方とFROM DUALとの違い【実行ログ付き】

  • URLをコピーしました!
Oracle AI Database
僕の作業環境
  • OS:Oracle Linux 9.7
  • 導入製品:Oracle AI Database 26ai Enterprise Edition Release 23.26.1.0.0
  • 構成:CDB=orcl/PDB=ORCLPDB1

Oracle Database 19c以前は関数の結果や計算結果を取得するとき、これまではFROM DUALを付ける必要がありました。

Oracle Database 23ai以降はFROM DUALを省略したSELECTが実行できるようになりました。

本記事では、「FROM DUALを省略したSELECTの使い方とFROM DUALとの違い」について解説します。

この記事でわかること
  • FROM DUALを省略したSELECTの基本
  • FROM DUALとの実行計画・統計の違い
  • 順序・INSERT … SELECT・PL/SQLでの使い方
  • FROM DUALを省略できないケース
目次

FROM DUALの省略とは

DUALは1行1列だけを持つダミーの表で、ORACLE Database内部で定数や関数の値を取得するために使われてきました。

Oracle Database 23ai以降はFROM DUALを書かずにSELECT <関数、式>;と実行できます。内部ではFROM DUALを指定した場合と同じ処理(FAST DUAL)になります。

書き方19c以前23ai以降
SELECT SYSDATE FROM DUAL;可可
SELECT SYSDATE;ORA-00923可

基本の書き方

計算式と、SYSDATE・USER・SYS_CONTEXTをFROM DUALを省略して実行しました。どちらも1行の結果が返りました。

SQL> SELECT 1+1;

       1+1
----------
         2

1 row selected.

SQL> SELECT SYSDATE, USER, SYS_CONTEXT('USERENV','CON_NAME');

SYSDATE             USER      SYS_CONTEXT('USERENV','CON_NAME')
------------------- --------- ----------------------------------------
2026-09-14 23:50:20 SYS       ORCLPDB1

1 row selected.

定数・関数・SYS_CONTEXTは、FROM DUALを省略でそのまま取得できます。

FROM DUALとの実行計画の比較

FROM DUALを省略したSELECTと、FROM DUALを付けたSELECTの実行計画を比較しました。どちらもFAST DUALで、Plan hash value(1388734953)も同じでした。

SQL> EXPLAIN PLAN SET STATEMENT_ID='V26_01A' FOR SELECT SYSDATE;

Explained.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(NULL,'V26_01A','BASIC'));

PLAN_TABLE_OUTPUT
----------------------------------------
Plan hash value: 1388734953

---------------------------------
| Id  | Operation        | Name |
---------------------------------
|   0 | SELECT STATEMENT |      |
|   1 |  FAST DUAL       |      |
---------------------------------

8 rows selected.

SQL> EXPLAIN PLAN SET STATEMENT_ID='V26_01B' FOR SELECT SYSDATE FROM DUAL;

Explained.

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY(NULL,'V26_01B','BASIC'));

PLAN_TABLE_OUTPUT
----------------------------------------
Plan hash value: 1388734953

---------------------------------
| Id  | Operation        | Name |
---------------------------------
|   0 | SELECT STATEMENT |      |
|   1 |  FAST DUAL       |      |
---------------------------------

8 rows selected.

FROM DUALを省略しても、オプティマイザはFROM DUAL付けたSELECTと同じ実行計画を選びます。

最後に、PLAN_TABLEに保存した2つの実行計画を削除しました。

SQL> DELETE FROM plan_table WHERE statement_id IN ('V26_01A','V26_01B');

4 rows deleted.

SQL> COMMIT;

Commit complete.

統計(consistent gets)の比較

AUTOTRACEで、2つのSELECTが読み込んだブロック数を比較しました。どちらもconsistent getsは0で、その他の統計もすべて同じ値でした。

SQL> SET AUTOTRACE TRACEONLY STATISTICS
SQL> SELECT 42;

1 row selected.

Statistics
----------------------------------------
          1  recursive calls
          0  db block gets
          0  consistent gets
          0  physical reads
          0  redo size
        580  bytes sent via SQL*Net to client
        108  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> SELECT 42 FROM DUAL;

1 row selected.

Statistics
----------------------------------------
          1  recursive calls
          0  db block gets
          0  consistent gets
          0  physical reads
          0  redo size
        580  bytes sent via SQL*Net to client
        108  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

FROM句を省略しても表は読まれず、FROM DUALとの性能差はありません。

V$SQLに保存されるSQL文

目印のコメントを付けた2つのSELECTを実行し、共有プールのV$SQLを確認しました。

FROM DUALを省略した文は、FROM DUALが付かないまま保存されていました。

SQL> SELECT /* v26_fromless */ 42 AS answer;

    ANSWER
----------
        42

1 row selected.

SQL> SELECT /* v26_dual */ 42 AS answer FROM DUAL;

    ANSWER
----------
        42

1 row selected.

SQL> SELECT sql_text, executions FROM v$sql WHERE sql_text LIKE 'SELECT /* v26\_%' ESCAPE '\' ORDER BY sql_text;

SQL_TEXT                                                     EXECUTIONS
------------------------------------------------------------ ----------
SELECT /* v26_dual */ 42 AS answer FROM DUAL                          1
SELECT /* v26_fromless */ 42 AS answer                                1

2 rows selected.

V$SQLやAWRでSQLを探すときは、FROM DUALの有無で別のSQLとして扱われる点に注意します。

順序とINSERT … SELECT

順序のNEXTVALと、INSERT … SELECTの問い合わせ部分でFROM DUALを省略しました。どちらも正常に実行され、2行が登録されました。

SQL> SELECT V26_BLOG.s1.NEXTVAL;

   NEXTVAL
----------
         1

1 row selected.

SQL> INSERT INTO V26_BLOG.t SELECT 1, 'fromless';

1 row created.

SQL> INSERT INTO V26_BLOG.t (id, name) SELECT V26_BLOG.s1.NEXTVAL, 'seq';

1 row created.

SQL> SELECT * FROM V26_BLOG.t ORDER BY id;

        ID NAME
---------- --------------------
         1 fromless
         2 seq

2 rows selected.

順序の採番やINSERT … SELECTでも、DUALなしで書けます。

副問合せ・WITH句・CASE式

カラー副問合せ、WITH句、CASE式をFROM DUALを省略して実行しました。いずれも想定どおりの値が返りました。

SQL> SELECT (SELECT MAX(id) FROM V26_BLOG.t) + 100 AS v;

         V
----------
       102

1 row selected.

SQL> WITH x AS (SELECT 10 AS a) SELECT a * 2 AS b FROM x;

         B
----------
        20

1 row selected.

SQL> SELECT CASE WHEN SYSDATE > DATE '2000-01-01' THEN 'Y' ELSE 'N' END AS c;

C
-
Y

1 row selected.

FROM DUALの省略は、副問合せやWITH句の中でも使えます。

WHERE句・UNION ALL・CONNECT BY

FROM DUALの省略のSELECTに、WHERE句・UNION ALL・CONNECT BY・COUNT(*)を組み合わせました。WHERE 1=0では0件、COUNT(*)は1が返りました。

SQL> SELECT 1 UNION ALL SELECT 2;

         1
----------
         1
         2

2 rows selected.

SQL> SELECT 1 WHERE 1=1;

         1
----------
         1

1 row selected.

SQL> SELECT 1 WHERE 1=0;

no rows selected

SQL> SELECT LEVEL CONNECT BY LEVEL <= 3;

     LEVEL
----------
         1
         2
         3

3 rows selected.

SQL> SELECT COUNT(*);

  COUNT(*)
----------
         1

1 row selected.

FROM DUALを省略したSELECTは、DUALと同じ「1行の表」として扱われます。

PL/SQLでの使い方

PL/SQLのSELECT INTOと、カーソルFORループの問い合わせでFROM DUALを省略しました。どちらも正常に値を取得できました。

SQL> DECLARE
      v DATE;
   BEGIN
         SELECT SYSDATE INTO v;
         DBMS_OUTPUT.PUT_LINE('PLSQL SELECT INTO OK: '||TO_CHAR(v,'YYYY-MM-DD HH24:MI:SS'));
     END;


PLSQL SELECT INTO OK: 2026-09-14 23:57:36

PL/SQL procedure successfully completed.

SQL> BEGIN
         FOR r IN (SELECT LEVEL AS n CONNECT BY LEVEL <= 3) LOOP
             DBMS_OUTPUT.PUT_LINE('cursor row: ' || r.n);
         END LOOP;
     END;

cursor row: 1
cursor row: 2
cursor row: 3

PL/SQL procedure successfully completed.

PL/SQLの中でも、FROM DUALを書く必要はありません。

FROM DUALを省略できないケース

SELECT *をFROM DUALを省略して実行しました。ORA-54058になりました。

SQL> SELECT * ;

SELECT *
*
ERROR at line 1:
ORA-54058: '*' not allowed in SELECT expressions without FROM clause

参照する表がないSELECT *だけは、FROM DUALを省略できません。

まとめ

FROM DUALを省略したSELECTは、FROM DUALを付けた場合と同じ処理を短く書ける構文になります。

FROM DUALの省略について
  • 実行計画はFROM DUALを付けた場合と同じFAST DUAL(Plan hash valueも同一)
  • consistent getsはどちらも0で性能差なし
  • V$SQLには書いたとおりの文が保存される
  • 順序・INSERT … SELECT・副問合せ・WITH句・PL/SQLでも使える
  • SELECT *だけはORA-54058になる

19c以前のデータベースでも同じSQLを動かす場合は、従来どおりFROM DUALを付けておくと安全です。

最後までご愛読ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次