【问题标题】:How to create shadow table in Informix如何在 Informix 中创建影子表
【发布时间】:2012-04-12 02:41:11
【问题描述】:

我正在开发一个审计跟踪模块,该模块要求我从原始表中创建一个庞大的影子表列表。我正在考虑编写一个可以从另一个表生成影子表的存储过程。该表可以是任何表,可以是包含 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 中,我可以将其复制到影子表中。

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

【问题讨论】:

    标签: database stored-procedures informix


    【解决方案1】:

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

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

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

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

    如果您有 IBM Informix Dynamic Server 11.70,您可以动态创建 CREATE {audit} TABLE 语句,然后执行该语句。因此,您将在存储过程中使用 FOREACH 循环来构建查询以依次添加每一列,然后执行该语句以创建审计表。您也必须解码类型。您也可以/应该为此使用程序。我假设tabname 是传递给存储过程的变量,而c_colnoc_colnamec_typename 是局部变量(就像cts 一样,是“创建表语句”的缩写,而@ 987654329@):

    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 和主键约束以及各种其他事情,但这为您提供了基础知识。

    【讨论】:

    • 请问如何在存储过程中执行cts语句?
    • 如果使用Informix 11.50 执行此操作,cts 变量将重写每一列(在每个 foreach 中),这是正常行为吗?
    • 我不知道(或忘记了)11.50 在存储过程中支持动态 SQL。撇开这一点不谈,该符号假定 cts 是 VARCHAR 或(更有可能)LVARCHAR 类型。如果是 CHAR,我认为您会遇到cts = cts || … 的截断问题,因为在将更多数据连接到cts 末尾之前不会删除末尾的空格。您可能需要使用cts = TRIM(cts) || …。但是,最终的意图是您应该在 cts 中使用单行 CREATE TABLE 语句。
    • 感谢@JonathanLeffler,我更改为LVARCHAR,并且工作正常,我添加type_name 程序作为另一个答案。
    【解决方案2】:

    除了JonathanLeffleranswer,还有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;
    

    归功于this

    【讨论】:

    • 谢谢。 CASE 2 正确吗?它不是总是返回“整数”吗?我认为应该是CASE coltype。同样在 CAST(2... 末尾的 ELSE 子句中。该代码不解码 DATETIME 或 INTERVAL;它也不能识别 [N]VARCHAR(255,3) 符号(我相信它会报告为 1023)。
    • 修复了CASE 2(用于调试目的)。在VARCHAR(M,N)的情况下,长度为1023,公式是否正确:M=colsize - 256 * NN=TRUNC(colsize/256)?,谢谢帮助,我是informix的新手。
    • 是的;低 8 位编码 [N]VARCHAR 列的总长度,高 8 位(通常为零)编码最小长度。
    猜你喜欢
    • 2014-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多