忘れかけのIT備忘録

今まで学んできた知識や小技、なるほど!と思ったことをメモするブログです。

OracleのSELECT文の評価順序

今回はOracleのSELECT文の評価順序について調査、検証しました。

SELECT文は列別名を指定できる句がありますが、一部の句(WHERE句、GROUP BY句など)では列別名を指定できません。
理由はSELECT文で指定する句の評価順序があるためです。
SELECT文で指定する句は下記の順序で評価されます。

1. FROM
2. WHERE
3. GROUP BY
4. HAVING
5. SELECT
6. ORDER BY

列別名を定義するのはSELECT句であるため、SELECT句より前に評価される句では列別名が使用できません。
評価順序のルールを守らないと解析エラーが発生する、というのを検証して実際の動きを見てみます。

■検証環境
OS:Oracle Linux 6.5
DB/GI:Oracle Database 12c Release 1 (12.1.0.2.0) Enterprise Edition
※2ノードRAC(管理者管理型DB)

■前提
下記2テーブルを検証に使用します

SQL> select * from emp;

     EMPNO ENAME           JOB                    MGR HIREDATE                   SAL       COMM     DEPTNO
---------- --------------- --------------- ---------- ------------------- ---------- ---------- ----------
      7369 SMITH           CLERK                 7902 1980-12-17 00:00:00        800                    20
      7499 ALLEN           SALESMAN              7698 1981-02-20 00:00:00       1600        300         30
      7521 WARD            SALESMAN              7698 1981-02-22 00:00:00       1250        500         30
      7566 JONES           MANAGER               7839 1981-04-02 00:00:00       2975                    20
      7654 MARTIN          SALESMAN              7698 1981-09-28 00:00:00       1250       1400         30
      7698 BLAKE           MANAGER               7839 1981-05-01 00:00:00       2850                    30
      7782 CLARK           MANAGER               7839 1981-06-09 00:00:00       2450                    10
      7839 KING            PRESIDENT                  1981-11-17 00:00:00       5000                    10
      7844 TURNER          SALESMAN              7698 1981-09-08 00:00:00       1500          0         30
      7900 JAMES           CLERK                 7698 1981-12-03 00:00:00        950                    30
      7902 FORD            ANALYST               7566 1981-12-03 00:00:00       3000                    20
      7934 MILLER          CLERK                 7782 1982-01-23 00:00:00       1300                    10

SQL> select * from dept;

    DEPTNO DNAME           LOC
---------- --------------- ---------------
        10 ACCOUNTING      NEW YORK
        20 RESEARCH        DALLAS
        30 SALES           CHICAGO
        40 OPERATIONS      BOSTON

 

■検証パターン
①WHERE句に列別名を指定
②ORDER BY句に列別名を指定
③GROUP BY句に列別名を指定
④HAVING句に列別名を指定
⑤(番外編1)ON句に列別名を指定
⑥(番外編2)WHERE句に集計関数を指定

■検証
①WHERE句に列別名を指定
WHERE句に列別名を指定した状態でSELECT文を実行した場合の動きを検証します

【検証手順】
1. WHERE句に列別名を指定した状態でSELECT文を実行

【作業ログ】

1. WHERE句に列別名を指定した状態でSELECT文を実行
SQL> select empno as eno, ename, deptno from emp where eno = 7369;
select empno as eno, ename, deptno from emp where eno = 7369
                                                  *
行1でエラーが発生しました。:
ORA-00904: "ENO": 無効な識別子です。

SQL> --改善案
SQL> select empno as eno, ename, deptno from emp where empno = 7369;

       ENO ENAME               DEPTNO
---------- --------------- ----------
      7369 SMITH                   20

 

②ORDER BY句に列別名を指定
ORDER BY句に列別名を指定した状態でSELECT文を実行した場合の動きを検証します

【検証手順】
1. ORDER BY句に列別名を指定した状態でSELECT文を実行

【作業ログ】

1. ORDER BY句に列別名を指定した状態でSELECT文を実行
SQL> select empno as eno, ename, deptno from emp order by eno;

       ENO ENAME               DEPTNO
---------- --------------- ----------
      7369 SMITH                   20
      7499 ALLEN                   30
      7521 WARD                    30
      7566 JONES                   20
      7654 MARTIN                  30
      7698 BLAKE                   30
      7782 CLARK                   10
      7839 KING                    10
      7844 TURNER                  30
      7900 JAMES                   30
      7902 FORD                    20
      7934 MILLER                  10
