【问题标题】:why this oracle table column accepting data bigger than initial size?为什么这个 oracle 表列接受大于初始大小的数据?
【发布时间】:2016-01-17 23:42:52
【问题描述】:

我是 oracle sql 和 pl sql 的新手。我编写了 pl sql 函数,它以 xml 格式从 php 获取数据。这是下面的 pl sql 代码,它工作正常:

FUNCTION save_overtime(overtime_data NVARCHAR2 ) RETURN clob IS ret clob;
   xmlData XMLType;

   v_code  NUMBER;
   v_errm  VARCHAR2(100);

   BEGIN 
   xmlData:=XMLType(overtime_data);
   INSERT INTO TBL_OVERTIME SELECT x.* FROM XMLTABLE('/overtime'
                                                PASSING xmlData
                                                COLUMNS OT_EMPID   NVARCHAR2(10)     PATH   'employee_id',
                                                        OT_DATE   DATE     PATH    'date',                                                        
                                                        OT_HOUR   NUMBER(4,0)    PATH    'overtime_hour'                                                        
                                                        ) x;


     ret:=to_char(sql%rowcount);
COMMIT;

RETURN '<result><status affectedRow='||ret||'>success</status></result>';
EXCEPTION
WHEN OTHERS THEN
v_code := SQLCODE;
v_errm := SUBSTR(SQLERRM, 1, 100);
DBMS_OUTPUT.PUT_LINE (v_code || ' ' || v_errm);
-- '<result><status>Error</status> <error_message>'|| 'Error Code:' || v_code || ' ' || 'Error Message:' || v_errm ||'</error_message> </result>';
RETURN '<result><status>Error</status> <error_message>'|| 'Error Message:' || v_errm ||'</error_message> </result>';

END save_overtime;

在插入数据时,我注意到 oracle 表也在接受大于其初始大小的数据! 例如,以下列接受大于 10 的数据。

 EMPID   NVARCHAR2(10) 

如果数据长度为 12,那么它需要第一个 10 个字符。但对我来说,我认为 它应该是无效/错误。因为,否则定义列大小是没有意义的。我知道添加约束。但是为这样的小事情添加许多约束非常无聊和乏味。如果我做错了/错误,那么请帮助我明白了。谢谢

【问题讨论】:

    标签: oracle plsql


    【解决方案1】:

    插入不允许并静默截断超过列大小的值。 XMLTable columns 子句正在截断;该数据类型表示 XMLTable 投影将返回的内容,并不意味着将根据数据类型大小限制验证指定路径处的值。向表中添加约束不会有任何区别。

    如果您只是从 XMLTable 表达式查询而不是插入,则可以看到截断:

    var overtime_data nvarchar2(4000);
    exec :overtime_data := '<overtime><employee_id>invalidvalue</employee_id><date>2016-01-15</date><overtime_hour>1234.5</overtime_hour></overtime>';
    
    select x.ot_empid, x.ot_date, x.ot_hour
    from xmltable('/overtime'
      passing xmltype(:overtime_data)
      columns ot_empid nvarchar2(10) path 'employee_id',
        ot_date date path 'date',
        ot_hour number(4,0) path 'overtime_hour'
    ) x;
    
    OT_EMPID   OT_DATE      OT_HOUR
    ---------- --------- ----------
    invalidval 15-JAN-16       1235
    

    XML 元素中的 12 个字符的值被截断以适应 columns 子句。另请注意,小时数已四舍五入以适应受限数字类型 - 1234.5 向上舍入为 1235。(只要该值可以转换为指定的精度,它就不会抱怨。本质上这意味着只要小数点前不超过四位将被四舍五入;如果小数点前有五位或更多位,则会得到 ORA-01438。)

    如果您尝试插入这些值,它不会报错,因为它们对表定义有效:

    insert into tbl_overtime (ot_empid, ot_date, ot_hour)
    select x.ot_empid, x.ot_date, x.ot_hour
    from xmltable('/overtime'
      passing xmltype(:overtime_data)
      columns ot_empid nvarchar2(10) path 'employee_id',
        ot_date date path 'date',
        ot_hour number(4,0) path 'overtime_hour'
    ) x;
    
    1 row inserted.
    

    如果您不打算在插入之前验证或操作该值,并且您确实希望它在 XML 节点值超过 10 个字符时出错,那么使 XMLTable 列数据类型甚至大一个字符都可以. (如果您要验证过程中的值,您可以将其放大很多;不过,这里任何超过 10 的都可以)。

       INSERT INTO TBL_OVERTIME SELECT x.* FROM XMLTABLE('/overtime'
                                                    PASSING xmlData
                                                    COLUMNS OT_EMPID   NVARCHAR2(11)     PATH   'employee_id',
                                                            OT_DATE   DATE     PATH    'date',        
                                                            OT_HOUR   NUMBER(4,0)    PATH    'overtime_hour'                                                        
                                                            ) x;
    

    如果路径中的值超过 10 个字符,x.ot_empid 值将被截断为 11 个字符,然后插入将失败,因为这对于实际表列来说太大了。

    用与之前相同的数据进行演示;

    select x.ot_empid, x.ot_date, x.ot_hour
    from xmltable('/overtime'
      passing xmltype(:overtime_data)
      columns ot_empid nvarchar2(11) path 'employee_id',
        ot_date date path 'date',
        ot_hour number(4,0) path 'overtime_hour'
    ) x;
    
    OT_EMPID    OT_DATE      OT_HOUR
    ----------- --------- ----------
    invalidvalu 15-JAN-16       1235
    

    请注意,ot_empid 列现在有 11 个字符。现在插入失败:

    insert into tbl_overtime (ot_empid, ot_date, ot_hour)
    select x.ot_empid, x.ot_date, x.ot_hour
    from xmltable('/overtime'
      passing xmltype(:overtime_data)
      columns ot_empid nvarchar2(11) path 'employee_id',
        ot_date date path 'date',
        ot_hour number(4,0) path 'overtime_hour'                                                        
    ) x;
    
    Error report -
    SQL Error: ORA-12899: value too large for column "SCHEMA"."TBL_OVERTIME"."OT_EMPID" (actual: 11, maximum: 10)
    

    【讨论】:

    • 谢谢@Alex Poole,你的解释让我明白了它的概念。你的技巧非常好,而且很有效。但是我注意到 xml 正确地遵循了 NUMBER(4,0) 类型的大小限制。问题出在 NVARCHAR2 类型上。你能告诉为什么 xml 表正确遵循 NUMBER(4,0) 类型的大小限制。谢谢。
    • @user5005768 - 如果超过指定的数字精度,它将出现 ORA-01438 错误,例如如果 XML 元素包含 10000 个,即使您只有一个没有插入的选择。如果您有一个像 9999.999 这样的值作为精度匹配,它不会出错,并且超出指定比例的有效数字将被四舍五入。 Read more about scale and precision.
    • 非常感谢@Alex Poole。您能否也访问我在 oracle xml 上的另一篇帖子stackoverflow.com/questions/34855163/…
    猜你喜欢
    • 2019-03-21
    • 2020-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 1970-01-01
    相关资源
    最近更新 更多