2010-01-06 4 views
1

SQLite로 마이그레이션 한 800MB MS Access 데이터베이스가 있습니다. 데이터베이스의 구조는 다음과 같습니다 (마이그레이션 후 SQLite 데이터베이스는 약 330MB입니다).SQLite 쿼리가 MSAccess 쿼리보다 10 배 느리게 실행됩니다.

테이블 Occurrence에는 1,600,000 개의 레코드가 있습니다. 그것은 다음과 같은 인덱스가

CREATE TABLE Occurrence 
(
SimulationID INTEGER, SimRunID INTEGER, OccurrenceID INTEGER, 
OccurrenceTypeID INTEGER, Period INTEGER, HasSucceeded BOOL, 
PRIMARY KEY (SimulationID, SimRunID, OccurrenceID) 
) 

: 표는 모양

CREATE INDEX "Occurrence_HasSucceeded_idx" ON "Occurrence" ("HasSucceeded" ASC) 

CREATE INDEX "Occurrence_OccurrenceID_idx" ON "Occurrence" ("OccurrenceID" ASC) 

CREATE INDEX "Occurrence_SimRunID_idx" ON "Occurrence" ("SimRunID" ASC) 

CREATE INDEX "Occurrence_SimulationID_idx" ON "Occurrence" ("SimulationID" ASC) 

OccurrenceParticipant는 3,400,000 기록을 가지고있다. 그것은 다음과 같은 인덱스가

CREATE TABLE OccurrenceParticipant 
(
SimulationID INTEGER,  SimRunID INTEGER, OccurrenceID  INTEGER, 
RoleTypeID  INTEGER,  ParticipantID INTEGER 
) 

: 표는 모양

CREATE INDEX "OccurrenceParticipant_OccurrenceID_idx" ON "OccurrenceParticipant" ("OccurrenceID" ASC) 

CREATE INDEX "OccurrenceParticipant_ParticipantID_idx" ON "OccurrenceParticipant" ("ParticipantID" ASC) 

CREATE INDEX "OccurrenceParticipant_RoleType_idx" ON "OccurrenceParticipant" ("RoleTypeID" ASC) 

CREATE INDEX "OccurrenceParticipant_SimRunID_idx" ON "OccurrenceParticipant" ("SimRunID" ASC) 

CREATE INDEX "OccurrenceParticipant_SimulationID_idx" ON "OccurrenceParticipant" ("SimulationID" ASC) 

InitialParticipant (130 개) 기록을 가지고있다. 테이블의 구조는 다음 테이블 인덱스가

CREATE TABLE InitialParticipant 
(
ParticipantID INTEGER PRIMARY KEY,  ParticipantTypeID INTEGER, 
ParticipantGroupID  INTEGER 
) 

이다

CREATE INDEX "initialpart_participantTypeID_idx" ON "InitialParticipant" ("ParticipantGroupID" ASC) 

CREATE INDEX "initialpart_ParticipantID_idx" ON "InitialParticipant" ("ParticipantID" ASC) 

표 22 개 ParticipantGroup 레코드를 갖는다. 표는 다음과 같은 인덱스가

CREATE TABLE ParticipantGroup (
ParticipantGroupID INTEGER, ParticipantGroupTypeID  INTEGER, 
Description varchar (50),  PRIMARY KEY( ParticipantGroupID ) 
) 

처럼 보이는 : 는 "ParticipantGroup"("ParticipantGroupID"ASC) ON INDEX "ParticipantGroup_ParticipantGroupID_idx을"CREATE

tmpSimArgs 18 개 기록을 가지고있다.

CREATE TABLE tmpSimArgs (SimulationID varchar, SimRunID int(10)) 

그리고 다음 인덱스 : 그것은 다음과 같은 구조를 가지고

CREATE INDEX tmpSimArgs_SimRunID_idx ON tmpSimArgs(SimRunID ASC) 

CREATE INDEX tmpSimArgs_SimulationID_idx ON tmpSimArgs(SimulationID ASC) 

표 'tmpPartArgs이'(80 개) 기록을 가지고있다.