★列別名は指定できる

 

③GROUP BY句に列別名を指定
GROUP BY句に列別名を指定した状態でSELECT文を実行した場合の動きを検証します

【検証手順】
1. GROUP BY句に列別名を指定した状態でSELECT文を実行

【作業ログ】

1. GROUP BY句に列別名を指定した状態でSELECT文を実行
SQL> select deptno as dno, count(*) from emp group by dno;
select deptno as dno, count(*) from emp group by dno
                                                 *
行1でエラーが発生しました。:
ORA-00904: "DNO": 無効な識別子です。

SQL> --改善案
SQL> select deptno as dno, count(*) from emp group by deptno;

       DNO   COUNT(*)
---------- ----------
        30          6
        20          3
        10          3

 

④HAVING句に列別名を指定
HAVING句に列別名を指定した状態でSELECT文を実行した場合の動きを検証します

【検証手順】
1. HAVING句に列別名を指定した状態でSELECT文を実行

【作業ログ】

1. HAVING句に列別名を指定した状態でSELECT文を実行
SQL> select deptno as dno, count(*) as dcnt from emp group by deptno having dcnt > 3;
select deptno as dno, count(*) as dcnt from emp group by deptno having dcnt > 3
                                                                       *
行1でエラーが発生しました。:
ORA-00904: "DCNT": 無効な識別子です。

SQL> --改善案
SQL> select deptno as dno, count(*) as dcnt from emp group by deptno having count(*) > 3;

       DNO       DCNT
---------- ----------
        30          6

 

⑤(番外編1)ON句に列別名を指定
検証の対象の句には上げていませんでしたが、表の結合でON句もよく使用するため、ON句に列別名を指定した状態でSELECT文を実行した場合の動きも検証しようと思います

【検証手順】
1. ON句に列別名を指定した状態でSELECT文を実行

【作業ログ】

1. ON句に列別名を指定した状態でSELECT文を実行
SQL> select e.empno as eno, e.ename, d.deptno as dno, d.dname from emp e inner join dept d on e.eno = d.dno;
select e.empno as eno, e.ename, d.deptno as dno, d.dname from emp e inner join dept d on e.eno = d.dno
                                                                                                 *
行1でエラーが発生しました。:
ORA-00904: "D"."DNO": 無効な識別子です。
★ON句も列別名は使用できない。SELECT句より前に評価されることが分かる(実際はFROM句とWHERE句の間で評価されるようです)

SQL> --改善案
SQL> select e.empno as eno, e.ename, d.deptno as dno, d.dname from emp e inner join dept d on e.deptno = d.deptno;

       ENO ENAME                  DNO DNAME
---------- --------------- ---------- ---------------
      7369 SMITH                   20 RESEARCH
      7499 ALLEN                   30 SALES
      7521 WARD                    30 SALES
      7566 JONES                   20 RESEARCH
      7654 MARTIN                  30 SALES
      7698 BLAKE                   30 SALES
      7782 CLARK                   10 ACCOUNTING
      7839 KING                    10 ACCOUNTING
      7844 TURNER                  30 SALES
      7900 JAMES                   30 SALES
      7902 FORD                    20 RESEARCH
      7934 MILLER                  10 ACCOUNTING

 

⑥(番外編2)WHERE句に集計関数を指定
こちらは列別名を指定するパターンではありませんが、条件を指定する句であるWHERE句とHAVING句のうち、なぜWHERE句では集計関数が指定できないのかも検証しようと思います

【検証手順】
1. WHERE句に集計関数を指定した状態でSELECT文を実行

【作業ログ】

1. WHERE句に集計関数を指定した状態でSELECT文を実行
SQL> select empno as eno, ename, deptno from emp where count(*) > 3;

select empno as eno, ename, deptno from emp where count(*) > 3
                                                  *
行1でエラーが発生しました。:
ORA-00934: ここではグループ関数は使用できません。
★WHERE句が評価される順番はGROUP BY句より前のため、WHERE句では集計関数を使用できない

 

■おわりに
整理すると下記のようになります。

ORACLE MASTERSQLの勉強などで「列別名を使用できる句、できない句が覚えられない」と言われることが結構ありますが、SELECT文の評価順序を意識すると覚えやすいかと思います。