2014-11-12 2 views
0

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

В Oracle PL/SQL, однако, это совершенно другое дело. Мои CTE работают отлично, как прямой SQL, но как только я попытаюсь обернуть их как PL/SQL, я столкнулся с множеством проблем. По моему мнению, SELECT теперь нуждается в INTO, который будет удерживать результаты только одной записи. Тем не менее, я хочу, чтобы весь набор записей из нескольких значений.

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

Учитывая следующий пример:

Примечание: Я сильно я над упрощая SQL здесь. Я знаю, что приведенный ниже пример может быть выполнен в одном выражении SQL. Фактический SQL намного сложнее. Это основные принципы, которые я ищу здесь.

WITH A as (SELECT * FROM EMPLOYEES WHERE DEPARTMENT = 200), 

B as (SELECT * FROM A WHERE EMPLOYEE_START_DATE > date '2014-02-01'), 

C as (SELECT * FROM B WHERE EMPLOYEE_TYPE = 'SALARY') 

SELECT 'COUNTS' as Total, 
(SELECT COUNT(*) FROM A) as 'DEPT_TOTAL', 
(SELECT COUNT(*) FROM B) as 'NEW_EMPLOYEES', 
(SELECT COUNT(*) FROM C) as 'NEW_SALARIED' 
FROM A 
WHERE rowcount = 1; 

Теперь, если я хочу сделать это в PL/SQL с переменными, которые передаются или предопределенные в верхней части, это не простой вопрос об объявлении переменных, появляются значения в них, и изменяя мой жесткий -кодированные значения в переменные и их запуск. ПРИМЕЧАНИЕ. Я знаю, что я могу просто изменить жестко закодированные значения на такие переменные, как: Department,: StartDate и: Type, но опять же, я упрощаю пример.

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

1) Что было бы лучшим способом переписать это с помощью PL/SQL с объявленной переменной? CTE теперь должны что-то вникать. Но тогда я имею дело с одной строкой за раз, а не со всей таблицей. Таким образом, CTE «A» является одной строкой за раз, а CTE B будет видеть только одну строку, а не все результаты данных A и т. Д. Я знаю, что, скорее всего, мне придется использовать КУРСОРЫ для прохождения записи, которые каким-то образом, похоже, усложняют это.

2) Выход теперь должен использовать DBMS_OUTPUT. Для нескольких записей мне придется использовать КУРСОР с FETCH (или FOR ... LOOP). Да?

3) Собирается ли большая проблема с производительностью с этим прямолинейным SQL в отношении скорости и используемых ресурсов?

Спасибо заранее и снова, извиняюсь, если мне не хватает чего-то действительно очевидного здесь!

+0

пожалуйста просматривать, например. на http://stackoverflow.com/tags/plsql/info и начать читать серии Стивена Фейерштейна PL/SQL 101 (вы найдете ссылки в теге wiki). – user272735

ответ

1

Во-первых, это не имеет никакого отношения к CTE. Такое поведение было бы одинаковым с простым запросом select * from table. Разница в том, что с T-SQL запрос переходит в неявный курсор, который возвращается вызывающему. При выполнении SP из Management Studio это удобно. Набор результатов появляется в окне данных, как если бы мы выполнили запрос напрямую. Но это на самом деле нестандартное поведение. Oracle имеет более стандартное поведение, которое может быть указано как «набор результатов любого запроса, который не направлен в курсор, должен быть направлен на переменные.«При указании на переменные запрос должен возвращать только одну строку.

Чтобы дублировать поведение T-SQL, вам просто нужно явно объявить и вернуть курсор. Затем вызывающий код извлекает из курсора весь результат за исключением одной строки за раз. Вы не получаете удобство Sql Developer или PL/SQL Developer, отклоняющих набор результатов к окну отображения данных, но у вас не может быть всего.

Однако, поскольку мы Обычно пишут SP, которые нужно вызывать из IDE, проще работать с явными курсорами Oracle, чем неявные SQL Server. Просто google «oracle return ref cursor to caller», чтобы получить много хорошего материала.

1

простой способ состоит, чтобы обернуть его в неявный цикл

begin 
    for i in (select object_id, object_name 
       from user_objects 
       where rownum = 1) loop 
     -- Do something with the resultset 
     dbms_output.put_line (i.object_id || ' ' || i.object_name); 
    end loop; 
end; 

одного запроса строки без необходимости предварительного определения переменных.

+1

Как побочный комментарий - начиная с неявных циклов FOR 10, как показано в @OlafurTryggvason, автоматически использует семантику BULK COLLECT. Таким образом, по моему опыту, немного легче объявить курсоры, используя декларацию CURSOR. Вы должны использовать только SELECT ... INTO ... если вы абсолютно уверены, что выбранный SELECT вернет только одну строку. TSQL и PL/SQL - это разные звери, использующие разные модели программирования, но придерживайтесь их и после небольшого количества психической боли вы привыкнете к нему (PL/SQL, я имею в виду - не боль :-). Удачи. –

+0

Тогда почему бы не явным образом использовать инструкцию BULK COLLECT и FORALL ;-) Я бы хотел, чтобы плакаты упоминали свою версию Oracle, в частности, до четырех десятичных знаков, чтобы избежать путаницы, если таковые имеются. –

+0

Я использую только явный сбор массы с подпунктом Limit. –

Смежные вопросы