【问题标题】:Oracle + SQL Server tree query issueOracle + SQL Server 树查询问题
【发布时间】:2023-03-28 10:16:01
【问题描述】:

我在让 SQL Server 为我们的 SQL 树查询返回与 Oracle 相同的结果时遇到了一点问题。

我们有一个带有属性的链接表

CHILD_ID, LINK_ID, PARENT_ID

还有一个带有ELEMENT_ID的元素表

在 Oracle 中我们使用这个

SELECT * FROM LINKS
JOIN ELEMENTS ON ELEMENT_ID = CHILD_ID
WHERE LINK_ID IN 
(SELECT LINK_ID FROM LINKS CONNECT BY PRIOR CHILD_ID = PARENT_ID START WITH PARENT_ID = 'startid')

在 SQL Server 中我们使用这个

WITH TREE_LINKS AS
(SELECT CHILD_ID, LINK_ID FROM LINKS WHERE PARENT_ID = 'startid'
UNION ALL
SELECT CURRENT_LINKS.CHILD_ID, CURRENT_LINKS.LINK_ID
FROM LINKS CURRENT_LINKS
INNER JOIN TREE_LINKS t1 ON CURRENT_LINKS.PARENT_ID = t1.CHILD_ID)

SELECT * FROM TREE_LINKS
INNER JOIN LINKS ON TREE_LINKS.LINK_ID = LINKS.LINK_ID 
INNER JOIN ELEMENTS ON ELEMENTS.ELEMENT_ID = TREE_LINKS.CHILD_ID

除了 1 个问题之外,这一切都很好。

在 Oracle 中,我们仅获取基于 LINK_ID 的每个唯一链接。在 SQL Server 中,我们获得了描述完整树的所有链接,当结构的不同分支中的多个其他元素下方存在一个元素时,该完整树可以包含重复项。

这意味着我们会从 SQL Server 获得大量重复行。我测试了添加

SELECT DISTINCT TREE_LINKS.LINK_ID AS TREE_LINK_ID, * from TREE_LINKS 

在最后一个 select 语句中,但只要服务器有更多工作可以删除在不同分支中发现的重复项。

目前在 1 个测试用例中,Oracle 返回 20,000 行,SQL Server 返回 160 万行。到目前为止,我还没有找到让 SQL Server 快速返回相同结果的方法。

仅供参考:在递归中添加 DISTINCT 会导致

DISTINCT 运算符不允许出现在递归的递归部分 公用表表达式“TREE_LINKS”。

编辑:- 一个例子

如果我们有这样的链接

PARENT_ID, LINK_ID, CHILD_ID
1          1        2
2          2        3 
3          3        4
1          4        3

有 4 个唯一元素,但完整树中有 6 个元素。这是因为元素 3 有 2 条路径

【问题讨论】:

  • 您可能会从内部连接中获取 dup 子节点,请先尝试我在添加 distinct 之前给出的代码以先查看差异。另外,查找“内部联接”。如果两个孩子有相同的父母,那么您将获得同一父母与每个孩子的多条记录。
  • 我不小心将 PARENT_ID 放在了其中一个语句中应该有 LINK_ID 的位置。以防万一你正在测试它。
  • 你有没有 child_id == parent_id 的链接节点?因为我可以看到这为 sql-server 语法造成了问题,因为没有用于递归的停止语句。也许在“WHERE TREE_LINKS.CHILD_ID = NLINKS.PARENT_ID”之后添加这个“AND TREE_LINKS.LINK_ID NLINKS.LINK_ID”只是为了确保,因为在我看来,那部分不应该有重复。如果有,那也会在您拥有的连接中创建它们......我将把它添加到我的代码中以防万一。
  • CHILD_ID 永远不能匹配 PARENT_ID
  • 嗯,当 startid 为 1 时,您有 from 1 >-2-3-4 >-3-4。好的,从此 sqlserver 的语法立即为您提供了两个父项(parent_id = 1 提供了 2 个元素),所以这可能就是为什么它给了您一些不同的东西……我稍后再看一遍。

标签: sql sql-server database oracle


【解决方案1】:
SELECT LINK_ID
  FROM LINKS
  START WITH PARENT_ID = 'startid'
  CONNECT BY PRIOR CHILD_ID = PARENT_ID

应该等价于

在下面编辑(并且在第一次编辑中):在第二次选择中将 PARENT_ID 更改为 LINK_ID

WITH TREE_LINKS(CHILD_ID, LINK_ID) AS 
   (SELECT CHILD_ID, LINK_ID
    FROM LINKS
    WHERE PARENT_ID = 'startid'
        UNION ALL
    SELECT NLINKS.CHILD_ID, NLINKS.LINK_ID
    FROM LINKS as NLINKS, TREE_LINKS
    WHERE TREE_LINKS.CHILD_ID = NLINKS.PARENT_ID)

SELECT LINK_ID FROM TREE_LINKS

根据Simulation of CONNECT BY PRIOR of ORACLE in SQL SERVER

据我所知,其余的应该是相似的。

编辑:可能是这样的:

WITH TREE_LINKS(CHILD_ID, LINK_ID) AS 
   (SELECT CHILD_ID, LINK_ID
    FROM LINKS
    WHERE PARENT_ID = 'startid'
        UNION ALL
    SELECT NLINKS.CHILD_ID, NLINKS.LINK_ID
    FROM LINKS as NLINKS, TREE_LINKS
    WHERE TREE_LINKS.CHILD_ID = NLINKS.PARENT_ID)

SELECT * FROM LINKS as TLINKS
JOIN ELEMENTS ON ELEMENT_ID = TLINKS.CHILD_ID
WHERE TLINKS.LINK_ID IN
(SELECT TREE_LINKS.LINK_ID FROM TREE_LINKS)

但我目前没有 mssql 或 oracle 来测试它

编辑:从查询中删除“AND TREE_LINKS.LINK_ID NLINKS.LINK_ID”,因为它不需要。

【讨论】:

  • 这在两种情况下都会返回重复项。即,如果结构的不同分支中的 2 个元素具有相同的孩子并且该孩子有很多孩子,那么您会多次获得这些孩子的链接。也许在这个查询上做一个 DISTINCT 会比我现在的更快。
  • 您的 SQL 语句似乎运行良好。它在大约 1 分钟内获得 20,000 个结果。比获得完整的 160 万个然后使用 distinct 过滤快 5 倍。我需要做更多的测试。
  • 好的。最后一个查询很快,实际上“AND TREE_LINKS.LINK_ID NLINKS.LINK_ID”是不需要的,它会稍微慢一些。虽然这个 SQL 和我的几乎一样,但我不明白为什么你的 SQL 这么快并返回 21000 而不是 1.6 百万条目,然后我想通了。在您的 SQL 中,最后一个 IN 子句具有非常有效地过滤重复项的副作用。 TREE_LINKs 仍然返回 160 万行,但 IN 子句在 58 秒内将结果过滤为仅 21,000 行。
  • 我刚刚向查询规划器确认,tree_links 仍然包含 160 万行,现在是 1 分钟的 98%。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多