通过 db2-luw 上的 xmlquery 使用正则表达式执行更新语句时出现 SQL 错误

Posted

技术标签:

【中文标题】通过 db2-luw 上的 xmlquery 使用正则表达式执行更新语句时出现 SQL 错误【英文标题】:SQL-Error when executing update statement with regexp via xmlquery on db2-luw 【发布时间】:2019-10-21 15:18:37 【问题描述】:

给定一个数据库表 TABLE1,其列 COLUMN1 类型为 VARCHAR(1020)。 我需要将一些数据从可变长度格式重新格式化为新的固定长度格式(哈希被删除,数字用零填充,直到长度为 10):

例如:从“123#456#789”到“000000012300000004560000000789”。

执行给定的 sql 语句会导致以下错误消息,这不是很有帮助,只会指出连接断开:

SQL查询执行过程中发生错误

乌拉切: SQL-Fehler [08001]:[jcc][t4][2030][11211][4.22.29] Bei Operationen auf dem der Verbindung zugrunde liegendem Socket, im Socketeingabedatenstrom oder Socketausgabedatenstrom ist ein Kommunikationsfehler aufgetreten。

Fehlerposition:Reply.fill() - 数据不足 (-1)。 Nachricht: Unzureichende Daten。 ERRORCODE=-4499,SQLSTATE=08001

我正在开发一个 DB2-LUW 11.1 v10.5.0.5 数据库,其中除了 xmlqueries 之外不支持正则表达式。

当我用“return xs:string($COLUMN1)”替换第二个返回语句时,该语句执行得很好。所以这看起来不像是语法错误。

我发现一些信息表明 let 语句的顺序不固定。所以我尝试删除 if/then/else-logic,结果总是相同,所以似乎也不是问题。

UPDATE TABLE1
SET COLUMN1 = xmlcast(xmlquery(
        '
        if (fn:matches( $COLUMN1,"(\d0,10)#(\d1,10)#(\d1,10)")) 
            then
                xs:string($COLUMN1)
            else
                let $part1A := fn:replace($COLUMN1, "(\d0,10)#(\d1,10)#(\d1,10)", "$1", "i")
                let $part1B := fn:string-join(("0000000000", $part1A), "")
                let $part1C := fn:substring($part1B, fn:string-length($part1B) - 9)

                let $part2A := fn:replace($COLUMN1, "(\d0,10)#(\d1,10)#(\d1,10)", "$2", "i")
                let $part2B := fn:string-join(("0000000000", $part2A), "")
                let $part2C := fn:substring($part2B, fn:string-length($part2B) - 9)

                let $part3A := fn:replace($COLUMN1, "(\d0,10)#(\d1,10)#(\d1,10)", "$3", "i")
                let $part3B := fn:string-join(("0000000000", $part3A), "")
                let $part3C := fn:substring($part3B, fn:string-length($part3B) - 9)

                let $result := fn:string-join(($part1C, $part2C, $part3C), "")

                return xs:string($result)
        '
        passing COLUMN1 AS "COLUMN1"
    ) AS VARCHAR(1020))
    WHERE COLUMN1 IS NOT NULL AND LENGTH(COLUMN1 ) > 0;

期望是成功运行 sql 更新,而不是导致数据库连接断开的错误。

【问题讨论】:

【参考方案1】:

Db2 11.1 确实支持 XML 函数之外的正则表达式函数

例如REGEXP_LIKE

https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.1.0/com.ibm.db2.luw.sql.ref.doc/doc/r0061494.html

所以,我会使用基于类似这样的更新

SELECT
   RIGHT('000000000' || REGEXP_EXTRACT(t,'(\d0,10)#(\d1,10)#(\d1,10)',1,1,'',1),10)
|| RIGHT('000000000' || REGEXP_EXTRACT(t,'(\d0,10)#(\d1,10)#(\d1,10)',1,1,'',2),10)
|| RIGHT('000000000' || REGEXP_EXTRACT(t,'(\d0,10)#(\d1,10)#(\d1,10)',1,1,'',3),10)
FROM
    TABLE(VALUES('test123#456#789data')) AS T(T)

返回

 1
 ------------------------------
 000000012300000004560000000789

【讨论】:

我必须更正关于版本的问题。取决于阶段,我有不同的 db2 版本需要支持。支持的最小的是 DB2 v10.5.0.5,所以没有内置的正则表达式函数。 好的。请注意,Db1 10.5 将于 2020 年 4 月 30 日终止基本支持 另外,我建议尽量避免您的 XML 版本中的程序逻辑。 IE。也许尝试复制我上面的建议,但使用 XML(或您自己编译的正则表达式函数 developer.ibm.com/articles/… )作为 REGEXP_EXTRACTs 的直接替换 并且,对于您的“导致数据库连接中断的错误”,请向 IBM 支持部门提出。【参考方案2】:

这是适合我的解决方案。

第一步: 问题是 fn.matches() 中的 \d 有效,但在 fn:replace() 中无效。我不得不用 [0-9] 替换 \d。

SELECT COLUMN1 AS SOURCE, xmlcast(xmlquery(
        '
        if (not(fn:matches( $COLUMN1,"(^\d0,10)#(\d1,10)#(\d1,10)$"))) 
            then
                xs:string($COLUMN1)
            else
                let $part1A := fn:replace($COLUMN1, "([0-9]0,10)#([0-9]1,10)#([0-9]1,10)", "$1", "i")
                let $part1B := fn:string-join(("0000000000", $part1A), "")
                let $part1C := fn:substring($part1B, fn:string-length($part1B) - 9)

                let $part2A := fn:replace($COLUMN1, "([0-9]0,10)#([0-9]1,10)#([0-9]1,10)", "$2", "i")
                let $part2B := fn:string-join(("0000000000", $part2A), "")
                let $part2C := fn:substring($part2B, fn:string-length($part2B) - 9)

                let $part3A := fn:replace($COLUMN1, "([0-9]0,10)#([0-9]1,10)#([0-9]1,10)", "$3", "i")
                let $part3B := fn:string-join(("0000000000", $part3A), "")
                let $part3C := fn:substring($part3B, fn:string-length($part3B) - 9)

                let $result := fn:string-join(($part1C, $part2C, $part3C), "")

                return xs:string($result)
        '
        passing COLUMN1 AS "COLUMN1"
    ) AS VARCHAR(1020)) AS REPLACEDBY
FROM
    TABLE(VALUES('123#456#789'),('test123#456#789data')) AS TABLE1(COLUMN1);

返回

SOURCE              | REPLACEDBY
---------------------------------------------
123#456#789         | 000000012300000004560000000789
test123#456#789data | test123#456#789data

第二步:避免程序逻辑导致

SELECT 
    COLUMN1 AS SOURCE,
    RIGHT('000000000' || xmlcast(xmlquery('fn:replace($COLUMN1, "([0-9]0,10)#([0-9]1,10)#([0-9]1,10)", "$1", "i")' passing COLUMN1 AS "COLUMN1") AS VARCHAR(10)), 10)
    || RIGHT('000000000' || xmlcast(xmlquery('fn:replace($COLUMN1, "([0-9]0,10)#([0-9]1,10)#([0-9]1,10)", "$2", "i")' passing COLUMN1 AS "COLUMN1") AS VARCHAR(10)), 10)
    || RIGHT('000000000' || xmlcast(xmlquery('fn:replace($COLUMN1, "([0-9]0,10)#([0-9]1,10)#([0-9]1,10)", "$3", "i")' passing COLUMN1 AS "COLUMN1") AS VARCHAR(10)), 10)
    AS REPLACEDBY
FROM
    TABLE(VALUES('123#456#789'),('test123#456#789data'),('0#0#0')) AS TABLE1(COLUMN1)
WHERE 
    0 <> xmlcast(xmlquery('fn:matches($COLUMN1,"(^\d0,10)#(\d1,10)#(\d1,10)$")' passing COLUMN1 AS "COLUMN1") AS INTEGER);

返回

SOURCE              | REPLACEDBY
---------------------------------------------
123#456#789         | 000000012300000004560000000789
0#0#0               | 000000000000000000000000000000

【讨论】:

以上是关于通过 db2-luw 上的 xmlquery 使用正则表达式执行更新语句时出现 SQL 错误的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用 Python 连接 DB2 上的表时出错 (SQL0332N)

XML query() 有效,value() 需要找到单例 xdt:untypedAtomic

使用 JDBC 的 Xquery -- 错误的 SQL 语法

如何使用 linq-to-xml 查询简化此功能?

如何从常规 SQL 查询中输出 XML?

PHP脚本适用于localhost,但不适用于Web服务器