Como migrar procedimentos armazenados do SQL Server usando tabelas temporárias ou variáveis ​​de tabela para Oracle?

9

O desenvolvedor de C # incentivado pelo gerenciamento a escrever procedimentos armazenados do SQL Server geralmente produz procedimentos 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;

A declaração única é bastante simples e esse método faz com que produzam resultados corretos.

Muitas vezes, minha tarefa é migrar esses procedimentos para o Oracle.

Vamos enfrentar os seguintes fatos.

  • Para diferentes tabelas temporárias no SQL Server são completamente independentes e podem ter qualquer estrutura ad hoc.
  • As tabelas comuns globais da Oracle são objetos globais e todos os usos compartilham a mesma estrutura de tabela. Modificar essa estrutura é impossível, enquanto é usado em qualquer lugar.

Uma das coisas que aprendi com um dba Oracle foi evitar o uso de tabelas temporárias sempre que possível. Até o desempenho no servidor SQL se beneficia dessas modificações.

Substitua as inserções individuais por uniões

No caso mais simples, o acima pode ser transformado em 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 funções

As funções escalares e as funções com valor de tabela podem ajudar a transformar seu procedimento em uma única consulta do formulário acima.

Expressões de tabela comuns, também conhecidas como Subquery Factoring

O Subquery Factoring é quase o melhor que a Oracle oferece para evitar tabelas temporárias. Usando isso, a migração do SQL Server para Oracle é novamente bastante fácil. Isso requer o SQL Server 2005 e superior.


Essas modificações aprimoram a versão do SQL Server e, em muitos casos, facilitam a migração. Em outros casos, o recurso a tabelas temporárias globais torna possível a migração em um tempo limitado, mas é menos satisfatório.


Existem outras maneiras de evitar o uso de tabelas temporárias globais no Oracle?

bernd_k
fonte
3
Eu direi que um código como esse é indicativo de pensamento procedimental e não definido. E essas são tabelas temporárias locais com um único #. Estou gestão e eu quebraria as pernas se eu vi que entrar em produção :-)
GBN
Eu concordo completamente #
11781 bernd_k
@gbn - O PL / SQL linguístico tende a ser um pouco mais processual do que o T-SQL idiomático. As tabelas temporárias possibilitam fazer quase tudo nas operações definidas no T-SQL. O PL / SQL possui operações paralelas de cursor e muito mais funcionalidade para otimizar o código processual.
ConcernedOfTunbridgeWells

Respostas:

3

Uma maneira de fazer isso seria Object Types , nesse caso, o tipo seria análogo ao seu #t1. Portanto, precisaria ser definido em algum lugar, mas não seria global, poderia ser por esquema ou por procedimento. Primeiro, podemos criar um 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.

Agora configure alguns dados de amostra:

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.

E crie uma função sobre esses dados retornando nosso tipo "temporário":

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.

E finalmente:

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

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

Como você pode ver, isso é bastante desajeitado (e usa pseudo-funções de coleção , o que é um recurso obscuro na melhor das hipóteses!), Como eu sempre digo, portar de DB para DB não é apenas sintaxe e palavras-chave em seus dialetos SQL , a dificuldade real ocorre em diferentes suposições subjacentes (no caso do SQL Server, que os cursores são caros e seu uso evitado / contornado a todo custo).

Gaius
fonte
3

Se a opção de caso não for flexível o suficiente, você poderá coletar os dados em massa no procedimento e manipular a (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
fonte
+1, mas isso não retorna um conjunto de resultados (se um SELECTé a última coisa em um proc T-SQL armazenado, que é o que ele retorna)
Caio
Se esse bloco de código fosse transformado em um procedimento, você poderia retornar a matriz ou abrir as matrizes como um cursor e passá-lo de volta, ou mesmo transformá-lo em uma função e canalizar as linhas de volta. Cada um deles seria semelhante, mas o que você deve usar dependeria da situação.
Leigh Riffel