优化慢速SQL查询:派生表和基于行类型的两个不同联接

时间:2016-09-01 11:21:43

标签: sql query-optimization firebird firebird2.1

我正在使用Firebird 2.1。我想优化以下查询,因为它甚至在一个小数据集(cca 500条记录)上执行得非常慢(差不多2秒):

TABLE客户端存储人员和公司/公司组。 (ClientType = 0 person,ClientType = 1公司)主键:ClientID

TABLE ClientContacts存储哪些人链接到哪些公司:ClientID是公司的ID,ContactClientID是链接到公司的人员ID。主键:ClientID,ContactClientID

TABLE CompanyGroups存储哪个公司链接到哪个公司组:ParentClientID是公司组的ID,ClientID是公司组附加的公司ID。主键:ParentClientID,ClientID

因此,某人可能属于多家公司,而公司可能属于多个公司集团。

我想列出所有人和公司。对于我想要显示它所属公司的人,以及我希望显示该公司所属公司集团的公司。

最重要的是,我需要搜索该人/公司所属的公司/公司集团的名称。在Firebird中,由于聚合函数“LIST”,我可以用派生表实现这一点。

以下是查询:

SELECT C.ClientID,
  C.ClientType,
  C.ClientName,
  IIF(C.ClientType = 0, PCN.PCompanyNames, CCN.CCompanyNames),
FROM Clients C
  LEFT JOIN (SELECT CC.ContactClientID, LIST(CL.ClientName, ', ') AS PCompanyNames
   FROM ClientContacts CC LEFT JOIN Clients CL ON CL.ClientID = CC.ClientID WHERE 
    CL.AccessRights = 0 OR CL.UserID = :UserID OR (CL.AccessRights = 2 AND 
     CL.ClientID IN (SELECT ClientID FROM ClientRights WHERE UserID = :UserID))
      GROUP BY CC.ContactClientID) PCN ON PCN.ContactClientID = C.ClientID AND C.ClientType = 0
  LEFT JOIN (SELECT CG.ClientID, LIST(CL.ClientName, ', ') AS CCompanyNames
   FROM CompanyGroups CG LEFT JOIN Clients CL ON CL.ClientID = CG.ParentClientID WHERE 
    CL.AccessRights = 0 OR CL.UserID = :UserID OR (CL.AccessRights = 2 AND
     CL.ClientID IN (SELECT ClientID FROM ClientRights WHERE UserID = :UserID))
      GROUP BY CG.ClientID) CCN ON CCN.ClientID = C.ClientID AND C.ClientType = 1
WHERE (C.AccessRights = 0
    OR C.UserID = :UserID
      OR (C.AccessRights = 2 AND C.ClientID IN (SELECT ClientID FROM ClientRights WHERE UserID = :UserID)))
  AND (:SearchStr IS NULL
    OR (PCN.PCompanyNames COLLATE UNICODE_CI LIKE '%' || :SearchStr || '%'
    OR CCN.CCompanyNames COLLATE UNICODE_CI LIKE '%' || :SearchStr || '%'))

更新查询计划,首先是上面的查询,第二个上面的查询没有WHERE子句到处(没有IN SELECT)

Field #01: CLIENTS.CLIENTID Alias:CLIENTID Type:INTEGER
Field #02: CLIENTS.CLIENTTYPE Alias:CLIENTTYPE Type:INTEGER
Field #03: CLIENTS.CLIENTNAME Alias:CLIENTNAME Type:STRING(1000)
Field #04: .CASE Alias:CASE Type:BLOB SUB_TYPE 1
PLAN (PCN CLIENTRIGHTS INDEX (RDB$PRIMARY46))
PLAN (CCN CLIENTRIGHTS INDEX (RDB$PRIMARY46))
PLAN (CLIENTRIGHTS INDEX (RDB$PRIMARY46))
PLAN JOIN (JOIN (C INDEX (IDX_CLIENTS_ACCESSRIGHTS, IDX_CLIENTS_USERID, IDX_CLIENTS_ACCESSRIGHTS), SORT (JOIN (PCN CC NATURAL, PCN CL INDEX (RDB$PRIMARY12)))), SORT (JOIN (CCN CG NATURAL, CCN CL INDEX (RDB$PRIMARY12))))

119643 fetches, 0 marks, 0 reads, 0 writes.
0 inserts, 0 updates, 0 deletes, 19977 index, 19629 seq.
Delta memory: 321686664 bytes.
Total execution time: 1.531s


Field #01: CLIENTS.CLIENTID Alias:CLIENTID Type:INTEGER
Field #02: CLIENTS.CLIENTTYPE Alias:CLIENTTYPE Type:INTEGER
Field #03: CLIENTS.CLIENTNAME Alias:CLIENTNAME Type:STRING(1000)
Field #04: .CASE Alias:CASE Type:BLOB SUB_TYPE 1
PLAN JOIN (JOIN (C NATURAL, SORT (JOIN (PCN CC NATURAL, PCN CL INDEX (RDB$PRIMARY12)))), SORT (JOIN (CCN CG NATURAL, CCN CL INDEX (RDB$PRIMARY12))))

119289 fetches, 0 marks, 0 reads, 0 writes.
0 inserts, 0 updates, 0 deletes, 19646 index, 19832 seq.
Delta memory: 321690896 bytes.
Total execution time: 1.406s

1 个答案:

答案 0 :(得分:3)

抱歉,我无法发表评论(没有必要的声誉)。没有结构很难做到,但我试试。

所以:

1)您需要将查询分为两部分:ClientType = 0和ClientType = 1;

2)你不需要在PCN和CCN内部进行左连接,因为它没有意义;

3)您经常使用查询:

var f2 = f1.bind(obj);

我认为你应该这样做:

var f2 = (function(f, v){
  return function(){
      return f.apply(v, arguments);
  }
})(f1, obj);