SELECT INTO

SELECT INTO语句是SQL SELECT命令的SPL变体。

SELECT INTO与SQL SELECT区别如下:

  • SELECT INTO旨在将结果赋给变量或记录,可以在SPL程序语句中使用。

  • SELECT INTO的可访问结果集最多是一行。

除了上述之外,SELECT命令的所有子句(如WHERE、ORDER BY、GROUPBY、HAVING等)都对SELECT INTO有效。以下是SELECT INTO的两个变体。

  • SELECT select_expressions INTO target FROM ...;

    其中,target是简单变量的逗号分隔列表。select_expressions和语句的其余部分与SELECT命令相同。所选值必须在数据类型、数量和顺序方面与目标的结构完全匹配,否则会发生运行时错误。

  • SELECT * INTO record FROM table ...;

    其中,record是以前声明的记录变量。

如果查询返回零行,会将NULL值赋给目标。如果查询返回多行,会将第一行赋给目标并丢弃其余部分。

说明
  • “第一行”定义不明确,除非您使用ORDER BY。

  • 如果未返回一行或返回了多行,SPL会引发异常。

  • 存在使用BULKCOLLECT子句的SELECT INTO变体,允许包含返回到集合中的多行的结果集。

您可以在EXCEPTION块中使用WHEN NO_DATA_FOUND子句来确定赋值是否成功(也就是说,查询至少返回了一行)。

示例

  1. 执行如下语句,准备测试表格emp。

    CREATE TABLE emp (
        empno           NUMBER(4) NOT NULL CONSTRAINT emp_pk PRIMARY KEY,
        ename           VARCHAR2(10),
        job             VARCHAR2(9),
        mgr             NUMBER(4),
        hiredate        DATE,
        sal             NUMBER(7,2) CONSTRAINT emp_sal_ck CHECK (sal > 0),
        comm            NUMBER(7,2),
        deptno          NUMBER(2)
    );
    
    INSERT INTO emp VALUES (7369,'SMITH','CLERK',7902,'17-DEC-80',800,NULL,20);
    INSERT INTO emp VALUES (7499,'ALLEN','SALESMAN',7698,'20-FEB-81',1600,300,30);
    INSERT INTO emp VALUES (7521,'WARD','SALESMAN',7698,'22-FEB-81',1250,500,30);
    INSERT INTO emp VALUES (7566,'JONES','MANAGER',7839,'02-APR-81',2975,NULL,20);
    INSERT INTO emp VALUES (7654,'MARTIN','SALESMAN',7698,'28-SEP-81',1250,1400,30);
  2. 执行如下语句,创建emp_sal_query存储过程,使用将结果集返回到记录中的SELECT INTO变体,添加了包含WHEN NO_DATA_FOUND条件表达式的EXCEPTION块。

    CREATE OR REPLACE PROCEDURE emp_sal_query (
        p_empno         IN emp.empno%TYPE
    )
    IS
        r_emp           emp%ROWTYPE;
        v_avgsal        emp.sal%TYPE;
    BEGIN
        SELECT * INTO r_emp
            FROM emp WHERE empno = p_empno;
        DBMS_OUTPUT.PUT_LINE('Employee # : ' || p_empno);
        DBMS_OUTPUT.PUT_LINE('Name       : ' || r_emp.ename);
        DBMS_OUTPUT.PUT_LINE('Job        : ' || r_emp.job);
        DBMS_OUTPUT.PUT_LINE('Hire Date  : ' || r_emp.hiredate);
        DBMS_OUTPUT.PUT_LINE('Salary     : ' || r_emp.sal);
        DBMS_OUTPUT.PUT_LINE('Dept #     : ' || r_emp.deptno);
    
        SELECT AVG(sal) INTO v_avgsal
            FROM emp WHERE deptno = r_emp.deptno;
        IF r_emp.sal > v_avgsal THEN
            DBMS_OUTPUT.PUT_LINE('Employee''s salary is more than the '
                || 'department average of ' || v_avgsal);
        ELSE
            DBMS_OUTPUT.PUT_LINE('Employee''s salary does not exceed the '
                || 'department average of ' || v_avgsal);
        END IF;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            DBMS_OUTPUT.PUT_LINE('Employee # ' || p_empno || ' not found');
    END;
  3. 执行如下查询。

    • 如果使用不存在的员工编号执行查询:

      EXEC emp_sal_query(0);

      返回结果如下:

      Employee # 0 not found
    • 在SELECT INTO的EXCEPTION部分中使用了另一个条件子句TOO_MANY_ROWS异常。如果SELECT INTO语句选择了多行,则引发TOO_MANY_ROWS异常,因为指定的部门中有许多员工。

      DECLARE
          v_ename         emp.ename%TYPE;
      BEGIN
          SELECT ename INTO v_ename FROM emp WHERE deptno = 20 ORDER BY ename;
      EXCEPTION
          WHEN TOO_MANY_ROWS THEN
              DBMS_OUTPUT.PUT_LINE('More than one employee found');
              DBMS_OUTPUT.PUT_LINE('First employee returned is ' || v_ename);
      END;

      返回结果如下:

      More than one employee found
      First employee returned is JONES