4

我正在开发一个审计跟踪模块,该模块需要我从原始表中创建一个巨大的影子表列表。我正在考虑编写一个可以从另一个表生成影子表的存储过程。该表可以是任何表,它可以是包含 3 个任意数据类型字段的 table_A,或者包含 10 个任意数据类型字段的 table_B,并且可以基于传入存储过程的参数。

我知道我可以从 syscolumns 和 systables 中检索特定 table_A 可用的字段列表,如下所示:

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

该语句将返回一个字段列表,假设为 field_A,然后我将重命名此 field_A 以创建 2 个新字段,即 pre_field_A 和 post_field_A,然后使用这 2 个新字段创建 shadow_table_A 的影子表。将此概念应用于其他领域。不必担心字段的数据类型,因为这些信息已经存在于 syscolumns 中,我可以将其复制到影子表中。

我目前被困在如何存储从上述语句返回的值列表,因为通常任何表都包含多个字段。可以使用数组完成吗?或为审计跟踪目的创建影子表的任何替代解决方案?

4

2 回答 2

4

您的查询将起作用,但使用连接可能更惯用:

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

另外,请注意,table_A如果您在环境中设置了 DELIMIDENT 并且创建了名称用双引号括起来的表时创建了表,则系统目录将仅包含大小写混合。正常情况下,系统目录中的表名全部小写;对于列名也是如此。

但是,这与您的问题无关。处理所有荣耀的用户定义类型是痛苦的。但是,如果您处理的是普通数据库,当然可以这样工作,尽管使用 DB-Schema ( dbschema) 生成表的模式,然后捕获它可能更容易。您实际上可以通过使用 SYSTEM 语句的存储过程来做到这一点,但我可能会在存储过程之外进行。这确实取决于您还需要做什么。每个字段的前映像和后映像的成本可能适中。

如果您有 IBM Informix Dynamic Server 11.70,您可以动态创建 CREATE {audit} TABLE 语句,然后执行该语句。因此,您将在存储过程中使用 FOREACH 循环来构建查询以依次添加每一列,然后执行该语句以创建审计表。您也必须解码类型。您也可以/应该为此使用程序。我假设这tabname是一个传递给存储过程的变量,并且c_colno, c_colname, 和c_typename是局部变量(原样cts,'create table statement' 的缩写,和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 || ');';

您可能想要处理 NOT NULL 和主键约束以及各种其他事情,但这为您提供了基础知识。

于 2012-04-12T03:46:13.027 回答
3

除了JonathanLeffler回答之外,还有以下type_name程序:

    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;

归功于

于 2014-08-21T17:37:46.923 回答