¿Cómo migrar los procedimientos almacenados de SQL Server usando tablas temporales o variables de tabla a Oracle?

9

El desarrollador de C # alentado por la gerencia para escribir procedimientos almacenados de SQL Server a menudo produce procedimientos como este

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;

La declaración única es bastante simple y este método hace que produzcan resultados correctos.

A menudo, mi tarea es migrar dichos procedimientos a Oracle.

Seamos realistas los siguientes hechos.

  • Las diferentes tablas temporales en SQL Server son completamente independientes y pueden tener cualquier estructura ad hoc.
  • Las tablas comunes globales de Oracle son objetos globales y todos los usos comparten la misma estructura de tabla. Es imposible modificar esta estructura, mientras se usa en cualquier lugar.

Una de las cosas que aprendí de un dba de Oracle fue evitar el uso de tablas temporales siempre que sea posible. Incluso el rendimiento en el servidor SQL se beneficia de tales modificaciones.

Reemplazar los insertos individuales por uniones

En el caso más simple, lo anterior se puede transformar en algo como

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

Uso de funciones

Tanto las funciones escalares como las funciones con valores de tabla pueden ayudar a transformar su procedimiento en una sola consulta del formulario anterior.

Expresiones de tabla comunes aka Subquery Factoring

Subgery Factoring es casi lo mejor que Oracle tiene para ofrecer para evitar tablas temporales. Usarlo de nuevo la migración de SQL Server a Oracle es bastante fácil. Esto requiere SQL Server 2005 y superior.


Estas modificaciones mejoran la versión de SQL Server y, en muchos casos, facilitan la migración. En otros casos, recurrir a tablas temporales globales permite realizar la migración en un tiempo limitado, pero es menos satisfactorio.


¿Existen otras formas de evitar el uso de tablas temporales globales en Oracle?

bernd_k
fuente
3
Diré que un código como ese es indicativo de un pensamiento basado en procedimientos no establecido. Y estas son tablas temporales locales con un solo #. Soy directivo y me rompería las piernas si viera que entra en producción: -)
gbn
Estoy completamente de acuerdo
bernd_k
@gbn - Idiomatic PL / SQL tiende a ser un poco más procesal que T-SQL idiomático. Las tablas temporales permiten hacer casi todo en operaciones establecidas en T-SQL. PL / SQL tiene operaciones de cursor paralelas y mucha más funcionalidad para optimizar el código de procedimiento.
Preocupado por

Respuestas:

3

Una forma de hacerlo sería Tipos de objetos , en este caso el tipo sería análogo a su #t1. Por lo tanto, tendría que definirse en alguna parte, pero no tendría que ser global, podría ser por esquema o incluso por procedimiento. Primero, podemos crear un tipo:

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.

Ahora configure algunos datos de muestra:

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.

Y cree una función sobre estos datos que devuelva nuestro tipo "temporal":

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.

Y finalmente:

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

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

Como puede ver, esto es bastante torpe (¡y usa pseudofunciones de colección , que es una característica oscura en el mejor de los casos!), Como siempre digo, la transferencia de DB a DB no se trata solo de sintaxis y palabras clave en sus dialectos SQL , la verdadera dificultad viene en diferentes supuestos subyacentes (en el caso de SQL Server, que los cursores son caros y su uso se evita / evita a toda costa).

Gayo
fuente
3

Si la opción de caso no es lo suficientemente flexible, puede recopilar los datos de forma masiva en su procedimiento y manipular la (s) matriz (s).

--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;
/
Leigh Riffel
fuente
1, pero esto no devuelve un conjunto de resultados (si una SELECTes la última cosa en un proc almacenado de T-SQL, que es lo que devuelve)
Gayo
Si este bloque de código se convirtiera en un procedimiento, podría devolver la matriz o abrir las matrices como un cursor y pasar el cursor hacia atrás, o incluso convertirlo en una función y volver a canalizar las filas. Cada uno de estos sería similar, pero el que debe utilizar dependerá de la situación.
Leigh Riffel