Август/Сентябрь 2003


Сделано в России


Павел Лузанов
Владимир Бегун

Understanding SQL*Plus. Использование переменных

Моей сестре Кате
Владимир Бегун
Лене и Лизе
Павел Лузанов

Источник: персональный сайт П.Лузанова (http://www.geocities.com/luzanovp/), 18 Марта 2003г., http://www.geocities.com/luzanovp/sp_vars.html

Переменные являются фундаментальной составляющей любого средства программирования. SQL*Plus не исключение и в этой статье мы рассмотрим использование переменных в этом продукте.

Системные переменные

Практически любой GUI продукт имеет в своем главном меню пункт с названием "Параметры" (иногда "Опции", "Свойства"). Бывает, что изучение нового продукта (или новой версии старого) начинается именно с этого пункта меню. Ведь именно здесь можно посмотреть какие установки можно задать, как можно настроить рабочую среду.

SQL*Plus использует для настройки среды системные переменные. Изменяя их значения, мы можем изменить поведение продукта в зависимости от поставленных задач и требований.

Установка

Для установки значений системным переменным используется команда SET:

scott@orcl> SET LINESIZE 120
scott@orcl> SET TIMING ON
scott@orcl> SET PAUSE ON
scott@orcl> SET PAUSE More...
scott@orcl> SET TERMOUT OFF PAGESIZE 0 FEEDBACK OFF VERIFY OFF

Из этого примера видно, что существуют системные переменные, окончательное значение которых определяется несколькими переданными параметрами (PAUSE, SERVEROUTPUT, AUTOTRACE и т.д.). А последняя строка показывает, что одной командой SET можно присвоить значение сразу нескольким переменным.

Просмотр

Логично предположить, что если есть способ установить значение системной переменной, то и должен быть способ посмотреть установленное значение. Это делается командой SHOW:

scott@orcl> SHOW LINESIZE
linesize 120
scott@orcl> SHOW TIMING PAUSE TERMOUT PAGESIZE FEEDBACK VERIFY

timing ON
PAUSE is ON and set to "More..."
termout OFF
pagesize 0
feedback OFF
verify OFF

Опять же отметим, что одной командой SHOW можно посмотреть значения сразу нескольких переменных.

А команда SHOW ALL покажет значения всех системных переменных. Более того, если внимательно сравнить результат работы этой команды с возможностями команды SET, то можно заметить, что SHOW позволяет получить больше, чем можно установить при помощи SET.

К таким дополнительным опциям команды SHOW относятся: USER SGA ERRORS PARAMETERS и некоторые другие.

Сохранение/восстановление среды

Разработка скриптов, как правило, начинается с установки необходимой рабочей среды, т.е. с установки значений системным переменным. Например, в скриптах выгружающих данные из таблиц БД в текстовые файлы, можно часто встретить следующее:

SET TERMOUT OFF ECHO OFF PAGESIZE 0 FEEDBACK OFF VERIFY OFF PAUSE OFF

Правила хорошего тона диктуют, что по окончании работы скрипта нужно вернуть значения измененных системных переменных в начальное состояние. Пути решения достаточно очевидны. Перед изменением переменных, нужно запомнить их начальные значения, а в конце работы восстановить эти запомненные значения.

И вот тут может возникнуть проблема. Точнее проблема возникает в том случае, если вы захотите запомнить значения отдельных переменных, например тех, которые будут изменены скриптом (что вообще-то вполне логично). Дело в том, что в SQL*Plus нет прямого способа запомнить значение переменной, а в последствии его восстановить.

Но SQL*Plus предлагает другой способ. Мы можем запомнить значения сразу всех системных переменных, затем изменить отдельные из них, а в последствии восстановить запомненные значения.

Для того чтобы запомнить значения всех системных переменных нужно воспользоваться командой STORE:

scott@orcl> STORE SET env.sql
Created file env.sql

Значения переменных сохраняются в файле, который мы указываем в качестве параметра команды STORE. В нашем случае это env.sql. Вот небольшой фрагмент этого файла:

set appinfo ON
set appinfo "SQL*Plus"
set arraysize 15
set autocommit OFF
set autoprint OFF
	...

Как видим, файл состоит из набора команд SET, выполнив которые можно установить текущие значения системных переменных.

Следовательно, если теперь изменить значения отдельных переменных, то в последствии для восстановления первоначальной среды, достаточно просто выполнить файл env.sql:

scott@orcl> SET ARRAYSIZE 10
scott@orcl> START env.sql
scott@orcl> SHOW ARRAYSIZE
arraysize 15

Пользовательские переменные

SQL*Plus предлагает к использованию два вида пользовательских переменных: переменные подстановки (substitution variables) и связанные переменные (bind variables). Рассмотрим их по порядку.

Переменные подстановки

Если попробовать продолжить классификацию переменных дальше, то переменные подстановки можно разделить на постоянные и временные.

Постоянные подстановочные переменные

Для создания переменных подстановки используется команда DEFINE (также можно использовать ACCEPT, но это тема для отдельного разговора):

scott@orcl> DEFINE tab = DEPT

Выполнив эту команду, мы определили переменную tab и присвоили ей значение DEPT. Теперь, можно использовать только что созданную переменную:

scott@orcl> SELECT * FROM &tab;
old   1: SELECT * FROM &tab

new   1: SELECT * FROM DEPT

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

scott@orcl>

Давайте посмотрим на то, что получилось.

Во-первых, перед именем переменной, мы поставили символ &. Это сделано для того, чтобы SQL*Plus мог понять, что tab это не имя таблицы, а имя переменной, значение которой нужно использовать в этом месте команды SELECT.

Во-вторых, сразу после команды SELECT и до вывода результатов запроса идут две строки, начинающиеся с old и new. Первая из них (old) показывает, как выглядела исходная строка запроса, какой её получил SQL*Plus. Вторая (new) - это то, как стала выглядеть эта строка после подстановки значения переменной tab. Именно в таком виде строка будет отправлена на сервер Oracle для исполнения. Вывод этих строк можно подавить, отключив системную переменную VERIFY, командой SET VERIFY OFF.

Важно понять, что переменная tab в нашем примере может быть использована не только для хранения имени таблицы. Она может содержать любое значение, после подстановки которого, получится допустимая команда SELECT:

scott@orcl> DEFINE tab = 'DEPT ORDER BY deptno DESC'
scott@orcl> SELECT * FROM &tab;
old   1: SELECT * FROM &tab
new   1: SELECT * FROM DEPT ORDER BY deptno DESC

    DEPTNO DNAME          LOC
---------- -------------- -------------
        40 OPERATIONS     BOSTON
        30 SALES          CHICAGO

        20 RESEARCH       DALLAS
        10 ACCOUNTING     NEW YORK

scott@orcl>

Теперь становится ясным истинный смысл названия переменная подстановки. Перед выполнением команды, SQL*Plus подставляет значение такой переменной в то место команды, где эта переменная встретилась.

Также, из этого примера видно, что для того, чтобы изменить значение переменной подстановки нужно снова выполнить команду DEFINE. Более того, для того чтобы проверить значение переменной подстановки, мы опять используем DEFINE varname, но уже без знака =

scott@orcl> DEFINE tab
DEFINE TAB             = "DEPT ORDER BY deptno DESC" (CHAR)

Всякий раз, выполняя команды, включающие &tab, SQL*Plus будет использовать значение этой переменной, и это будет происходить до тех пор, пока мы не выйдем из SQL*Plus или не удалим эту переменную командой UNDEFINE:

scott@orcl> UNDEFINE tab
scott@orcl> DEFINE tab
SP2-0135: symbol tab is UNDEFINED

Мы убедились, что переменная tab больше не существует.

Временные переменные подстановки

Но что будет, если выполнить команду, включающую переменную подстановки, заранее неопределенную командой DEFINE ?

scott@orcl> SELECT * FROM &tab;
Enter value for tab:

Оказывается в этом случае SQL*Plus попросит ввести значение для переменной tab. И именно введенное значение будет использовано в окончательном варианте команды SELECT:

scott@orcl> SELECT * FROM &tab;
Enter value for tab: dual
old   1: SELECT * FROM &tab

new   1: SELECT * FROM dual

D
-
X

Чему же теперь, после выполнения запроса, равно значение tab?

scott@orcl> DEFINE tab
SP2-0135: symbol tab is UNDEFINED

Переменная tab не существует! Это означает, что она была определена только на время выполнения команды SELECT.

Временные переменные подстановки удобно использовать в тех случаях, когда нужно выполнить подряд несколько почти одинаковых команд, заменив изменяющиеся части переменными подстановки:

scott@orcl> INSERT INTO emp (empno, deptno) VALUES (&empno, &deptno);
Enter value for empno: 1
Enter value for deptno: 10
old   1: INSERT INTO emp (empno, deptno) VALUES (&empno, &deptno)
new   1: INSERT INTO emp (empno, deptno) VALUES (1, 10)


1 row created.

scott@orcl> /
Enter value for empno: 2
Enter value for deptno: 30
old   1: INSERT INTO emp (empno, deptno) VALUES (&empno, &deptno)
new   1: INSERT INTO emp (empno, deptno) VALUES (2, 30)

1 row created.

Использование &&

В некоторых случаях нам может понадобиться использовать переменную подстановки несколько раз в одной и той же команде:

scott@orcl> SELECT &group_column, COUNT(*) FROM emp GROUP BY &group_column;
Enter value for group_column: deptno
Enter value for group_column: deptno
old   1: SELECT &group_column, COUNT(*) FROM emp GROUP BY &group_column
new   1: SELECT deptno, COUNT(*) FROM emp GROUP BY deptno

    DEPTNO   COUNT(*)
---------- ----------

        10          3
        20          5
        30          6

Как видим, SQL*Plus повторно запрашивает ввести значение переменной group_column, не смотря на то, что мы его только что вводили.

Для избежания повторного ввода, можно использовать два символа амперсанда перед именем переменной:

scott@orcl> SELECT &&group_column, COUNT(*) FROM emp GROUP BY &group_column;
Enter value for group_column: job
old   1: SELECT &&group_column, COUNT(*) FROM emp GROUP BY &group_column
new   1: SELECT job, COUNT(*) FROM emp GROUP BY job

JOB         COUNT(*)
--------- ----------
ANALYST            2
CLERK              4
MANAGER            3
PRESIDENT          1
SALESMAN           4

Теперь значение переменной group_column запрашивается лишь один раз. Это происходит потому, что при использовании && SQL*Plus сначала запрашивает значение несуществующей переменной у пользователя, аналогично &, а затем создаёт эту переменную, так, как это делается командой DEFINE. Встретив второй экземпляр переменной group_column, повторный ввод значения уже не требуется, а используется значение только что созданной переменной.

Мы можем убедиться в том, что переменная group_column теперь определена и последующие запросы с её использованием, не будут требовать ввода значения:

scott@orcl> DEFINE group_column
DEFINE GROUP_COLUMN    = "job" (CHAR)
scott@orcl> SELECT &group_column, COUNT(*) FROM emp GROUP BY &group_column;
old   1: SELECT &group_column, COUNT(*) FROM emp GROUP BY &group_column
new   1: SELECT job, COUNT(*) FROM emp GROUP BY job

JOB         COUNT(*)
--------- ----------
ANALYST            2
CLERK              4
MANAGER            3
PRESIDENT          1
SALESMAN           4

Предопределенные переменные

В предыдущих разделах мы увидели, как при помощи команды DEFINE создать новую переменную подстановки, изменить значение существующей переменной, вывести значение переменной на экран. При помощи этой же команды можно вывести на экран значения всех существующих переменных подстановки. Для этого нужно выполнить DEFINE без параметров. Давайте сделаем это сразу после запуска SQL*Plus:

 
c:\>sqlplus scott/tiger

SQL*Plus: Release 9.2.0.1.0 - Production on Mon Mar 3 23:42:41 2003

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production

scott@ora92> DEFINE
DEFINE _CONNECT_IDENTIFIER = "ora92" (CHAR)
DEFINE _SQLPLUS_RELEASE = "902000100" (CHAR)
DEFINE _EDITOR         = "c:\vim\vim60\gvim.exe" (CHAR)
DEFINE _O_VERSION      = "Oracle9i Enterprise Edition Release 9.2.0.1.0 - Produc
tion
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production" (CHAR)
DEFINE _O_RELEASE      = "902000100" (CHAR)
scott@ora92>

Мы видим, что несколько переменных уже существуют, хотя мы их не создавали (за исключением _EDITOR). Это переменные, которые были автоматически созданы SQL*Plus при старте. Вот их полный перечень (для версии 9.2):

_CONNECT_IDENTIFIER (Появилась в 9.2)
TNS алиас базы данных, с которой произведено соединение или SID/service_name (усечённое) если при запуске sqlplus использовалось:
c:\> sqlplus "u/p@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=myhost)(PORT=myport))(CONNECT_DATA=(SID=mysid)))" 
_SQLPLUS_RELEASE
Версия SQL*Plus
_O_VERSION (Доступна после соединения с БД)
Описание версии сервера Oracle и имеющихся опций
_O_RELEASE (Доступна после соединения с БД)
Версия сервера Oracle
_EDITOR
Редактор используемый командой EDIT. Эта переменная не создается при запуске SQL*Plus хотя и является предопределенной. Она может вообще отсутствовать, в этом случае SQL*Plus будет использовать платформно зависимые алгоритмы для определения редактора (в Windows будет использован системный редактор, в *nix проверяется окружение пользователя: переменные $EDITOR, затем $VISUAL и если они не установлены, то используется ed).
_RC
Также как и _EDITOR, эта переменная не создается при старте. Она создается после выполнения команды HOST и содержит код завершения команды операционной системы:
scott@orcl> DEFINE _RC
SP2-0135: symbol _rc is UNDEFINED
scott@orcl> HOST del afiedt.buf

scott@orcl> DEFINE _RC
DEFINE _RC             = "0" (CHAR)
scott@orcl> HOST ping hostnotexists
hostnotexists: неизвестный узел.

scott@orcl> DEFINE _RC
DEFINE _RC             = "1" (CHAR)
	

Эти переменные предоставляют дополнительную информацию о текущей SQL*Plus-сессии и являются самыми обычными переменными подстановки. Ничего не мешает нам изменить их значения или даже удалить, вопрос только в том - зачем?

Настройка

SQL*Plus имеет несколько системных переменных, которые влияют на использование переменных подстановки:

SET DEFINE {&|c|ON|OFF}
Определяет символ подстановки. Также позволяет отключить сканирование команды на наличие переменных подстановки.
Поклонники perl могут использовать SET DEFINE $
SET ESCAPE {\|c|ON|OFF}
Определяет символ, который ставится перед символом подстановки. В таком случае, SQL*Plus будет считать символ подстановки как обычный символ.
SET VERIFY {ON|OFF}
Определяет, нужно ли после выполнения подстановки выводить на экран значения старой и новой строки.
SET CONCAT {.|c|ON|OFF}
Определяет символ, который разделяет имя переменной подстановки от последующего текста, если этот текст должен начинаться сразу после значения переменной.
Если вы не переопределили эту переменную и сразу после значения переменной должна следовать точка (.), то используйте два символа точки подряд:
scott@orcl> DEFINE filename=myfile
scott@orcl> SPOOL &filename..lst 
В результате последующий вывод будет направлен в файл myfile.lst

Связанные переменные

Помимо переменных подстановки, SQL*Plus предлагает ещё один тип переменных - связанные переменные (хост-переменные).

Их главное предназначение - это использование в анонимных PL/SQL блоках. В отличие от переменных подстановки, которые лишь формируют окончательный вид выполняемой команды (PL/SQL блока в данном случае), связанные переменные используются как обычные переменные, т.е. внутри PL/SQL блока связанной переменной можно присвоить значение, сравнить это значение со значением других переменных и, что очень важно, использовать значение этой переменной в другом PL/SQL блоке:

scott@orcl> VARIABLE num_var NUMBER
scott@orcl> DECLARE
  2            dummy NUMBER := 10;
  3         BEGIN
  4            :num_var := dummy + 5;
  5            IF :num_var > 10
  6            THEN
  7               DBMS_OUTPUT.PUT_LINE ('num_var > 10');
  8            END IF;
  9         END;
 10  /
num_var > 10

PL/SQL procedure successfully completed.

scott@orcl> BEGIN
  2            :num_var := :num_var + 10;
  3            DBMS_OUTPUT.PUT_LINE ('num_var: ' || TO_CHAR(:num_var));
  4         END;
  5  /

num_var: 25

PL/SQL procedure successfully completed.

scott@orcl>

Как видим, связанные переменные создаются командой VARIABLE, а при использовании перед именем переменной ставится двоеточие. При создании переменной мы определяем тип данных, в данной случае, была объявлена числовая переменная. Помимо числовых можно объявлять переменные типов: CHAR, VARCHAR2, CLOB, REFCURSOR и некоторых других.

Если вы забыли, какого типа та или иная связанная переменная, то нам опять же поможет команда VARIABLE:

scott@orcl> VARIABLE num_var
variable   num_var
datatype   NUMBER

А чтобы получить полный список объявленных связанных переменных, то нужно выполнить эту команду без параметров:

scott@orcl> VARIABLE
variable   n
datatype   NUMBER

variable   vc30
datatype   VARCHAR2(30)

variable   vc255
datatype   VARCHAR2(255)


variable   rc
datatype   REFCURSOR

variable   num_var
datatype   NUMBER

Команда VARIABLE позволяет получить тип данных переменной, но не её значение.

Получить значение связанной переменной можно разными способами. Один из них мы видели в вышеприведенном примере, это использование пакета DBMS_OUTPUT внутри PL/SQL блока.

Помимо этого можно воспользоваться командой SQL*Plus - PRINT:

scott@orcl> PRINT num_var vc30

   NUM_VAR
----------
        25


VC30
--------------------------------
scott@orcl>

Т.е. PRINT может выводить на экран значения сразу нескольких переменных, ну а без параметров, как и можно предположить, она выведет значения всех переменных.

Здесь же нужно упомянуть системную переменную AUTOPRINT, установив которую в значение ON, мы добъемся того, что после каждого выполненного PL/SQL блока, SQL*Plus будет автоматически выполнять команду PRINT для всех связанных переменных, использованных в этом блоке:

scott@orcl> SET AUTOPRINT ON
scott@orcl> BEGIN :vc30 := 'Hello, World!'; END;
  2  /

PL/SQL procedure successfully completed.


VC30
--------------------------------
Hello, World!

Ну и напоследок мы оставили использование связанных переменных в командах SQL:

scott@orcl> EXECUTE :n := 10;

PL/SQL procedure successfully completed.

scott@orcl> SELECT empno, ename, deptno FROM emp WHERE deptno = :n;

     EMPNO ENAME          DEPTNO
---------- ---------- ----------
      7782 CLARK              10
      7839 KING               10
      7934 MILLER             10

Ничего необычного, всё отлично работает.

Но настораживает один факт. В документации по SQL*Plus версии 9, в разделах посвященных связанным переменным, вы не найдете ни одного упоминания о SQL, ни одного примера использования связанных переменных в командах SQL. Речь идет только о PL/SQL.

Мы не знаем, с чем это связано, но знаем, что в старых версиях продукта (в 3-ей, которая поставлялась с Oracle7) в определённых ситуациях связанные переменные в командах SQL работали некорректно.

Передача данных между переменными разных типов и базой данных

Переменные ничего бы не стоили, если бы им нельзя было присваивать значения. Ну а поскольку мы имеем дело с СУБД Oracle, то переменные бы ничего не стоили, если бы им нельзя было присваивать значения из базы данных.

Поэтому в этом разделе мы рассмотрим способы, позволяющие пользовательским переменным обоих типов получать значения из базы данных, а также обмениваться значениями между собой.

Со связанными переменными всё достаточно просто. Мы присваиваем им значения внутри анонимного PL/SQL блока как обычным PL/SQL переменным:

scott@orcl> VARIABLE version VARCHAR2(30)
scott@orcl> VARIABLE compatibility VARCHAR2(30)
scott@orcl> BEGIN dbms_utility.db_version (:version, :compatibility); END;
  2  /

PL/SQL procedure successfully completed.

scott@orcl> PRINT version compatibility

VERSION
--------------------------------
8.1.7.1.1


COMPATIBILITY

--------------------------------
8.1.0

С переменными подстановки немного сложнее. Им нельзя присвоить значение из базы данных при помощи анонимного PL/SQL блока. Но это можно сделать комбинацией из команды COLUMN... NEW_VALUE... и последующей SELECT:

scott@orcl> DEFINE rowcount = ''
scott@orcl> COLUMN total_rows NEW_VALUE rowcount
scott@orcl> SELECT COUNT(*) AS total_rows FROM emp;

TOTAL_ROWS
----------
        14

scott@orcl> DEFINE rowcount
DEFINE ROWCOUNT        =         14 (NUMBER)

В результате выполнения запроса, значение столбца total_rows присваивается переменной rowcount. Если запрос возвратит несколько строк, то переменной rowcount будет присвоено значение этого столбца для последней возвращенной строки:

scott@orcl> DEFINE rownum = 0
scott@orcl> COLUMN rownum NEW_VALUE rownum
scott@orcl> SELECT rownum, dname FROM dept;

    ROWNUM DNAME
---------- --------------
         1 ACCOUNTING
         2 RESEARCH
         3 SALES
         4 OPERATIONS

scott@orcl> DEFINE rownum

DEFINE ROWNUM          =          4 (NUMBER)

Этот приём уже настолько вошел в практику, что и не сразу вспомнишь, что опция NEW_VALUE (вместе с OLD_VALUE) команды COLUMN изначально предназначалась для удобства при создании мастер-детальных отчетов.

Теперь, что касается обмена значениями между переменными разных типов.

Опять же, присвоить связанной переменной значение переменной подстановки просто:

scott@orcl> DEFINE subst_var = 'Hello, World!';
scott@orcl> VARIABLE bind_var VARCHAR2(30)
scott@orcl> BEGIN :bind_var := '&subst_var'; END;
  2  /
old   1: BEGIN :bind_var := '&subst_var'; END;
new   1: BEGIN :bind_var := 'Hello, World!'; END;

PL/SQL procedure successfully completed.

scott@orcl> PRINT bind_var

BIND_VAR
--------------------------------
Hello, World!

Нужно лишь следить за наличием одинарных кавычек в переменной подстановки. Их наличие может привести к неправильному синтаксису результирующего PL/SQL блока.

Ну а для присвоения переменной подстановки значения связанной переменной, используем тот же трюк с COLUMN... NEW_VALUE...:

scott@orcl> DEFINE version = ''
scott@orcl> COLUMN version NEW_VALUE version NOPRINT
scott@orcl> SELECT :version AS version FROM DUAL;



scott@orcl> DEFINE version
DEFINE VERSION         = "8.1.7.1.1" (CHAR)

Примеры использования

Выгрузка табличных данных в текстовый файл

Часто можно услышать вопрос о том, какую утилиту/программу использовать для выгрузки данных из Oracle. Весьма достойную альтернативу написанию собственных "unloader-ов" на C, Java и даже Delphi представляет собой написание скрипта для SQL*Plus.

Нам же этот пример интересен тем, что в нём активно используются системные переменные SQL*Plus.

Оговоримся сразу, что речь не идет об универсальном скрипте для выгрузки данных из любой таблицы. Создание универсальной программы для выгрузки - весьма нетривиальная задача. Мало того, что нужно определиться с форматом вывода дат и чисел, нужно еще как-то обрабатывать такие типы данных как LOB-ы, LONG, RAW, XMLType, объектные типы и т.п.

Речь идет о примере скрипта, который может быть использован для выгрузки данных из таблицы emp. Данные выгружаются в формате, где отдельные столбцы разделяются друг от друга разделителем, в данном случае символом табуляции. Выходной файл имеет расширение xls, для того чтобы его можно было открыть в Excel.


REM Отключим всё, что может помешать процессу выгрузки данных 
REM или повлияет на выходной формат
SET TERMOUT OFF ECHO OFF PAGESIZE 0 FEEDBACK OFF VERIFY OFF PAUSE OFF

REM Установим длину строки достаточную для вывода одной записи
SET LINESIZE 80
SET TRIMSPOOL ON


REM Установим символ табуляции в качестве разделителя столбцов
COLUMN tabchar NEW_VALUE tabchar NOPRINT
SELECT CHR(9) AS tabchar FROM DUAL;
SET COLSEP '&tabchar'

REM Для примера возьмём лишь четыре столбца
COLUMN empno FORMAT 9999
COLUMN ename FORMAT a10
COLUMN hiredate FORMAT a10
COLUMN sal FORMAT 99999.99

REM Сохраним вывод в файле с расширением xls, для того чтобы
REM его можно было открыть в Excel
SPOOL emp.xls

SELECT empno,
       ename,
       TO_CHAR(hiredate, 'DD.MM.YYYY') AS hiredate,
       sal
  FROM 
       emp
 ORDER BY
       empno;

SPOOL OFF
EXIT

Если сохранить этот скрипт с именем emp.sql, то следующие команды позволят увидеть данные о сотрудниках в электронной таблице:

c:\>sqlplus -s scott/tiger @emp.sql
c:\>start emp.xls

Макроподстановки для часто используемых команд

Понятие макроподстановок существует во многих средствах разработки. В SQL*Plus для реализации макроподстановок можно использовать переменные подстановки.

Например, при работе с анонимными PL/SQL блоками в SQL*Plus часто приходится пользоваться процедурой DBMS_OUTPUT.PUT_LINE для вывода информации на экран. Каждый раз набирать эту последовательность символов - крайне утомительное занятие. Так почему бы ни поместить в файл login.sql следующую команду:

DEFINE p = DBMS_OUTPUT.PUT_LINE
Теперь мы можем заметно продлить жизнь клавиатуре:
scott@orcl> begin &p('Hello, World!'); end;
  2  /
Hello, World!

PL/SQL procedure successfully completed.

Другой пример. Попробуйте догадаться, что скрывается за переменной DBA_INVALIDS :

system@orcl> select &dba_invalids;

no rows selected

Ответ:

system@orcl> DEFINE dba_invalids = "owner, object_name, object_type -
> FROM dba_objects -
> WHERE status = 'INVALID' -
> ORDER BY owner, object_name"

Проверка существования переменной и установка значения по умолчанию

Скрипт nvl.sql проверяет существование переменной подстановки и если таковой еще нет, то задаёт ей значение по умолчанию. Если же такая переменная существует, то её значение остаётся неизменным. Как и следует из названия, действие скрипта похоже на действие функции NVL (а если быть ещё точнее, на действия встроенной процедуры DEFAULT_VALUE из Oracle Forms).

Например, если вы хотите установить в качестве редактора - vi, но только в том случае если редактор ещё не определен, то выполните следующее:

SQL> DEFINE nvl_name = '_EDITOR'
SQL> DEFINE nvl_value = 'vi'
SQL> @nvl

С одной стороны может показаться, что было бы более логично передать значения _EDITOR и vi в скрипт nvl.sql при помощи параметров:

SQL> @nvl _editor vi

но дело в том, что параметры скриптов сами являются переменными подстановки, и им точно также можно задавать значения по умолчанию скриптом nvl.sql! Таким образом, для того чтобы воспользоваться данным скриптом для проверки переменных 1 и 2, нужен другой способ передачи данных.

Но параметры, скрипты - это тема для отдельного разговора, к которой мы обязательно вернёмся. Что же касается файла nvl.sql, то вот он:


REM nvl.sql
REM
REM DESCRIPTION
REM    Emulates NVL() functionality for sqlplus substitution variables.
REM    Before running nvl.sql you need to define two variables:
REM       nvl_name  - a name of checked variable
REM       nvl_value - a value which will be assigned to the variable
REM                   if that is not defined yet
REM USAGE
REM    The following commands assign vi to _editor variable
REM    if _editor is not defined.
REM
REM    DEFINE nvl_name = '_EDITOR'
REM    DEFINE nvl_value = 'vi'
REM    @nvl
REM
REM RESTRICTIONS
REM    NVL_VALUE variable must not contain single quote character - '
REM    Use two single quote characters instead - ''
REM

REM Copyright(c) 2002,2003 Pavel Luzanov, Vladimir Begun
REM All Rights Reserved.
REM No warranty, the code is distributed "as is". This header
REM must not be removed in case of using the code or the algorithm.

SET TERMOUT OFF 

REM *nix command for deleting files
REM DEFINE del = 'rm -f'
REM Windows command for deleting files
DEFINE del = 'del'

REM
REM Define temp file name
REM
COLUMN hidden_column NEW_VALUE tmpfile NOPRINT
SELECT dbms_random.string('U', 8)||'.sql' AS hidden_column FROM dual;

REM
REM Save the output from DEFINE command into a file and then
REM load the file back to SQL buffer
REM
SPOOL &tmpfile
DEFINE &nvl_name
SPOOL OFF
GET &tmpfile NOLIST

REM
REM Here we suppose that nvl_name is NOT a multilined variable, therefore
REM all lines except first one are removed
REM
DEL 2 LAST

REM
REM If variable exists then do nothing
REM
CHANGE /DEFINE .../BEGIN null; END;/

REM
REM Else assign default value
REM
COLUMN hidden_column NEW_VALUE &nvl_name NOPRINT
CHANGE /SP2-.../SELECT '&nvl_value' AS hidden_column FROM dual/


REM
REM Run accumulated SQL buffer
REM
/

REM
REM Cleanup
REM
COLUMN hidden_column CLEAR
HOST &del &tmpfile
UNDEFINE nvl_name
UNDEFINE nvl_value
UNDEFINE tmpfile
UNDEFINE del
CLEAR BUFFER

Матричные отчеты

Этот пример показывает использование связанной переменной типа REFCURSOR для получения матричных отчётов.

Нижеприведенный скрипт позволяет получить информацию о заработной плате сотрудников в разрезах отделов и должностей.

VARIABLE rc REFCURSOR
DECLARE
   sql_v VARCHAR2(32767);
BEGIN
   sql_v := 'SELECT job';
   FOR rec IN (SELECT deptno, dname FROM dept)
   LOOP
      sql_v := sql_v ||',SUM(DECODE(deptno,'''||rec.deptno||''',sal,0)) AS "'||
                       rec.dname||'"';
   END LOOP;
   sql_v := sql_v || ' FROM emp GROUP BY job';
   OPEN :rc FOR sql_v;
END;
/
PRINT rc

JOB       ACCOUNTING   RESEARCH      SALES OPERATIONS
--------- ---------- ---------- ----------- ----------
ANALYST            0       6000          0          0
CLERK           1300       1900        950          0
MANAGER         2450       2975       2850          0
PRESIDENT       5000          0          0          0

SALESMAN           0          0       5600          0


18 Марта 2003г.

Владимир Бегун. Примечание.

The statements and opinions expressed here are my own and do not necessarily represent those of Oracle Corporation.

Copyright(c) 2002, 2003 Pavel Luzanov, Vladimir Begun
All Rights Reserved

You may use library or code examples in your non-commerical use subject to citing the original authors in your documentation and to understanding that NO WARRANTY is implied. For commerical use or purpose, you must contact the authors of any library or code example for permission.

An example of appropriate credit is:
"This product uses code for /whatever/ written by Pavel Luzanov and Vladimir Begun, which is being used by their permission."

E-mail this page