如何优化SQL自联接查询?

时间:2015-11-10 17:13:48

标签: sql sql-server

我有两张桌子(这些是第三方应用程序的一部分,因此我无法更改其架构):

  • doc包含doc_id,is_active,part_number,name和description
  • 节点具有node_id,doc_id和parent_node_id。

node.doc_id是指doc.doc_id值(没有外键关系),node.parent_node_id是指node.node_id值,设置doc表中值的父/子关系。 doc表中的每个条目都可以有零个或一个父级,以及任意数量的子级。

对于给定的部件号,我需要doc表中所有匹配条目的名称和描述AND(这里是棘手的部分)每个这样的匹配条目我需要知道该条目是否有任何活动的子项

以下是一个例子:

doc
doc_id   is_active   part_number   name   description
1        T           AAA           Fred   Little
2        T           AAA           George Middle
3        T           AAA           Sam    Morse
4        T           CCC           Mary   Moo
5        T           DDD           Carol  Smith
6        F           DDD           Midge  Moo

node
node_id    doc_id    parent_node_id
10          1           null
11          2           null
12          3           null
13          4           10
14          5           10
15          6           11

因此,您可以看到doc_id 1有2个子节点(doc_ids 4和5),doc_id 2有一个子节点(doc_id 6)。

图形:

doc [doc_id = 1] - >节点[DOC_ID = 1,NODE_ID = 10]; parent_node_id = 10的节点是节点[node_id = 13,doc_id = 4]和节点[node_id = 14,doc_id = 5]; doc [doc_id = 4]和doc [doc_id = 5]都有is_active = T.

doc [doc_id = 2] - >节点[DOC_ID = 2,NODE_ID = 11]; parent_node_id = 11的唯一节点是node [node_id = 15,doc_id = 6],但doc [doc_id = 6]的is_active = F.

如果我要求part_number = AAA,我需要回来:

doc_id   name    description    has_active_children
1        Fred    Little         T
2        George  Middle         F
3        Sam     Morse          F

现在我已经得到了这个查询,我计算了孩子的数量(这是不必要的,但我唯一可以弄明白的):

select d1.*,
   (select count(dn.node_id) from node dn
    inner join doc dc on dn.doc_id=dc.doc_id
    where dn.parent_node_id=
      (select dx.node_id from node dx where dx.doc_id=d1.doc_id)
    and dc.is_active='T') as childCount
from doc d1 where d1.part_number='AAA'

这种方法有效,但速度非常快。我们在SQL Server上运行,我尝试了"设置showplan_all"但是没有充分理解输出,无法做出任何改变。

有没有明显更好的方法来执行此查询?或者,是否有一个文档可以帮助我理解showplan输出?

2 个答案:

答案 0 :(得分:1)

这应该是一个不错的起点。我必须在数据库中进行类似的自连接,该数据库具有地理区域的层次表示。

如果您将2个left join语句中的任何一个更改为简单的join,那么任何没有孩子的父母都将从查询结果中删除。

SELECT parent.[doc_id],
       parent.[name],
       parent.[description],
       parent.[part_number],
       CASE WHEN COUNT(child.[doc_id]) > 0 THEN 'T' ELSE 'F' END
FROM       doc   parent
JOIN       node  parentRef on parent.[doc_id] = parentRef.[doc_id]
LEFT JOIN  node  childRef on parentRef.[node_id] = childRef.[parent_node_id]
LEFT JOIN  doc   child on child.[doc_id] = childRef.[doc_id]
WHERE parent.[part_number] = 'AAA'
GROUP BY parent.[doc_id], parent.[name], parent.[part_number], parent.[description]

编辑: 将part_number添加到查询

此外,与任何SQL查询一样,请查看这些表中存在的索引。您可以添加一个或两个索引来提高查询性能。

答案 1 :(得分:0)

您可以使用适合您案例的窗口功能。

SELECT parent.[doc_id],
       parent.[name],
       parent.[description],
       parent.[part_number],
       CASE WHEN ROW_NUMBER(PARTITION BY child.[doc_id]) > 0 THEN 'T' ELSE 'F' END  AS childCount
FROM       doc   parent
JOIN       node  parentRef on parent.[doc_id] = parentRef.[doc_id]
LEFT JOIN  node  childRef on parentRef.[node_id] = childRef.[parent_node_id]
LEFT JOIN  doc   child on child.[doc_id] = childRef.[doc_id]
WHERE parent.[part_number] = 'AAA' AND child.is_active = 'T'