CREATE TABLE tmpPartArgs(participantID INT) 

그리고 아래의 인덱스 : 그것은 아래의 구조가

CREATE INDEX tmpPartArgs_participantID_idx ON tmpPartArgs(participantID ASC) 

을 나는 여러 INNER을 포함하는 쿼리가 조인 내가 직면하고있는 문제는 쿼리의 Access 버전이 정도 걸립니다이다가 두 번째 SQLite 버전의 동일한 쿼리가 10 초 (약 10 배 느린!) 반면 Access로 다시 마이 그 레이션하는 것은 불가능하고 SQLite는 유일한 옵션입니다.

나는 데이터베이스 쿼리를 작성하는 데 익숙하지 않으므로 이러한 쿼리가 어리석은 것으로 보일 수 있으므로 잘못되었거나 꼬마 접시가있는 것에 대해서는 조언을 구하십시오.

다음과 SQLite는 쿼리가
SELECT ParticipantGroup.Description, Occurrence.SimulationID, Occurrence.SimRunID, Occurrence.Period, Count(OccurrenceParticipant.ParticipantID) AS CountOfParticipantID FROM 
( 
    ParticipantGroup INNER JOIN InitialParticipant ON ParticipantGroup.ParticipantGroupID = InitialParticipant.ParticipantGroupID 
) INNER JOIN 
(
tmpPartArgs INNER JOIN 
    (
    (
     tmpSimArgs INNER JOIN Occurrence ON (tmpSimArgs.SimRunID = Occurrence.SimRunID) AND (tmpSimArgs.SimulationID = Occurrence.SimulationID) 
    ) INNER JOIN OccurrenceParticipant ON (Occurrence.OccurrenceID = OccurrenceParticipant.OccurrenceID) AND (Occurrence.SimRunID = OccurrenceParticipant.SimRunID) AND (Occurrence.SimulationID = OccurrenceParticipant.SimulationID) 
) ON tmpPartArgs.participantID = OccurrenceParticipant.ParticipantID 
) ON InitialParticipant.ParticipantID = OccurrenceParticipant.ParticipantID WHERE (((OccurrenceParticipant.RoleTypeID)=52 Or (OccurrenceParticipant.RoleTypeID)=49)) AND Occurrence.HasSucceeded = True GROUP BY ParticipantGroup.Description, Occurrence.SimulationID, Occurrence.SimRunID, Occurrence.Period; 

(이 질의는 약 10 초 정도) :

SELECT ij1.Description, ij2.occSimulationID, ij2.occSimRunID, ij2.Period, Count(ij2.occpParticipantID) AS CountOfParticipantID FROM 
(
    SELECT ip.ParticipantGroupID AS ipParticipantGroupID, ip.ParticipantID AS ipParticipantID, ip.ParticipantTypeID, pg.ParticipantGroupID AS pgParticipantGroupID, pg.ParticipantGroupTypeID, pg.Description FROM ParticipantGroup as pg INNER JOIN InitialParticipant AS ip ON pg.ParticipantGroupID = ip.ParticipantGroupID 
) AS ij1 INNER JOIN 
(
    SELECT tpa.participantID AS tpaParticipantID, ij3.* FROM tmpPartArgs AS tpa INNER JOIN 
    (
     SELECT ij4.*, occp.SimulationID as occpSimulationID, occp.SimRunID AS occpSimRunID, occp.OccurrenceID AS occpOccurrenceID, occp.ParticipantID AS occpParticipantID, occp.RoleTypeID FROM 
      (
       SELECT tsa.SimulationID AS tsaSimulationID, tsa.SimRunID AS tsaSimRunID, occ.SimulationID AS occSimulationID, occ.SimRunID AS occSimRunID, occ.OccurrenceID AS occOccurrenceID, occ.OccurrenceTypeID, occ.Period, occ.HasSucceeded FROM tmpSimArgs AS tsa INNER JOIN Occurrence AS occ ON (tsa.SimRunID = occ.SimRunID) AND (tsa.SimulationID = occ.SimulationID) 
     ) AS ij4 INNER JOIN OccurrenceParticipant AS occp ON (occOccurrenceID =  occpOccurrenceID) AND (occSimRunID = occpSimRunID) AND (occSimulationID = occpSimulationID) 
    ) AS ij3 ON tpa.participantID = ij3.occpParticipantID 
) AS ij2 ON ij1.ipParticipantID = ij2.occpParticipantID WHERE (((ij2.RoleTypeID)=52 Or (ij2.RoleTypeID)=49)) AND ij2.HasSucceeded = 1 GROUP BY ij1.Description, ij2.occSimulationID, ij2.occSimRunID, ij2.Period; 

I

Access의 쿼리가

(전체 쿼리를 실행하기 위해 1 초 소요) 내가 여기서 뭘 잘못하고 있는지 모르겠다.나는 모든 색인을 가지고 있지만, 나는 나를 위해 속임수를 쓸 핵심 지표를 놓치고 있다고 생각한다. 흥미로운 것은 마이그레이션 이전에 SQLite에 대한 '연구'에서 SQLite가 Access보다 모든 측면에서 더 빠르고 작고 더 나은 것으로 나타났습니다. 하지만 질의 측면에서 액세스보다 SQLite 작업 속도가 빠르다. 나는 SQLite를 처음 접했고 분명히 많은 경험과 경험이 없으므로 배우는 영혼이 나를 도와 줄 수 있다면 많은 도움이 될 것입니다.

+1

그 쿼리는 내 머리를 아프게합니다. 나는 왜 당신이 모든 derrived 질의 (subselects)를하고 있는지 보지 못합니다. 당신은 영어로 (SQL 대신에) 당신이 그 쿼리로부터 무엇을 돌려 주려고하는지 설명 할 수 있습니까? 질문에 쉽게 답할 수 있습니다. – JohnFx

+0

각 하위 선택 문이 영어로하는 것을 설명 할 것입니다.이 메모 상자는 600 자까지만 가능하므로 내 질문에 대답으로 설명을 게시하고 있습니다 .. –

답변

0

내 쿼리의 축소 버전이 더 작습니다. 이것이 이전보다 더 명확하고 읽기 쉽기를 바랍니다.

SELECT5 * FROM 
(
SELECT4 FROM ParticipantGroup as pg INNER JOIN InitialParticipant AS ip ON pg.ParticipantGroupID = ip.ParticipantGroupID 
) AS ij1 INNER JOIN 
(
    SELECT3 * FROM tmpPartArgs AS tpa INNER JOIN 
     (
      SELECT2 * FROM 
       (
        SELECT1 * FROM tmpSimArgs AS tsa INNER JOIN Occurrence AS occ ON (tsa.SimRunID = occ.SimRunID) AND (tsa.SimulationID = occ.SimulationID) 
      ) AS ij4 INNER JOIN OccurrenceParticipant AS occp ON (occOccurrenceID =  occpOccurrenceID) AND (occSimRunID = occpSimRunID) AND (occSimulationID = occpSimulationID) 
    ) AS ij3 ON tpa.participantID = ij3.occpParticipantID 
) AS ij2 ON ij1.ipParticipantID = ij2.occpParticipantID WHERE (((ij2.RoleTypeID)=52 Or (ij2.RoleTypeID)=49)) AND ij2.HasSucceeded = 1 

