带有多个COUNT(DISTINCT xxx)的PL / SQL SELECT - 意外结果

时间:2011-06-08 06:01:43

标签: sql xml oracle count distinct

我正在尝试整理Oracle 11g应用程序的查询,但我遇到了问题。

我将简化真实场景,以便更容易理解(以及保护客户端的数据):

  • 表A是基表。它有一个已知的标识符,我将其传递给查询。
  • 对于表A中的每个条目,表B中可能有多个条目。表B包含我感兴趣的值。
  • 对于表B中的每个条目,表C中也可能有多个条目。表C包含我感兴趣的另一个值。
  • 我还有一个XML代码段,其中包含可能与表C中感兴趣的值匹配或不匹配的值列表。
  • 查询对XML进行外连接,这样如果有匹配值,它将再次返回该值,否则为null。

我想要做的是获取我传入的标识符,B和C中唯一值的计数,以及来自连接的XML部分的唯一(和非空)值的计数

我目前的查询是:

SELECT
    a.ID
  , COUNT(DISTINCT b.VAL) AS B_VAL
  , COUNT(DISTINCT c.VAL) AS C_VAL
  , COUNT(DISTINCT xml.VAL) AS XML_VAL
FROM a, b, c,
  XMLTABLE('/field1/collection/value' passing my_xml_type
    COLUMNS VAL VARCHAR2(50) PATH '.') xml
WHERE
      a.ID = b.SOME_ID
  AND b.OTHER_ID = c.OTHER_ID
  AND c.VAL = xml.VAL (+)

现在,如果您忘记计数并返回行,示例结果集可能如下所示:

ID     B_VAL     C_VAL     XML_VAL
---------------------------------------
X      abc       123       123
X      abc       456       null
X      abc       789       789
X      abc       789       789

渴望:现在,当我想要执行不同的计数时,我希望它能够返回:

ID     B_VAL     C_VAL     XML_VAL
---------------------------------------
X      1         3         2

实际然而,当我将它们全部作为COUNT(DISTINCT ......)时,这就是我所得到的:

ID     B_VAL     C_VAL     XML_VAL
---------------------------------------
X      1         1         1

ALTERNATIVE: ...如果我从计数中取出DISTINCT,那么我得到:

ID     B_VAL     C_VAL     XML_VAL
---------------------------------------
X      1         4         3

为什么DISTINCT似乎只在特定的B_VAL中运行,但是将其取出导致它在所有行中运行但不考虑唯一性?

是否有另一种方法可以不必将所有连接复制为子查询?我完全错过了这一点吗?

(请注意,我根本不是数据库开发人员,我刚刚被提供帮助,对不起,如果这是一个简单的问题...我已经搜索了谷歌并浏览了这个网站以获得答案但是张贴!)

感谢。


我发现,如果我将XML表连接出来,那么整个B_VAL和C_VAL上的count distinct工作正常......那么,对于Oracle如何处理XML表连接,这可能是奇怪的?

2 个答案:

答案 0 :(得分:4)

我无法使用Oracle 10.2.0.3重现您的发现。

这是我的设置:

SQL> CREATE TABLE a AS SELECT 'X' ID FROM dual;

Table created

SQL> CREATE TABLE b AS SELECT 'abc' val, 'X' some_id, 1 other_id FROM dual;

Table created

SQL> CREATE TABLE c AS
  2     SELECT 1 other_id, '123' val,
  3            XMLTYPE('<field1>
  4                        <collection><value>123</value></collection>
  5                     </field1>') my_xml_type
  6       FROM dual UNION ALL
  7     SELECT 1 other_id, '456' val, NULL FROM dual UNION ALL
  8     SELECT 1 other_id, '789' val,
  9            XMLTYPE('<field1>
 10                        <collection><value>789</value></collection>
 11                        <collection><value>789</value></collection>
 12                     </field1>') my_xml_type
 13       FROM dual;

Table created

查询返回正确的结果:

SQL> SELECT
  2      a.ID
  3    , COUNT(DISTINCT b.VAL) AS B_VAL
  4    , COUNT(DISTINCT c.VAL) AS C_VAL
  5    , COUNT(DISTINCT xml.VAL) AS XML_VAL
  6  FROM a, b, c
  7     , XMLTABLE('/field1/collection/value' passing my_xml_type
  8                 COLUMNS VAL VARCHAR2(50) PATH '.') xml
  9  WHERE a.ID = b.SOME_ID
 10    AND b.OTHER_ID = c.OTHER_ID
 11    AND c.VAL = xml.VAL (+)
 12  GROUP BY a.id;

ID      B_VAL      C_VAL    XML_VAL
-- ---------- ---------- ----------
X           1          3          2

你可以运行这个测试用例吗?

答案 1 :(得分:4)

由于Vincent的测试用例适用于10.2.0.3和11.2.0.2,如果您使用的是11g的早期版本,则可能是错误8816675:XMLexists查询使用选择的DISTINCT返回错误的结果。错误中的示例是指count(distinct)的问题。您没有明确使用XMLexists,但该错误可能会比标题所暗示的更广泛,或者它可能会在幕后使用。

如果这是问题所在,并且你无法修补,你可以通过包装非计数版本来解决它,这仍然不是很好:

SELECT
    A_ID
    , COUNT(DISTINCT B_VAL) AS B_VAL
    , COUNT(DISTINCT C_VAL) AS C_VAL
    , COUNT(DISTINCT XML_VAL) AS XML_VAL
FROM (
SELECT a.ID as A_ID, b.VAL as B_VAL, c.VAL as C_VAL, xml.VAL as XML_VAL
FROM a, b, c
    , XMLTABLE('/field1/collection/value' passing my_xml_type
        COLUMNS VAL VARCHAR2(50) PATH '.') xml
WHERE a.ID = b.SOME_ID
AND b.OTHER_ID = c.OTHER_ID
AND c.VAL = xml.VAL (+)
)
GROUP BY A_ID;