通过 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