내가 일하고 응용 프로그램은 시뮬레이션 응용 프로그램이며 위의 쿼리의 문맥을 이해하기 위해 나는 그것이 필요한 응용 프로그램에 대한 간략한 설명을 제공하기 위해 생각했다. 일부 초기 자원과 살아있는 대리인이있는 행성이 있다고 가정합시다. 행성은 1000 년 동안 존재하도록 허용되며 에이전트에 의해 수행되는 동작은 모니터링되고 데이터베이스에 저장됩니다. 1000 년이 지난 후에 행성은 처음으로 동일한 초기 자원과 살아있는 대리인으로 파괴되고 다시 만들어집니다. 이것 (창조와 파괴)은 18 번 반복되며 그 1000 년 동안 수행 된 요원의 모든 행동은 데이터베이스에 저장됩니다. 따라서 우리의 전체 실험은 '시뮬레이션'으로 불리는 18 개의 재창조로 구성됩니다. 행성이 재창조 된 18 번 각각은 달리기 (run)라고 불리며, 1000 년 동안의 달리기를 각각 기간이라고 부릅니다. 따라서 '시뮬레이션'은 18 회의 실행으로 구성되며 각 실행은 1000 개의 기간으로 구성됩니다. 각각의 실행이 시작될 때, 우리는 '시뮬레이션'에 지식 항목과 서로 상호 작용하는 동적 에이전트 및 항목의 초기 세트를 할당합니다. 지식 항목은 지식 저장소 내부의 에이전트에 의해 저장됩니다. 또한 지식 저장소는 시뮬레이션의 참여 엔터티로 간주됩니다. 그러나 (지식 상점에 관한)이 개념은 중요하지 않습니다. 모든 SELECT 문과 관련된 테이블에 대해 자세히 설명하려고했습니다.

SELECT1 :이 쿼리는 'Occurrence'테이블로 대체 될 수 있다고 생각합니다. Occurrence 테이블은 특정 '시뮬레이션'의 모든 시뮬레이션 실행 기간마다 상담원이 수행 한 여러 작업을 저장합니다. 일반적으로 각각의 '시뮬레이션'은 18 회의 실행으로 구성됩니다. 그리고 각 실행은 1000 기간으로 구성되어 있습니다. 상담원은 '시뮬레이션'에서 매주마다 매 회마다 조치를 취할 수 있습니다. 그러나 어? 런스 테이블은 조치를 수행하는 에이전트에 대한 세부 사항을 저장하지 않습니다. Occurrence 테이블은 여러 'Simulations'와 관련된 데이터를 저장할 수 있습니다.

SELECT2 :이 쿼리는 '시뮬레이션'의 모든 참가자의 세부 정보와 함께 각 참가자 ID와 마찬가지로 '시뮬레이션'을 실행할 때마다 수행 된 작업의 세부 정보를 반환합니다. OccurrenceParticipant 테이블은 시뮬레이션의 모든 참여 엔터티에 대한 레코드를 저장하며 에이전트, 지식 저장소, 지식 항목 등을 포함합니다.

SELECT3 :이 쿼리는 에이전트 및 지식 항목으로 인한 의사 테이블 ij3의 레코드 만 반환합니다 . 지식 항목에 관한 ij3의 모든 레코드는 필터링되어 제거됩니다.

SELECT4 :이 쿼리는 '설명'필드를 'InitialParticipant'의 모든 레코드에 연결합니다. '설명'열은 전체 쿼리의 출력 열입니다. 테이블 InitialParticipant는 '시뮬레이션'에 처음 할당 된 모든 상담원 및 모든 지식 항목에 대한 레코드를 포함합니다.

SELECT5 :이 최종 쿼리는 참여하는 엔티티의 RoleType이 될 수있는 의사 테이블 ij2의 모든 레코드를 반환합니다 에이전트 또는 지식 항목이 됨)는 49 또는 52입니다.

+3

왜이 대답 대신 질문을 편집하지 않는 것이 좋을까요? '? –

2

다른 사람들이 쉽게 읽을 수 있도록 코드 (내 집 작성 SQL formatter 사용)를 다시 포맷했습니다.

포맷 질의 :

JohnFx (위)으로 당
SELECT 
    ij1.Description, 
    ij2.occSimulationID, 
    ij2.occSimRunID, 
    ij2.Period, 
    Count(ij2.occpParticipantID) AS CountOfParticipantID 

FROM (

    SELECT 
     ip.ParticipantGroupID AS ipParticipantGroupID, 
     ip.ParticipantID AS ipParticipantID, 
     ip.ParticipantTypeID, 
     pg.ParticipantGroupID AS pgParticipantGroupID, 
     pg.ParticipantGroupTypeID, 
     pg.Description 

    FROM ParticipantGroup AS pg 

    INNER JOIN InitialParticipant AS ip 
      ON pg.ParticipantGroupID = ip.ParticipantGroupID 

) AS ij1 

