Как перенести хранимые процедуры SQL Server с использованием временных таблиц или табличных переменных в Oracle?

9

Разработчик C # поощряется руководством для написания хранимых процедур SQL Server, часто производящих подобные процедуры

create table #t1 (...);
insert into #t1 Select ... from table_a where ...;
insert into #t1 Select ... from table_b where ...;
update #t1 Set ... = ... where ...
Select * from #t1;

Одно утверждение довольно просто, и этот метод заставляет их давать правильные результаты.

Часто моя задача - перенести такие процедуры в Oracle.

Давайте посмотрим правде в глаза следующие факты.

  • Разные временные таблицы в SQL Server полностью независимы и могут иметь любую специальную структуру.
  • Глобальные общие таблицы Oracle являются глобальными объектами, и все они используют одну и ту же структуру таблиц. Модифицировать эту структуру невозможно, пока она используется где угодно.

Одна из вещей, которую я узнал от Oracle dba, заключалась в том, чтобы избегать использования временных таблиц, когда это возможно. Даже производительность на сервере SQL выигрывает от таких изменений.

Заменить отдельные вставки на союзы

В самом простом случае вышеперечисленное можно преобразовать во что-то вроде

select case when ... then ... end, ... from table_a where ...
union
select case when ... then ... end, ... from table_b where ...
Order by ...;

Использование функций

Как скалярные функции, так и табличные функции могут помочь преобразовать вашу процедуру в один запрос приведенной выше формы.

Распространенные табличные выражения aka Subquery Factoring

Факторинг подзапроса - это почти лучшее, что Oracle может предложить, чтобы избежать временных таблиц. С его помощью миграция SQL Server в Oracle снова довольно проста. Это требует SQL Server 2005 и выше.


Эти изменения улучшают версию SQL Server и во многих случаях упрощают миграцию. В других случаях использование глобальных временных таблиц позволяет выполнять миграцию в ограниченное время, но это менее удовлетворительно.


Существуют ли другие способы избежать использования глобальных временных таблиц в Oracle?

bernd_k
источник
3
Я скажу, что такой код указывает на процедурное мышление, не основанное на установках. И это локальные временные таблицы с одним #. Я - менеджер, и я сломал бы ноги, если бы увидел, что это идет в производство :-)
gbn
Я полностью согласен
bernd_k
@gbn - Идиоматический PL / SQL имеет тенденцию быть немного более процедурным, чем идиоматический T-SQL. Временные таблицы позволяют делать почти все в заданных настройках в T-SQL. PL / SQL имеет параллельные операции с курсорами и намного больше функций для оптимизации процедурного кода.
ConcernedOfTunbridgeWells

Ответы:

3

Один из способов сделать это - Object Type , в этом случае тип будет аналогичен вашему #t1. Поэтому его нужно будет где-то определить, но он не должен быть глобальным, он может быть даже для каждой схемы или для каждой процедуры. Сначала мы можем создать тип:

SQL> create or replace type t1_type as object (x int, y int, z int)
  2  /

Type created.

SQL> create or replace type t1 as table of t1_type
  2  /

Type created.

Теперь настройте некоторые примеры данных:

SQL> create table xy (x int, y int)
  2  /

Table created.

SQL> insert into xy values (1, 2)
  2  /

1 row created.

SQL> insert into xy values (3, 4)
  2  /

1 row created.

SQL> commit
  2  /

Commit complete.

И создайте функцию над этими данными, возвращающую наш «временный» тип:

SQL> create or replace function fn_t1 return t1 as
  2  v_t1 t1 := t1();       -- empty temporary table (really an array)
  3  v_ix number default 0; -- array index
  4  begin
  5  for r in (select * from xy) loop
  6  v_ix := v_ix + 1;
  7  v_t1.extend;
  8  v_t1(v_ix) := t1_type(r.x, r.y, (r.x + r.y));
  9  end loop;
 10  return v_t1;
 11  end;
 12  /

Function created.

И наконец:

SQL> select * from the (select cast (fn_t1 as t1) from dual)
  2  /

         X          Y          Z
---------- ---------- ----------
         1          2          3
         3          4          7

Как вы можете видеть, это довольно неуклюже (и использует псевдофункции коллекций , что в лучшем случае является непонятной функцией!), Как я всегда говорю, перенос с БД на БД - это не просто синтаксис и ключевые слова в их диалектах SQL реальная трудность заключается в различных базовых допущениях (в случае с SQL Server курсоры стоят дорого, а их использование избегается / обходится любой ценой).

Gaius
источник
3

Если опция case недостаточно гибкая, вы можете массово собрать данные в вашей процедуре и манипулировать массивом (ами).

--Setup
CREATE TABLE table_a (c1 Number(2));
CREATE TABLE table_b (c1 Number(2));
INSERT INTO table_a (SELECT rownum FROM dual CONNECT BY rownum<=4);
INSERT INTO table_b (SELECT rownum+5 FROM dual CONNECT BY rownum<=4);

--Example
DECLARE
   Type tNumberArray Is Table Of Number;
   v1 tNumberArray;
BEGIN
   SELECT c1 BULK COLLECT INTO v1 FROM (
      SELECT c1 FROM table_a
      UNION ALL
      SELECT c1 FROM table_b
      );

   For x IN v1.First..v1.Last Loop
      /* Complex manipulation goes here. */
      If (v1(x) <= 3) Then
         v1(x) := v1(x)*10;
      End If;
      DBMS_OUTPUT.PUT_LINE(v1(x));
   End Loop;
END;
/
Ли Риффель
источник
+1, но это не возвращает набор результатов (если a SELECTявляется последним в хранимом процессе T-SQL, это то, что он возвращает)
Gaius
Если этот кодовый блок был превращен в процедуру, вы могли бы вернуть массив или открыть массивы как курсор и передать курсор назад, или даже сделать его функцией и вернуть строки обратно. Каждый из них будет похож, но то, что вы должны использовать, зависит от ситуации.
Ли Риффель