Wie migriere ich gespeicherte SQL Server-Prozeduren mithilfe temporärer Tabellen oder Tabellenvariablen nach Oracle?

9

Vom Management ermutigte C # -Entwickler, gespeicherte SQL Server-Prozeduren zu schreiben, erzeugen häufig solche Prozeduren

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;

Die einzelnen Anweisungen sind recht einfach und führen mit dieser Methode zu korrekten Ergebnissen.

Oft besteht meine Aufgabe darin, solche Prozeduren nach Oracle zu migrieren.

Stellen wir uns den folgenden Fakten.

  • Verschiedene temporäre Tabellen in SQL Server sind völlig unabhängig und können jede Ad-hoc-Struktur haben.
  • Globale allgemeine Oracle-Tabellen sind globale Objekte, und alle Verwendungszwecke haben dieselbe Tabellenstruktur. Das Ändern dieser Struktur ist nicht möglich, solange sie überall verwendet wird.

Eines der Dinge, die ich von einer Oracle-Datenbank gelernt habe, war, die Verwendung temporärer Tabellen zu vermeiden, wann immer dies möglich ist. Sogar die Leistung auf SQL Server profitiert von solchen Änderungen.

Ersetzen Sie die einzelnen Einsätze durch Gewerkschaften

Im einfachsten Fall kann das Obige in so etwas umgewandelt werden

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

Verwendung von Funktionen

Sowohl Skalarfunktionen als auch Tabellenwertfunktionen können dazu beitragen, Ihre Prozedur in eine einzige Abfrage des obigen Formulars umzuwandeln.

Allgemeine Tabellenausdrücke, auch bekannt als Subquery Factoring

Subquery Factoring ist fast das Beste, was Oracle zu bieten hat, um temporäre Tabellen zu vermeiden. Mit ihm ist die Migration von SQL Server zu Oracle wieder recht einfach. Dies erfordert SQL Server 2005 und höher.


Diese Änderungen verbessern die SQL Server-Version und machen in vielen Fällen die Migration einfach. In anderen Fällen ermöglicht der Rückgriff auf globale temporäre Tabellen die Migration in einer begrenzten Zeit, ist jedoch weniger zufriedenstellend.


Gibt es weitere Möglichkeiten, die Verwendung globaler temporärer Tabellen in Oracle zu vermeiden?

bernd_k
quelle
3
Ich werde sagen, dass ein solcher Code auf prozedurales, nicht satzbasiertes Denken hinweist. Und dies sind lokale temporäre Tabellen mit einem einzelnen #. Ich bin Management und ich würde mir die Beine brechen, wenn ich sehen würde, dass das in Produktion geht :-)
gbn
Ich stimme vollkommen zu
bernd_k
@gbn - Idiomatisches PL / SQL ist tendenziell etwas prozeduraler als idiomatisches T-SQL. Temp-Tabellen ermöglichen es, fast alles in Set-Ops in T-SQL auszuführen. PL / SQL verfügt über parallele Cursoroperationen und viel mehr Funktionen zur Optimierung des prozeduralen Codes.
ConcernedOfTunbridgeWells

Antworten:

3

Eine Möglichkeit, dies zu tun, wären Objekttypen . In diesem Fall wäre der Typ analog zu Ihrem #t1. Es müsste also irgendwo definiert werden, aber es müsste nicht global sein, es könnte sogar pro Schema oder pro Prozedur sein. Zuerst können wir einen Typ erstellen:

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.

Richten Sie nun einige Beispieldaten ein:

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.

Und erstellen Sie eine Funktion über diesen Daten, die unseren "temporären" Typ zurückgibt:

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.

Und schlussendlich:

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

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

Wie Sie sehen, ist dies ziemlich umständlich (und verwendet Sammlungspseudofunktionen , was im besten Fall eine undurchsichtige Funktion ist!), Wie ich immer sage, geht es beim Portieren von DB zu DB nicht nur um Syntax und Schlüsselwörter in ihren SQL-Dialekten Die eigentliche Schwierigkeit besteht in unterschiedlichen zugrunde liegenden Annahmen (im Fall von SQL Server sind Cursor teuer und ihre Verwendung wird um jeden Preis vermieden / umgangen).

Gaius
quelle
3

Wenn die case- Option nicht flexibel genug ist, können Sie die Daten in Ihrer Prozedur in großen Mengen erfassen und die Arrays bearbeiten.

--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
quelle
+1, aber dies gibt keine Ergebnismenge zurück (wenn a SELECTdas letzte in einem in T-SQL gespeicherten Prozess ist, gibt es das zurück)
Gaius
Wenn dieser Codeblock in eine Prozedur umgewandelt würde, könnten Sie das Array zurückgeben oder die Arrays als Cursor öffnen und den Cursor zurückgeben oder ihn sogar zu einer Funktion machen und die Zeilen zurückleiten. Jedes davon wäre ähnlich, aber welches Sie verwenden sollten, hängt von der Situation ab.
Leigh Riffel