【问题标题】:How to select count from XML clob column?如何从 XML clob 列中选择计数?
【发布时间】:2021-06-29 13:23:24
【问题描述】:

我正在尝试选择 XML(CLOB) 列中的标记数。

我试过了

select regexp_count(diagram, 'userTask id=', 1, 'c') as "User Tasks",
       regexp_count(diagram, 'task id=', 1, 'c') as "Task"
from process_table

它有效,但我需要比使用 regexp_count 更快地获得输出。

我试过了:

select count(xt.task),
count(xt.userTask)
from process_table process
cross join xmltable(
            xmlnamespaces(default 'http://www.omg.org/spec/BPMN/20100524/MODEL'),
            '//definitions/process' passing xmltype(process.diagram) 
            columns
            task varchar2(20) path 'task',
            userTask varchar2(60) path 'userTask'
            ) xt

但是,我收到一个错误ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence

【问题讨论】:

  • 对 CLOB 使用正则表达式是否比通过 XMLTable 提取节点然后计算节点更快?你确定你不能从恰好包含表达式的文本节点的内容中得到错误的匹配吗?
  • 是的,错误匹配是使用 XMLTable 的另一个原因
  • 好的,你有没有尝试过这种方法,它甚至更慢;还是您无法使其正常工作?如果您需要这方面的帮助,请编辑您的问题以包含示例 XML 和预期输出、您当前的最佳尝试以及您遇到的问题/错误。
  • 我编辑了问题
  • 请注意,XML 中的属性可能以任何顺序出现。 <whateverElementuserTask id= 也会匹配,<userTask anotherAtribute="123" id= 不会

标签: sql oracle sql-optimization


【解决方案1】:

我认为将 XML 存储为 XMLTYPE 而不是 CLOB 会更好。然后您可以创建一个虚拟列,甚至可以在其上创建索引。像这样的:

CREATE TABLE PROCESS_TABLE (
    DIAGRAM XMLTYPE,
    TASK_COUNT      INTEGER GENERATED ALWAYS AS (
        TO_NUMBER(CAST(XMLQUERY('count(/definitions/process/task)' PASSING BY VALUE "DIAGRAM" RETURNING CONTENT) AS VARCHAR2(100)))
    ) VIRTUAL,
    USER_TASK_COUNT INTEGER  GENERATED ALWAYS AS (
        TO_NUMBER(CAST(XMLQUERY('count(/definitions/process/userTask)' PASSING BY VALUE "DIAGRAM" RETURNING CONTENT) AS VARCHAR2(100)))
    ) VIRTUAL
)
XMLTYPE DIAGRAM STORE AS SECUREFILE BINARY XML; 

CREATE INDEX IND_TASK_COUNT ON process_table (TASK_COUNT);

【讨论】:

  • 我测试了,在插入有任务的XML和userTasks到图表列后,task_count和user_task_count都是0。
  • 您有示例 XML 文件吗?我所做的XMLQUERY() 子句“又快又脏”看起来@Alex Poole 使用了正确的查询,试试他的。
【解决方案2】:

有几种方法可以做到这一点。一种是使用 XMLTable 查找任一子节点,获取节点名称,并计算每个子节点出现的次数:

select
  count(case when xt.name = 'userTask' then name end) as userTasks,
  count(case when xt.name = 'task' then name end) as tasks
from process_table process
cross join xmltable(
  xmlnamespaces(default 'http://www.omg.org/spec/BPMN/20100524/MODEL'),
  '//definitions/process/(userTask|task)' passing xmltype(process.diagram) 
  columns
    name varchar2(20) path 'name(.)'
) xt

或者您可以使用 FLWOR 表达式同时获取两种子节点类型的计数:

select userTasks, tasks
from process_table process
cross join xmltable(
  xmlnamespaces(default 'http://www.omg.org/spec/BPMN/20100524/MODEL'),
  'let $u := count(//definitions/process/userTask)
   let $s := count(//definitions/process/task)
   return <x><u>{$u}</u><s>{$s}</s></x>'
  passing xmltype(process.diagram) 
  columns
    userTasks number path 'u',
    tasks number path 's'
) xt

但我不确定最终会比第一个选项更快。

另一种选择是对每个节点使用单独的 XMLQuery XPath 计数来检查:

select
  xmlquery('declare default element namespace "http://www.omg.org/spec/BPMN/20100524/MODEL";
      count(//definitions/process/userTask)'
    passing xmltype(diagram)
    returning content) as userTasks,
  xmlquery('declare default element namespace "http://www.omg.org/spec/BPMN/20100524/MODEL";
      count(//definitions/process/task)'
    passing xmltype(diagram)
    returning content) as tasks
from process_table

db<>fiddle 使用一个简单的、抛出您看到的错误的 XML CLOB,以及这三种方法(包括将 XMLQuery 结果转换为数字)。

我也很想看看其他人的想法。


XPath 计数返回 ORA-06502:PL/SQL:超过 10 行时出现数值或值错误。当我添加 where 子句时效果很好

当我添加 id = some_number 或显示 10 行时,它可以工作。

如果处理的行有空diagram,这将出错;但是您可以通过添加 where diagram is not null 来排除这些。

db<>fiddle

【讨论】:

  • 非常感谢!当行数超过 10 行时,XPath 计数返回 ORA-06502: PL/SQL: numeric or value error。当我添加 where 子句时效果很好
  • @Samuel - 我不知道怎么做;我不能让它抛出那个错误。您使用 where 子句过滤掉的 CLOB 有什么不同吗?
  • 当我添加 id = some_number 或显示 10 行时,它可以工作。我不知道为什么:(
  • D'oh,我实际上并没有使用 CLOB ......所以你有 diagram 为空的行吗?这将引发该错误;但是你可以过滤where diagram is not null?
  • 是的,我有,现在完美运行。为什么count(null)返回0会报错?
猜你喜欢
  • 1970-01-01
  • 2011-06-20
  • 2018-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-29
  • 1970-01-01
  • 2022-07-21
相关资源
最近更新 更多