2012-04-12 14 views
3

Ich arbeitete an einem Audit-Trail-Modul, bei dem ich eine riesige Liste von Schattentabelle aus der ursprünglichen Tabelle erstellen musste. Ich dachte, eine gespeicherte Prozedur zu schreiben, die eine Schattentabelle aus einer anderen Tabelle generieren konnte. Diese Tabelle könnte eine beliebige Tabelle sein, sie könnte Tabelle_A sein, die aus 3 Feldern eines beliebigen Datentyps besteht, oder Tabelle_B, die aus 10 Feldern eines beliebigen Datentyps besteht und die auf dem Parameter basieren könnte, der in die Speicherprozedur übergeben wird.So erstellen Sie eine Schattentabelle in Informix

Ich weiß, dass ich eine Liste der Felder zur Verfügung, die insbesondere TABLE_A von syscolumns und systables wie dieses abrufen kann:

select * from syscolumns where tabid = (select tabid from systables where tabname='table_A') 

Es wird eine Liste der Felder aus dieser Aussage zurückkehrt, läßt Field_A sagen, dass ich dann Benennt dieses Feld_A um, um 2 neue Felder zu erstellen, die pre_field_A und post_field_A sind, und dann diese 2 neuen Felder zu verwenden, um eine Schattentabelle von shadow_table_A zu erstellen. Wenden Sie dieses Konzept auf die restlichen Felder an. Machen Sie sich keine Sorgen wegen des Datentyps eines Felds, da diese Information bereits in syscolumns vorhanden ist und ich sie einfach in die Schattentabelle übernehmen kann.

Ich bin derzeit fest, wie könnte ich die Liste der Werte zurück von der obigen Anweisung speichern, weil in der Regel jede Tabelle aus mehr als einem Feld bestehen wird. Kann es mit Array getan werden? oder eine alternative Lösung zum Erstellen einer Schattentabelle für Audit-Trail-Zwecke?

Antwort

3

Ihre Anfrage wird funktionieren, aber es könnte mehr idiomatische sein, ein Join zu verwenden:

SELECT * 
    FROM "informix".systables AS t 
    JOIN "informix".syscolumns AS c ON t.tabid = c.tabid 
WHERE t.tabname = 'table_a'; 

Auch darüber im Klaren sein, dass der Systemkatalog nur table_A in gemischten Fall enthalten, wenn Sie die Tabelle erstellt, während Sie hatten DELIMIDENT in der Umgebung festgelegt und Sie erstellten die Tabelle mit dem Namen in Anführungszeichen eingeschlossen. Normalerweise wird der Tabellenname im Systemkatalog in Kleinbuchstaben angezeigt. ähnlich für Spaltennamen.

Allerdings ist das alles tangential zu Ihrer Frage. Der Umgang mit benutzerdefinierten Typen in ihrer ganzen Pracht ist schmerzhaft. Wenn Sie jedoch mit normalen Datenbanken arbeiten, können Sie sicherlich so arbeiten, obwohl es einfacher sein könnte, DB-Schema (dbschema) zu verwenden, um das Schema für eine Tabelle zu generieren und dann das zu fangen. Sie könnten dies tatsächlich mithilfe einer gespeicherten Prozedur mithilfe der SYSTEM-Anweisung tun, aber ich würde es wahrscheinlich von außerhalb einer gespeicherten Prozedur tun. Es hängt davon ab, was Sie sonst noch tun müssen. Das Vorabbild und das Nachbild für jedes Feld könnten geringfügig kostspielig sein.

Wenn Sie IBM Informix Dynamic Server 11.70 verwenden, können Sie die CREATE {audit} TABLE-Anweisung dynamisch erstellen und dann die Anweisung ausführen. Daher würden Sie Ihre Abfrage mit einer FOREACH-Schleife in der gespeicherten Prozedur aufbauen, um nacheinander jede Spalte hinzuzufügen, und dann die Anweisung zum Erstellen der Audit-Tabelle ausführen. Sie müssen den Typ auch entschlüsseln. Sie könnten/sollten auch dafür eine Prozedur verwenden. Ich gehe davon aus, dass tabname eine Variable an die gespeicherte Prozedur übergeben wird, und c_colno, c_colname und c_typename sind lokale Variablen (wie cts, die Abkürzung für 'Anweisung create table' und pad):