INNER JOIN (

    SELECT 
     tpa.participantID AS tpaParticipantID, 
     ij3.* 

    FROM tmpPartArgs AS tpa 

    INNER JOIN (

     SELECT 
      ij4.*, 
      occp.SimulationID AS occpSimulationID, 
      occp.SimRunID AS occpSimRunID, 
      occp.OccurrenceID AS occpOccurrenceID, 
      occp.ParticipantID AS occpParticipantID, 
      occp.RoleTypeID 

     FROM (

      SELECT 
       tsa.SimulationID AS tsaSimulationID, 
       tsa.SimRunID AS tsaSimRunID, 
       occ.SimulationID AS occSimulationID, 
       occ.SimRunID AS occSimRunID, 
       occ.OccurrenceID AS occOccurrenceID, 
       occ.OccurrenceTypeID, 
       occ.Period, 
       occ.HasSucceeded 

      FROM tmpSimArgs AS tsa 

      INNER JOIN Occurrence AS occ 
        ON (tsa.SimRunID = occ.SimRunID) 
        AND (tsa.SimulationID = occ.SimulationID) 

     ) AS ij4 

     INNER JOIN OccurrenceParticipant AS occp 
       ON (occOccurrenceID = occpOccurrenceID) 
       AND (occSimRunID = occpSimRunID) 
       AND (occSimulationID = occpSimulationID) 

    ) AS ij3 
     ON tpa.participantID = ij3.occpParticipantID 

) AS ij2 
    ON ij1.ipParticipantID = ij2.occpParticipantID 

WHERE (

    (

     (ij2.RoleTypeID) = 52 
     OR 
     (ij2.RoleTypeID) = 49 

    ) 

) 
    AND ij2.HasSucceeded = 1 

GROUP BY 
    ij1.Description, 
    ij2.occSimulationID, 
    ij2.occSimRunID, 
    ij2.Period; 

, I 파생 뷰 혼동 하였다. 나는 실제로 그것들에 대한 필요성이 없다고 생각합니다. 특히 그들은 모두 내부 조인이기 때문입니다. 그래서 아래에서 나는 복잡성을 줄이려고 시도했다. 실적을 검토하고 테스트하십시오. 나는 그것이 단지 Occurence에 합류했기 때문에 tmpSimArgs와 cross join을해야만했다. 나는 이것이 바람직한 행동이라고 가정한다. 내가 ij3하는 가장 바깥 쪽 쿼리에서 필터링 ij2.RoleTypeID 이동 제안

SELECT 
    pg.Description, 
    occ.SimulationID, 
    occ.SimRunID, 
    occ.Period, 
    COUNT(occp.ParticipantID) AS CountOfParticipantID 

FROM ParticipantGroup AS pg 

INNER JOIN InitialParticipant AS ip 
     ON pg.ParticipantGroupID = ip.ParticipantGroupID 

CROSS JOIN tmpSimArgs AS tsa 

INNER JOIN Occurrence AS occ 
     ON tsa.SimRunID = occ.SimRunID 
     AND tsa.SimulationID = occ.SimulationID 

INNER JOIN OccurrenceParticipant AS occp 
     ON occ.OccurrenceID = occp.OccurrenceID 
     AND occ.SimRunID = occp.SimRunID 
     AND occ.SimulationID = occp.SimulationID 

INNER JOIN tmpPartArgs AS tpa 
     ON tpa.participantID = occp.ParticipantID 

WHERE occ.HasSucceeded = 1 
    AND (occp.RoleTypeID = 52 OR occp.RoleTypeID = 49) 

GROUP BY 
    pg.Description, 
    occ.SimulationID, 
    occ.SimRunID, 
    occ.Period; 
0

대신 OR의 IN 사용하고 ij4하는 HasSucceeded 쿼리를 이동합니다.

관련 문제