
- 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を付ける必要がありました。
本記事では、「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.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.最後に、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 processedV$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.順序と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.
副問合せ・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.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.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.FROM DUALを省略できないケース
SELECT *をFROM DUALを省略して実行しました。ORA-54058になりました。
SQL> SELECT * ;
SELECT *
*
ERROR at line 1:
ORA-54058: '*' not allowed in SELECT expressions without FROM clause
まとめ
FROM DUALを省略したSELECTは、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を付けておくと安全です。
最後までご愛読ありがとうございました。