LET cts = 'CREATE TABLE ' || tabname || '('; 
LET pad = ''; 
FOREACH SELECT c.colno, c.colname, type_name(c.coltype, c.collength) 
      INTO c_colno, c_colname, c_typename 
      FROM "informix".systables AS t 
      JOIN "informix".syscolumns AS c 
      ON t.tabid = c.tabid 
     WHERE t.tabname = tabname 
     ORDER BY c.colno 
    LET cts = cts || pad || 'pre_' || c_colname || ' ' || c_coltype; 
    LET cts = cts || ',' || 'post_' || c_colname || ' ' || c_coltype; 
    LET pad = ','; 
END FOREACH; 
LET cts = cts || ');'; 

Sie Vielleicht möchten Sie sich mit NOT NULL und Primärschlüssel-Constraints und allen möglichen anderen Dingen beschäftigen, aber dies gibt Ihnen die Grundlagen, mit denen Sie weitermachen können.

+0

Darf ich wissen, wie kann ich die cts-Anweisung in gespeicherte Prozedur ausführen? – huahsin68

+1

'EXECUTE IMMEDIATE cts;' –

+0

Wenn Sie dies mit 'Informix 11.50' ausführen, schreibt die' cts' Variable jede Spalte (in jeder 'foreach'), ist das das normale Verhalten? –

2

Neben JonathanLeffler ‚s answer gibt es die type_name Prozedur:

CREATE PROCEDURE type_name(coltype INTEGER, colsize INTEGER) 
      RETURNING VARCHAR(128); 
      DEFINE toRet     VARCHAR(128); 

      DEFINE size_5     VARCHAR(5); 
      DEFINE decimal_p, decimal_s  INTEGER; 
      DEFINE decimal_t    VARCHAR(16); 
      DEFINE varchar_m, varchar_n  INTEGER; 
      DEFINE varchar_t    VARCHAR(16); 

      LET size_5 = '(' || TRIM(CAST(colsize AS CHAR(5))) || ')'; 
      -- Precision 
      LET decimal_p = TRUNC(colsize/256); 
      -- Scale 
      LET decimal_s = colsize - 256 * decimal_p; 
      -- Decimal total 
      LET decimal_t = '(' || TRIM(CAST(decimal_p as VARCHAR(8))) || ',' || TRIM(CAST(decimal_s as VARCHAR(8))) || ')'; 

      -- VARCHAR(M,N) 
      LET varchar_n = decimal_p; 
      LET varchar_m = decimal_s; 
      LET varchar_t = '(' || TRIM(CAST(varchar_m as VARCHAR(8))) || ',' || TRIM(CAST(varchar_n as VARCHAR(8))) || ')'; 

      SELECT 
       CASE coltype 
        WHEN 0 THEN 'char' || size_5 
        WHEN 1 THEN 'smallint' 
        WHEN 2 THEN 'integer' 
        WHEN 3 THEN 'float' 
        WHEN 4 THEN 'smallfloat' 
        WHEN 5 THEN 'decimal' || decimal_t 
        WHEN 6 THEN 'serial' 
        WHEN 7 THEN 'date' 
        WHEN 8 THEN 'money' || decimal_t 
        WHEN 9 THEN 'null' 
        WHEN 10 THEN 'DATETIME YEAR TO FRACTION(3)' 
        WHEN 11 THEN 'byte' 
        WHEN 12 THEN 'TEXT' 
        WHEN 13 THEN 'VARCHAR' || varchar_t 
        WHEN 14 THEN 'INTERVAL' 
        WHEN 15 THEN 'NCHAR' || size_5 
        WHEN 16 THEN 'NVARCHAR' || varchar_t 
        WHEN 17 THEN 'INT8' 
        WHEN 18 THEN 'SERIAL8' 
        WHEN 19 THEN 'SET' 
        WHEN 20 THEN 'MULTISET' 
        WHEN 21 THEN 'LIST' 
        WHEN 22 THEN 'ROW' 
        WHEN 23 THEN 'COLLECTION' 
        WHEN 24 THEN 'ROWDEF' 
        WHEN 40 THEN 'LVARCHAR' || size_5 
        WHEN 256 THEN 'CHAR' || size_5 || ' NOT NULL' 
        WHEN 257 THEN 'SMALLINT NOT NULL' 
        WHEN 258 THEN 'INTEGER NOT NULL' 
        WHEN 259 THEN 'FLOAT NOT NULL' 
        WHEN 260 THEN 'SMALLFLOAT NOT NULL' 
        WHEN 261 THEN 'DECIMAL' || decimal_t || ' NOT NULL' 
        WHEN 262 THEN 'SERIAL NOT NULL' 
        WHEN 263 THEN 'DATE NOT NULL' 
        WHEN 264 THEN 'MONEY' || decimal_t || ' NOT NULL' 
        WHEN 265 THEN 'null NOT NULL' 
        WHEN 266 THEN 'DATETIME YEAR TO FRACTION(3) NOT NULL' 
        WHEN 267 THEN 'BYTE NOT NULL' 
        WHEN 268 THEN 'TEXT NOT NULL' 
        WHEN 269 THEN 'VARCHAR' || varchar_t || ' NOT NULL' 
        WHEN 270 THEN 'INTERVAL NOT NULL' 
        WHEN 271 THEN 'nchar(' || size_5 || ') NOT NULL' 
        WHEN 272 THEN 'nvarchar' || varchar_t || ' NOT NULL' 
        WHEN 273 THEN 'int8 NOT NULL' 
        WHEN 274 THEN 'serial8 NOT NULL' 
        WHEN 275 THEN 'set NOT NULL' 
        WHEN 276 THEN 'multiset NOT NULL' 
        WHEN 277 THEN 'list NOT NULL' 
        WHEN 278 THEN 'row NOT NULL' 
        WHEN 279 THEN 'collection NOT NULL' 
        WHEN 280 THEN 'rowdef NOT NULL' 
        WHEN 296 THEN 'LVARCHAR' || varchar_t || ' NOT NULL' 
        ELSE 'ERROR' 
       END datatype 
      INTO toRet 
      FROM systables 
      WHERE tabid = 1; 
      IF toRet = 'ERROR' THEN 
       RAISE EXCEPTION -746, 0, 'Unknow datatype ' || coltype; 
      END IF 

      RETURN toRet; 
    END PROCEDURE; 

Credit this.

+0

Danke. Ist der 'Fall 2' korrekt? Gibt es nicht immer 'Ganzzahl' zurück? Es sollte, denke ich, "CASE Coltype" sein. . Ähnlich ist es in der 'CAST (2 ...' im 'ELSE' Klausel am Ende der Code entschlüsseln nicht DATETIME- oder INTERVAL, es ist auch nicht erkennen, [N] VARCHAR (255,3) Notation (es wird berichten, dass als Größe 1023, glaube ich). –

+0

die 'CASE 2' behoben (es für Debug-Zwecke war). im Falle des VARCHAR (M, N)' die Länge '1023' ist, stimmt die Formel : 'M = colsize - 256 * N' und' N = TRUNC (colsize/256) ', Danke für die Hilfe, ich bin ein Newview mit Informix –

+0

Ja, die niedrigwertigen 8 Bits codieren die Gesamtlänge von a [N] VARCHAR-Spalte, und die höherwertigen 8 Bits (normalerweise Null) codieren die minimale Länge. –