简体   繁体   English

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

[英]Optimizing slow SQL query: derived tables and two different joins based on row type

I'm using Firebird 2.1. 我正在使用Firebird 2.1。 I'd like to optimize the following query as it executes really slowly (almost 2 secs) on even a small dataset (cca 500 records): 我想优化以下查询,因为它在很小的数据集(cca 500条记录)上的执行速度非常慢(几乎2秒):

TABLE Client stores persons and companies/company groups. TABLE客户端存储人员和公司/公司组。 (ClientType=0 person, ClientType=1 company) Primary key: ClientID (ClientType = 0人,ClientType = 1公司)主键:ClientID

TABLE ClientContacts stores which persons are linked to which companies: ClientID is the company's ID, ContactClientID is the person's ID linked to the company. TABLE ClientContacts存储哪些人链接到哪些公司:ClientID是公司的ID,ContactClientID是链接到公司的人的ID。 Primary keys: ClientID, ContactClientID 主键:ClientID,ContactClientID

TABLE CompanyGroups stores which companys are linked to which company groups: ParentClientID is the company group's ID, ClientID is the company's ID attached to the company group. TABLE CompanyGroups存储将哪些公司链接到哪些公司组:ParentClientID是公司组的ID,ClientID是附属于公司组的公司的ID。 Primary keys: ParentClientID, ClientID 主键:ParentClientID,ClientID

So a person may belong to more than one company, and a company may belong to more than one company groups. 因此,一个人可能属于多个公司,而一个公司可能属于多个公司组。

I would like to list all persons and companys. 我想列出所有个人和公司。 For persons I want to show which companies it belongs to, and for companys I want to show which company groups the company belongs to. 对于个人,我想显示其所属的公司,对于公司,我想显示该公司所属的公司组。

To top it, I need to search by the name of the companies/company groups to which the person/company belongs. 最重要的是,我需要按人员/公司所属的公司/公司组的名称进行搜索。 In Firebird because of aggregate function "LIST" I can achieve this with derived tables. 在Firebird中,由于聚合函数“ LIST”,我可以使用派生表来实现此目的。

Here is the query: 这是查询:

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 || '%'))

Update Query plan, first the above query, second the above query without the WHERE clauses everywhere (no IN SELECT) 更新查询计划,首先是上面的查询,然后是上面的查询,到处都没有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

Sorry, I can't place comment (there is no needed reputation). 抱歉,我无法发表评论(没有所需的声誉)。 It's difficult to do without structure, but I try. 没有结构很难做到,但我会尝试。

So: 所以:

1) You need devide your query into two parts exactly: ClientType = 0 and ClientType = 1; 1)您需要将查询准确地分为两部分:ClientType = 0和ClientType = 1;

2) You don't need left join inside PCN and CCN, because it makes no sense; 2)您不需要在PCN和CCN内部进行左联接,因为这没有任何意义;

3) You have often used query: 3)您经常使用查询:

select ClientID
from Client
where c.AccessRight = 0 or
      c.UserId = :UserId or
      (c.AccessRight = 2 and
       c.ClientId in (
          select r.ClientId
          from ClientRights r
          where r.UserId = :UserId))

I think you should do something like this: 我认为您应该执行以下操作:

with
cl as (
   select c.ClientId, c.ClientName, c.ClientType
   from Client c
   where c.AccessRight = 0 or
         c.UserId = :UserId or
         (c.AccessRight = 2 and c.ClientId in (select r.ClientId from ClientRights r where r.UserId = :UserId))),
q2 as (
    select cc.ContactClientId, List (cl.ClientName, ', ') as PCompanyNames
    from ClientContacts cc
         join cl on (cc.ClientId = cl.ClientId)
    group by cc.ContactClientId),
q3 as (
    select cg.ClientId, List (cl.ClientName, ', ') as CCompanyNames
    from CompanyGroups cg
         join cl on (cg.ParentClientId = cl.ClientId)
    group by cg.ClientId)
select cl.ClientId, cl.ClientType, cl.ClientName, q2.PCompanyNames
from cl
     left join q2 on (cl.ClientId = q2.ContactClientId)
where cl.ClientType = 0
  and (q2.PCompanyNames like '%' || Coalesce (:SearchStr, '') || '%' or Coalesce(:SearchStr, '') = '')
union all
select cl.ClientId, cl.ClientType, cl.ClientName, q3.CCompanyNames
from cl
     left join q3 on (cl.ClientId = q3.ClientId)
where cl.ClientType = 1
  and (q3.CCompanyNames like '%' || Coalesce (:SearchStr, '') || '%' or Coalesce(:SearchStr, '') = '')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM