2016-09-01 3 views
0

저는 Firebird 2.1을 사용하고 있습니다. 작은 데이터 세트 (cca 500 레코드)에서도 실제로는 매우 느리게 (거의 2 초) 실행되므로 다음 쿼리를 최적화하고 싶습니다.느린 SQL 쿼리 최적화 : 파생 테이블 및 행 유형에 따른 두 개의 다른 조인

TABLE 클라이언트는 개인과 회사/회사 그룹을 저장합니다. (ClientType = 0 person, ClientType = 1 company) 기본 키 : ClientID

테이블 ClientContacts는 어떤 개인이 회사에 링크되어 있는지 저장합니다. ClientID는 회사의 ID이고 ContactClientID는 회사에 연결된 사람의 ID입니다. 기본 키 : ClientID, ContactClientID

테이블 CompanyGroups는 회사 그룹에 연결된 회사를 저장합니다. ParentClientID는 회사 그룹의 ID이고 ClientID는 회사 그룹에 연결된 회사 ID입니다. 기본 키 : ParentClientID, ClientID

사람이 둘 이상의 회사에 속할 수 있으며 회사가 둘 이상의 회사 그룹에 속할 수 있습니다.

모든 개인과 회사를 기재하고 싶습니다. 내가 속한 회사와 회사에 대해 회사에 속한 회사 그룹을 보여 드리고 싶습니다.

맨 위로 그 사람/회사가 속한 회사/회사 그룹의 이름으로 검색해야합니다. 파이어 버드에서는 집계 함수 "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 || '%')) 

업데이트 쿼리 계획을 먼저 위의 쿼리 (SELECT 없음) 모든 곳에서 WHERE 절없이 두 번째 위의 질의

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 
+0

모든 인덱스를 포함하여 관련된 테이블의 DDL을 제시해주십시오. 또한 쿼리의 현재 계획을 게시하십시오. 즉각적으로 빠져 나가는 것들 중 하나는'IN (select ...) '을 사용하는 것인데, 이것을'exists' 쿼리로 대체하는 것이 일반적으로 더 잘 수행됩니다. 그러나 퍼포먼스 킬러는 아마도 내부 텍스트 필드를 검색하기 위해 'LIKE'를 사용하는 것일 것입니다. –

+0

Mark, "IN (SELECT)"및 "LIKE"를 꺼내도 성능이 향상되지 않습니다 (SearchStr = NULL로 테스트했을 때 AccessRights가 클라이언트의 거의 모든 곳에서 0 임). 나는 더 게시 할 것이다. – Steve

+0

좋아요, 그럼에도 기본 키를 포함한 쿼리 계획을 추가했습니다. 사용 된 다른 인덱스가 없습니다. – Steve

답변

3
: 여기

쿼리입니다

죄송합니다. 의견을 게시 할 수 없습니다 (평판이 필요 없음). 구조 없이는하기가 어렵지만 시도합니다.

그래서 :

1) 당신은 두 부분으로 쿼리를으로 나눔 필요 정확히 :하여 clientType = 0하여 clientType = 1;

2) 의미가 없으므로 PCN 및 CCN 내부에 왼쪽으로 조인 할 필요가 없습니다.

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

난 당신이 이런 식으로 뭔가를해야한다고 생각 :

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, '') = '') 
+0

감사합니다. 쿼리가 매우 빠르며 0 초 미만으로 실행됩니다! 유일한 점은 Clientname에 의해 결과 집합을 정렬해야한다는 것입니다. 질의에 order by 절을 넣으면 실행 시간이 4 초로 느려집니다. 내 쿼리를 사용하면 주문한 시간에 따라 실행 시간이 동일 해집니다. order by 절을 사용하여 쿼리를 최적화하는 방법에 대한 아이디어가 있습니까? – Steve

+0

ClientName으로 인덱스를 만들 수 있습니까? 여기에 타이밍을 적어 라. – jurden

+0

마찬가지로 약 4 초 정도의 속도로 인덱스가 빠릅니다. 다른 아이디어? – Steve