액세스 (Jet) SQL : TableB의 각 DateTime 스탬프 옆에있는 TableB의 DateTime 스탬프


21

첫 단어

아래의 섹션 (및 JOIN 포함)을 무시 해도됩니다. 배경결과는 단지 상황에 맞는 역할을한다. 코드가 처음에 어떻게 보이는지 보려면 2015-10-06 이전의 편집 기록을 참조하십시오.


목표

궁극적 으로 표 에서 관측을 직접 옆에있는 표에서 사용 가능한 GPS 데이터의 DateTime 스탬프를 기반으로 송신기 ( X또는 Xmit)에 대한 보간 GPS 좌표를 계산하려고 SecondTable합니다.FirstTable .

가장 참여에 궁극적 인 목표를 달성하기 나의 즉각적인 목표는 방법을 알아낼 것이다 FirstTable하는 SecondTable그 측면 시간 포인트를 획득 할 수 있습니다. 나중에 그 정보를 사용할 수 있습니다. 나는 직각 좌표계를 따라 선형 피팅을 가정하고 중간 지구 GPS 좌표를 계산할 수 있습니다.


질문

  1. 가장 가까운 전후 타임 스탬프를 생성하는보다 효율적인 방법이 있습니까?
    • "after"를 잡고 "after"와 관련된 "before"를 얻는 것만으로 스스로 고쳤습니다.
  2. 더 직관적 인 방법이 있습니까? (A<>B OR A=B)구조를
    • Byrdzeye 는 기본적인 대안을 제공했지만, 나의 "실제"경험은 그의 4 가지 조인 전략이 모두 같은 성과를 거두지 못했습니다. 그러나 대체 조인 스타일을 해결 한 것에 대해 전적으로 공로를 인정했습니다.
  3. 다른 생각, 요령 및 조언이있을 수 있습니다.
    • 지금까지 byrdzeyePhrancis 는이 점에서 상당히 도움이되었습니다. 나는 프랜시스의 조언 이 훌륭하게 정리되어 중요한 단계에서 도움을 제공 한다는 것을 알았 으므로 여기서 그에게 우위를 줄 것입니다.

질문 3과 관련하여받을 수있는 추가 도움에 대해서는 여전히 감사하겠습니다. 글 머리 기호는 개별 질문에 가장 도움이 된 사람을 나타냅니다.


테이블 정의

반 시각적 표현

FirstTable

Fields
  RecTStamp | DateTime  --can contain milliseconds via VBA code (see Ref 1) 
  ReceivID  | LONG
  XmitID    | TEXT(25)
Keys and Indices
  PK_DT     | Primary, Unique, No Null, Compound
    XmitID    | ASC
    RecTStamp | ASC
    ReceivID  | ASC
  UK_DRX    | Unique, No Null, Compound
    RecTStamp | ASC
    ReceivID  | ASC
    XmitID    | ASC

SecondTable

Fields
  X_ID      | LONG AUTONUMBER -- seeded after main table has been created and already sorted on the primary key
  XTStamp   | DateTime --will not contain partial seconds
  Latitude  | Double   --these are in decimal degrees, not degrees/minutes/seconds
  Longitude | Double   --this way straight decimal math can be performed
Keys and Indices
  PK_D      | Primary, Unique, No Null, Simple
    XTStamp   | ASC
  UIDX_ID   | Unique, No Null, Simple
    X_ID      | ASC

ReceiverDetails 테이블

Fields
  ReceivID                      | LONG
  Receiver_Location_Description | TEXT -- NULL OK
  Beginning                     | DateTime --no partial seconds
  Ending                        | DateTime --no partial seconds
  Lat                           | DOUBLE
  Lon                           | DOUBLE
Keys and Indicies
  PK_RID  | Primary, Unique, No Null, Simple
    ReceivID | ASC

ValidXmitters 테이블

Field (and primary key)
  XmitID    | TEXT(25) -- primary, unique, no null, simple

SQL 바이올린 ...

... 테이블 정의와 코드를 가지고 놀 수 있도록이 질문은 MSAccess에 대한 것이지만 Phrancis가 지적했듯이 Access에 대한 SQL 바이올린 스타일은 없습니다. 따라서 Phrancis의 답변을 기반으로 내 테이블 정의 및 코드를 보려면 여기방문 해야합니다 : http://sqlfiddle.com/#!6/e9942/4 (외부 링크)


가입 : 시작

나의 현재 "내부 내장"JOIN 전략

먼저 열 순서가있는 FirstTable_rekeyed를 작성하고 복합 기본 키를 (RecTStamp, ReceivID, XmitID)모두 색인화 / 정렬하십시오 ASC. 또한 각 열에 개별적으로 인덱스를 만들었습니다. 그런 다음 채우십시오.

INSERT INTO FirstTable_rekeyed (RecTStamp, ReceivID, XmitID)
  SELECT DISTINCT ROW RecTStamp, ReceivID, XmitID
  FROM FirstTable
  WHERE XmitID IN (SELECT XmitID from ValidXmitters)
  ORDER BY RecTStamp, ReceivID, XmitID;

위의 쿼리는 새 테이블에 153006 개의 레코드를 채우고 10 초 정도 내에 반환합니다.

TOP 1 서브 쿼리 메소드가 사용될 때이 전체 메소드가 "SELECT Count (*) FROM (...)"로 랩핑되면 다음이 1 초 또는 2 초 내에 완료됩니다.

SELECT 
    ReceiverRecord.RecTStamp, 
    ReceiverRecord.ReceivID, 
    ReceiverRecord.XmitID,
    (SELECT TOP 1 XmitGPS.X_ID FROM SecondTable as XmitGPS WHERE ReceiverRecord.RecTStamp < XmitGPS.XTStamp ORDER BY XmitGPS.X_ID) AS AfterXmit_ID
    FROM FirstTable_rekeyed AS ReceiverRecord
    -- INNER JOIN SecondTable AS XmitGPS ON (ReceiverRecord.RecTStamp < XmitGPS.XTStamp)
         GROUP BY RecTStamp, ReceivID, XmitID;
-- No separate join needed for the Top 1 method, but it would be required for the other methods. 
-- Additionally no restriction of the returned set is needed if I create the _rekeyed table.
-- May not need GROUP BY either. Could try ORDER BY.
-- The three AfterXmit_ID alternatives below take longer than 3 minutes to complete (or do not ever complete).
  -- FIRST(XmitGPS.X_ID)
  -- MIN(XmitGPS.X_ID)
  -- MIN(SWITCH(XmitGPS.XTStamp > ReceiverRecord.RecTStamp, XmitGPS.X_ID, Null))

이전 "내부 내장"JOIN 쿼리

첫 번째 (빠르게 ...하지만 충분하지 않음)

SELECT 
  A.RecTStamp,
  A.ReceivID,
  A.XmitID,
  MAX(IIF(B.XTStamp<= A.RecTStamp,B.XTStamp,Null)) as BeforeXTStamp,
  MIN(IIF(B.XTStamp > A.RecTStamp,B.XTStamp,Null)) as AfterXTStamp
FROM FirstTable as A
INNER JOIN SecondTable as B ON 
  (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)
GROUP BY A.RecTStamp, A.ReceivID, A.XmitID
  -- alternative for BeforeXTStamp MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
  -- alternatives for AfterXTStamp (see "Aside" note below)
  -- 1.0/(MAX(1.0/(-(B.XTStamp>A.RecTStamp)*B.XTStamp)))
  -- -1.0/(MIN(1.0/((B.XTStamp>A.RecTStamp)*B.XTStamp)))

두 번째 (느리게)

SELECT
  A.RecTStamp, AbyB1.XTStamp AS BeforeXTStamp, AbyB2.XTStamp AS AfterXTStamp
FROM (FirstTable AS A INNER JOIN 
  (select top 1 B1.XTStamp, A1.RecTStamp 
   from SecondTable as B1, FirstTable as A1
   where B1.XTStamp<=A1.RecTStamp
   order by B1.XTStamp DESC) AS AbyB1 --MAX (time points before)
ON A.RecTStamp = AbyB1.RecTStamp) INNER JOIN 
  (select top 1 B2.XTStamp, A2.RecTStamp 
   from SecondTable as B2, FirstTable as A2
   where B2.XTStamp>A2.RecTStamp
   order by B2.XTStamp ASC) AS AbyB2 --MIN (time points after)
ON A.RecTStamp = AbyB2.RecTStamp; 

배경

나는 1 백만 항목 미만의 원격 측정 테이블 (A라고도 함)을 기반으로 복합 기본 키를 사용합니다. DateTime스탬프, 송신기 ID 및 기록 장치 ID를 . 통제 할 수없는 상황으로 인해 SQL 언어는 Microsoft Access의 표준 Jet DB입니다 (사용자는 2007 이상 버전을 사용함). 트랜스미터 ID로 인해이 항목 중 약 200,000 개만이 쿼리와 관련이 있습니다.

단일 원격 측정 테이블 (별칭 B)에는 약 50,000 개의 항목이 포함되어 있습니다. DateTime 기본 키로

첫 번째 단계에서는 두 번째 테이블에서 첫 번째 테이블의 스탬프에 가장 가까운 타임 스탬프를 찾는 데 중점을 두었습니다.


가입 결과

내가 발견 한 쿼크 ...

... 디버깅 중에 길을 따라

그것은 기록 할 정말 이상한 느낌 JOIN같은 논리 FROM FirstTable as A INNER JOIN SecondTable as B ON (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)로하는 @byrdzeye이 교차 결합의 한 형태이다 (사라진 이후가) 댓글에서 지적을. 대체 것을 주 LEFT OUTER JOIN를 위해 INNER JOIN나타납니다 위의 코드에서하는 반환 라인의 양이나 정체성에 영향을 없습니다. 또한 ON 절을 떠나거나 말할 수없는 것 같습니다 ON (1=1). (A <> B OR A = B) 명시 적 반환 값 으로 쉼표를 사용하여 조인 ( INNER또는 대신 LEFT OUTER JOIN)을 사용하면 Count(select * from A) * Count(select * from B)테이블 A 당 한 줄이 아니라이 쿼리 에서 행이 JOIN반환됩니다. 이것은 분명히 적합하지 않습니다. FIRST복합 기본 키 유형이 주어지면 사용할 수없는 것 같습니다.

두 번째 JOIN스타일은 더 읽기 쉽지만 속도가 느려집니다. JOIN더 큰 테이블과 CROSS JOIN두 옵션 모두에서 발견되는 두 개의 내부 에 대해 두 개의 내부가 추가 로 필요 하기 때문일 수 있습니다 .

옆으로 : IIF절을 MIN/ MAX로 바꾸면 같은 수의 항목이 반환됩니다.
MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
"Before"( MAX) 타임 스탬프에는 작동하지만 MIN다음과 같이 "After"( ) 에는 직접 작동하지 않습니다 . 조건
MIN(-(B.XTStamp>A.RecTStamp)*B.XTStamp)
의 최소값은 항상 0이기 때문 FALSE입니다. 이 0은 포스트 에포크보다 작습니다 DOUBLE(이 DateTime필드는 Access에서 하위 집합이며이 계산으로 필드를 변환합니다). IIFMIN/ MAX0으로 나누기 때문에 AfterXTStamp 값 작업 제안 메소드 번갈아 ( FALSE) 집계 함수 MIN과 MAX는 건너 NULL 값을 생성한다.

다음 단계

더 나아가서, 두 번째 테이블에서 첫 번째 테이블의 타임 스탬프를 직접 옆에두고 해당 지점까지의 시간 거리를 기반으로 두 번째 테이블의 데이터 값을 선형 보간하는 타임 스탬프를 찾고 싶습니다 (예 : 첫 번째 테이블은 "이전"과 "이후"사이의 25 %이며, 계산 된 값의 25 %가 "이후"포인트와 연관된 두 번째 테이블 값 데이터에서 나오고 "이전"에서 75 %가 되길 원합니다. ). 내부 내장의 일부로 수정 된 조인 유형을 사용하고 아래 제안 된 답변을 얻은 후 ...

    SELECT
        AvgGPS.XmitID,
        StrDateIso8601Msec(AvgGPS.RecTStamp) AS RecTStamp_ms,
        -- StrDateIso8601MSec is a VBA function returning a TEXT string in yyyy-mm-dd hh:nn:ss.lll format
        AvgGPS.ReceivID,
        RD.Receiver_Location_Description,
        RD.Lat AS Receiver_Lat,
        RD.Lon AS Receiver_Lon,
        AvgGPS.Before_Lat * (1 - AvgGPS.AfterWeight) + AvgGPS.After_Lat * AvgGPS.AfterWeight AS Xmit_Lat,
        AvgGPS.Before_Lon * (1 - AvgGPS.AfterWeight) + AvgGPS.After_Lon * AvgGPS.AfterWeight AS Xmit_Lon,
        AvgGPS.RecTStamp AS RecTStamp_basic
    FROM ( SELECT 
        AfterTimestampID.RecTStamp,
        AfterTimestampID.XmitID,
        AfterTimestampID.ReceivID,
        GPSBefore.BeforeXTStamp, 
        GPSBefore.Latitude AS Before_Lat, 
        GPSBefore.Longitude AS Before_Lon,
        GPSAfter.AfterXTStamp, 
        GPSAfter.Latitude AS After_Lat, 
        GPSAfter.Longitude AS After_Lon,
        ( (AfterTimestampID.RecTStamp - GPSBefore.XTStamp) / (GPSAfter.XTStamp - GPSBefore.XTStamp) ) AS AfterWeight
        FROM (
            (SELECT 
                ReceiverRecord.RecTStamp, 
                ReceiverRecord.ReceivID, 
                ReceiverRecord.XmitID,
               (SELECT TOP 1 XmitGPS.X_ID FROM SecondTable as XmitGPS WHERE ReceiverRecord.RecTStamp < XmitGPS.XTStamp ORDER BY XmitGPS.X_ID) AS AfterXmit_ID
             FROM FirstTable AS ReceiverRecord 
             -- WHERE ReceiverRecord.XmitID IN (select XmitID from ValidXmitters)
             GROUP BY RecTStamp, ReceivID, XmitID
            ) AS AfterTimestampID INNER JOIN SecondTable AS GPSAfter ON AfterTimestampID.AfterXmit_ID = GPSAfter.X_ID
        ) INNER JOIN SecondTable AS GPSBefore ON AfterTimestampID.AfterXmit_ID = GPSBefore.X_ID + 1
    ) AS AvgGPS INNER JOIN ReceiverDetails AS RD ON (AvgGPS.ReceivID = RD.ReceivID) AND (AvgGPS.RecTStamp BETWEEN RD.Beginning AND RD.Ending)
    ORDER BY AvgGPS.RecTStamp, AvgGPS.ReceivID;

... 최소한 예상 레코드 수에 부합하는 152928 개의 레코드를 반환합니다. 런타임은 아마도 i7-4790, 16GB RAM, SSD 없음, Win 8.1 Pro 시스템에서 5-10 분입니다.


참고 1 : MS 액세스 밀리 초 시간 값을 처리 할 수 - 정말소스 파일을 동반 [08080011.txt]

답변:


10

먼저 Access DB를 사용하여 이와 같은 작업을 수행하는 용기를 칭찬해야합니다. 내 경험으로는 SQL과 같은 것을 하기매우 어렵습니다 . 어쨌든, 검토에.


첫 가입

귀하의 IIF필드 선택은 사용 혜택을 누릴 수있는 스위치 문을 대신. 때로는 (특히 SQL과 관련하여) SWITCH일반적으로 CASEa의 본문에서 간단한 비교를 할 때 (일반적인 SQL에서 더 일반적으로 알려진 ) 경우가 매우 빠릅니다 SELECT. 한 필드에서 많은 비교를 포함하도록 스위치를 확장 할 수는 있지만 귀하의 경우 구문은 거의 동일합니다. 고려해야 할 것.

  SWITCH (
    expr1, val1,
    expr2, val2,
    val3        -- default value or "else"
  )

더 큰 문장에서 스위치는 또한 가독성을 도울 수 있습니다. 문맥:

  MAX(SWITCH(B.XTStamp <= A.RecTStamp,B.XTStamp,Null)) as BeforeXTStamp,
  --alternatively MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp) as BeforeXTStamp,
  MIN(SWITCH(B.XTStamp>A.RecTStamp,B.XTStamp,Null)) as AfterXTStamp

조인 자체에 관해서는, (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)당신이하려는 일을 고려할 때, 당신이 얻는 것만 큼 좋을 것이라고 생각 합니다. 그렇게 빠르지는 않지만 나도 그렇게 기대하지는 않습니다.


두 번째 조인

당신은 이것이 느리다고 말했다. 또한 코드 관점에서 읽기가 어렵습니다. 1과 2 사이에 똑같이 만족스러운 결과 세트가 주어지면 나는 1로 가라고 말할 것입니다. 적어도 당신이 그렇게하려고하는 것은 분명합니다. 하위 쿼리는 종종 빠르지 않지만 (이 경우 피할 수는 없지만) 각 경우에 추가 조인을 수행하므로 실행 계획이 확실히 복잡해야합니다.

한 가지 말로, 이전 ANSI-89 조인 구문을 사용했습니다. 최신 조인 구문을 사용하면 성능이 동일하거나 향상되며 모호하지 않거나 읽기가 쉽고 실수를하기가 더 어렵다는 점을 피하는 것이 가장 좋습니다.

FROM (FirstTable AS A INNER JOIN 
  (select top 1 B1.XTStamp, A1.RecTStamp 
   from SecondTable as B1
   inner join FirstTable as A1
     on B1.XTStamp <= A1.RecTStamp
   order by B1.XTStamp DESC) AS AbyB1 --MAX (time points before)

명명하는 것

나는 당신의 물건이 명명되는 방식은 기껏해야 도움이되지 않으며 최악의 경우에는 비밀이라고 생각합니다. A, B, A1, B1등 테이블 별칭으로 더 나을 수 있다고 생각합니다. 또한 필드 이름이 좋지 않다고 생각하지만 제어 할 수 없다는 것을 알고 있습니다. 나는 이름을 짓는 주제에 대한 코드리스 코드 를 빨리 인용 하고 그에 맡길 것입니다 ...

"독설은!" 여사 대답했다. “당신의 명사를 동사로!”


"다음 단계"쿼리

나는 그것이 어떻게 쓰여 졌는지 이해하지 못했고, 텍스트 편집기로 가져 와서 더 읽기 쉽도록 스타일을 변경해야했습니다. Access의 SQL 편집기가 복잡하지 않다는 것을 알고 있으므로 일반적으로 메모장 ++ 또는 Sublime Text와 같은 우수한 편집기로 쿼리를 작성합니다. 좀 더 가독성을 높이기 위해 적용한 일부 스타일 변경 사항 :

  • 2 칸 대신 4 칸 들여 쓰기
  • 수학 및 비교 연산자 주위의 공간
  • 더 자연스럽게 괄호와 들여 쓰기 배치 (자바 스타일 괄호를 사용했지만 원하는대로 C 스타일도 가능)

결과적으로 이것은 매우 복잡한 쿼리입니다. 그것을 이해하기 위해, 나는 가장 중요한 쿼리부터 시작해야합니다. ID데이터 세트는 첫 번째 조인과 동일하다는 것을 이해합니다. 관심있는 장치의 하위 세트 내에서 이전 / 이후 타임 스탬프가 가장 가까운 장치의 ID 및 타임 스탬프를 반환합니다. 대신 ID호출하지 마십시오 ClosestTimestampID.

귀하의 Det번만 사용되는 조인

여기에 이미지 설명을 입력하십시오

나머지 시간은 이미 가지고있는 값만 조인합니다 ClosestTimestampID. 대신 우리는 이것을 할 수 있어야합니다 :

    ) AS ClosestTimestampID
    INNER JOIN SecondTable AS TL1 
        ON ClosestTimestampID.BeforeXTStamp = TL1.XTStamp) 
    INNER JOIN SecondTable AS TL2 
        ON ClosestTimestampID.AfterXTStamp = TL2.XTStamp
    WHERE ClosestTimestampID.XmitID IN (<limited subset S>)

큰 성능 향상은 아니지만 열악한 Jet DB 옵티 마이저를 돕기 위해 할 수있는 모든 것이 도움이 될 것입니다!


보간에 사용 하는 계산 / 알고리즘이 더 잘 수행 될 수 있다는 느낌을 떨칠 수는 없지만 불행히도 나는 BeforeWeight그에 AfterWeight대해 좋지 않습니다.

충돌을 피하기위한 한 가지 제안 (응용 프로그램에 따라 이상적이지는 않지만) 중첩 된 하위 쿼리를 자체 테이블로 나누고 필요할 때 업데이트합니다. 소스 데이터를 얼마나 자주 새로 고쳐야하는지 잘 모르겠지만 너무 자주 그렇지 않으면 테이블 및 파생 테이블의 업데이트를 예약하기 위해 VBA 코드를 작성하고 가장 바깥 쪽 쿼리를 가져와야 할 수도 있습니다. 원래 소스 대신 해당 테이블에서 내가 이상적으로 말하지는 않았지만 도구가 주어지면 선택을하지 못할 수도 있습니다.


모두 함께 :

SELECT
    InGPS.XmitID,
    StrDateIso8601Msec(InGPS.RecTStamp) AS RecTStamp_ms,
       -- StrDateIso8601MSec is a VBA function returning a TEXT string in yyyy-mm-dd hh:nn:ss.lll format
    InGPS.ReceivID,
    RD.Receiver_Location_Description,
    RD.Lat AS Receiver_Lat,
    RD.Lon AS Receiver_Lon,
    InGPS.Before_Lat * InGPS.BeforeWeight + InGPS.After_Lat * InGPS.AfterWeight AS Xmit_Lat,
    InGPS.Before_Lon * InGPS.BeforeWeight + InGPS.After_Lon * InGPS.AfterWeight AS Xmit_Lon,
    InGPS.RecTStamp AS RecTStamp_basic
FROM (
    SELECT 
        ClosestTimestampID.RecTStamp,
        ClosestTimestampID.XmitID,
        ClosestTimestampID.ReceivID,
        ClosestTimestampID.BeforeXTStamp, 
        TL1.Latitude AS Before_Lat, 
        TL1.Longitude AS Before_Lon,
        (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) 
            / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) AS BeforeWeight,
        ClosestTimestampID.AfterXTStamp, 
        TL2.Latitude AS After_Lat, 
        TL2.Longitude AS After_Lon,
        (     (ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) 
            / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS AfterWeight
        FROM (((
            SELECT 
                A.RecTStamp, 
                A.ReceivID, 
                A.XmitID,
                MAX(SWITCH(B.XTStamp <= A.RecTStamp, B.XTStamp, Null)) AS BeforeXTStamp,
                MIN(SWITCH(B.XTStamp > A.RecTStamp, B.XTStamp, Null)) AS AfterXTStamp
            FROM FirstTable AS A
            INNER JOIN SecondTable AS B 
                ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp)
            WHERE A.XmitID IN (<limited subset S>)
            GROUP BY A.RecTStamp, ReceivID, XmitID
        ) AS ClosestTimestampID
        INNER JOIN FirstTable AS Det 
            ON (Det.XmitID = ClosestTimestampID.XmitID) 
            AND (Det.ReceivID = ClosestTimestampID.ReceivID) 
            AND (Det.RecTStamp = ClosestTimestampID.RecTStamp)) 
        INNER JOIN SecondTable AS TL1 
            ON ClosestTimestampID.BeforeXTStamp = TL1.XTStamp) 
        INNER JOIN SecondTable AS TL2 
            ON ClosestTimestampID.AfterXTStamp = TL2.XTStamp
        WHERE Det.XmitID IN (<limited subset S>)
    ) AS InGPS
INNER JOIN ReceiverDetails AS RD 
    ON (InGPS.ReceivID = RD.ReceivID) 
    AND (InGPS.RecTStamp BETWEEN <valid parameters from another table>)
ORDER BY StrDateIso8601Msec(InGPS.RecTStamp), InGPS.ReceivID;

5
  • 추가 속성 및 필터 조건이 추가되었습니다.
  • 최소 및 최대 중첩 쿼리를 사용하면 모든 형태의 크로스 조인이 제거됩니다. 이것이 가장 큰 성능 향상입니다.
  • 가장 내부 중첩 쿼리에서 반환 된 최소 및 최대 측면 값은 최종 계산을위한 탐색을 사용하여 추가 측면 속성 (위도 및 경도)을 검색하는 데 사용되는 기본 키 값 (스캔)입니다 (액세스에는 해당하는 항목이 있음).
  • 기본 테이블 속성은 가장 안쪽 쿼리에서 검색 및 필터링되며 성능에 도움이됩니다.
  • 정렬을 위해 시간 값을 형식화 (StrDateIso8601Msec) 할 필요가 없습니다. 테이블에서 날짜 시간 값을 사용하는 것이 동일합니다.

SQL Server 실행 계획 (액세스가이를 표시 할 수 없기 때문에)
비용이 많이 들기 때문에 최종 주문없이 :
Clustered Index Scan [ReceiverDetails]. [PK_ReceiverDetails] 비용 16 %
Clustered Index Seek [FirstTable]. [PK_FirstTable] Cost 19 %
Clustered Index [SecondTable]을 찾습니다. [PK_SecondTable] 비용 16 %
클러스터형 인덱스 찾기 [SecondTable]. [PK_SecondTable] 비용 16 %
Clustered Index를 찾아 [SecondTable]. [PK_SecondTable] [TL2] 비용 16 %
클러스터형 인덱스 찾기 [SecondTable]. [PK_SecondTable] [TL1] 비용 16 %

최종 주문 :
정렬 비용 36 %
클러스터 된 인덱스 스캔 [수신자 세부 사항]. [PK_ReceiverDetails] 비용 10 % 클러스터 된 인덱스 탐색 [SecondTable]. [PK_SecondTable] 비용 10 %
클러스터 된 인덱스 탐색 [FirstTable]. [PK_FirstTable] 비용 12 %

클러스터형 인덱스 탐색 [SecondTable]. [PK_SecondTable] 비용 10 %
클러스터형 인덱스 탐색 [SecondTable]. [PK_SecondTable] [TL2] 비용 10 %
클러스터형 인덱스 탐색 [SecondTable]. [ PK_SecondTable] [TL1] 비용 10 %

코드 :

select
     ClosestTimestampID.XmitID
    --,StrDateIso8601Msec(InGPS.RecTStamp) AS RecTStamp_ms
    ,ClosestTimestampID.ReceivID
    ,ClosestTimestampID.Receiver_Location_Description
    ,ClosestTimestampID.Lat
    ,ClosestTimestampID.Lon
,[TL1].[Latitude] * (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) + [TL2].[Latitude] * ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS Xmit_Lat
,[TL1].[Longitude] * (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) + [TL2].[Longitude] * ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS Xmit_Lon
    ,ClosestTimestampID.RecTStamp as RecTStamp_basic
from (
        (
            (
                select
                     FirstTable.RecTStamp
                    ,FirstTable.ReceivID
                    ,FirstTable.XmitID
                    ,ReceiverDetails.Receiver_Location_Description
                    ,ReceiverDetails.Lat
                    ,ReceiverDetails.Lon
                    ,(
                        select max(XTStamp) as val
                        from SecondTable
                        where XTStamp <= FirstTable.RecTStamp
                     ) as BeforeXTStamp
                    ,(
                        select min(XTStamp) as val
                        from SecondTable
                        where XTStamp > FirstTable.RecTStamp
                     ) as AfterXTStamp
                from FirstTable
                inner join ReceiverDetails
                on ReceiverDetails.ReceivID = FirstTable.ReceivID
                where FirstTable.RecTStamp between #1/1/1990# and #1/1/2020#
                and FirstTable.XmitID in (100,110)
            ) as ClosestTimestampID
            inner join SecondTable as TL1
            on ClosestTimestampID.BeforeXTStamp = TL1.XTStamp
        )
        inner join SecondTable as TL2
        on ClosestTimestampID.AfterXTStamp = TL2.XTStamp
    )
order by ClosestTimestampID.RecTStamp, ClosestTimestampID.ReceivID;

교차 조인이 포함 된 쿼리에 대해 쿼리를 테스트하는 성능

FirstTable은 13 개의 레코드로로드되었고 SecondTable은 1,000,000으로로드되었습니다.
내 쿼리의 실행 계획은 게시 된 내용과 크게 다르지 않았습니다.
교차 결합에 대한 실행 계획 : 스트림 집계 8 % 인덱스 스캔을 사용하는 경우 중첩 루프를
사용한 중첩 루프 비용 81 % INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp
가 75 %로 감소 [두 번째 테이블] [UK_ID] [B] 6 % 테이블 스풀 5 % 다른 여러 클러스터 된 인덱스 탐색 및 인덱스 탐색 비용이 0 % 인 (게시 된 쿼리와 유사)CROSS JOIN SecondTable AS B' or ',SecondTable AS B




내 쿼리와 CROSS JOIN의 실행 시간은 .007과 8-9 초입니다.
비용 비교 0 % 및 100 %.

조인 조건에 대해 50,000 개의 레코드가있는 FirstTable과 ReceiverDetails에 단일 레코드를로드하고 쿼리를 실행했습니다.
0.9와 1.0 사이에서 50,013이 반환되었습니다.

크로스 조인으로 두 번째 쿼리를 실행하고 약 20 분 동안 실행하여 종료했습니다.
교차 조인 쿼리가 원래 13 개만 반환하도록 필터링되면 실행 시간은 다시 8-9 초입니다.
필터 조건의 배치는 최 내부 선택, 최 외부 선택 및 둘 다에있었습니다. 차이 없음.

CROSS JOIN에 유리하게이 두 결합 조건간에 차이가 있으며, 첫 번째는 술어를 사용하고 CROSS JOIN은 사용하지 않습니다.
INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp) CROSS JOIN SecondTable AS B


내 시스템에서 ClosestTimestampID 부분을 실행하면 Count (*)로 캡슐화 된 경우 152928 개의 레코드가 즉시 반환됩니다. 해당 단계에서 실제 레코드를 반환 할 때 MSAccess가 잠겼습니다. 다른 방법의 임시 테이블이 모든 종류의 메모리를 사용하고 있었을 수 있습니다. 귀하의 방법론에서 생성 한 최종 쿼리는 현재 사용중인 것과 매우 유사하다고 생각합니다. 내가 생각하는 것은 좋은 것입니다 :)
mpag

1
원래 의견에서 귀하는 즉시 일부 기록을 되찾았다 고 표시했습니다. 이는 액세스 작동 방식과 관련하여 액세스 전략을 제시하고 실행 시간에 대한 기대치를 설정하는 데 중요합니다. 이를 지연된 실행이라고합니다. (마지막 레코드를 쳤을 때 충돌했습니다.) 최종 쿼리에있을 것으로 예상되는 상한 반환 레코드 수는 얼마입니까?
byrdzeye

저는 믿습니다 152928
mpag

새 레코드가 추가 될 때 두 테이블에서 DateTime 값의 특성은 무엇입니까? 현재 시간 소인 또는 최근 값이거나 완전히 임의입니까?
byrdzeye

첫 번째 테이블에는 2013 이상인 DateTime 스탬프가 있습니다. 두 번째 표에는 2015 년 중반에 몇 달 안에있는 DateTime 스탬프가 있습니다. 새 값이 추가되면 기존 값 이후 일 가능성이 높습니다. 테이블에 새 값을 추가 할 수 있습니다.
mpag

2

두 번째 답변을 첫 번째 답변보다 낫지 만 제시된 요구 사항을 변경하지 않고 추가하면 제출에 대한 액세스를 이길 수 있고 까다로워 보이는 몇 가지 방법이 있습니다. '트리거'를 사용하여 시간 효과에 약간의 합병증을 '재료 화'합니다. 액세스 테이블에는 트리거가 없으므로 crud 프로세스를 가로 채서 주입하십시오.

--*** Create a table for flank values.
    create table Flank (
         RecTStamp      datetime not null
        ,BeforeXTStamp  datetime null
        ,AfterXTStamp   datetime null
        ,constraint PK_Flank primary key clustered ( RecTStamp asc )
        )

--*** Create a FlankUpdateLoop sub. (create what is missing)
    -- loop until rowcount < 5000 or rowcount = 0
    -- a 5K limit appears to be manageable for Access, especially for the initial population.
    insert into Flank (
         RecTStamp
        ,BeforeXTStamp
        ,AfterXTStamp
        )
    select top 5000 FirstTable.RecTStamp
        ,(
            select max(XTStamp) as val
            from SecondTable
            where XTStamp <= FirstTable.RecTStamp
            ) as BeforeXTStamp
        ,(
            select min(XTStamp) as val
            from SecondTable
            where XTStamp > FirstTable.RecTStamp
            ) as AfterXTStamp
    from FirstTable
    left join Flank
        on FirstTable.RecTStamp = Flank.RecTStamp
    where Flank.RecTStamp is null;

--*** For FirstTable Adds, Changes or Deletes:
    delete from Flank where Flank.RecTStamp = CRUD_RecTStamp
    execute FlankUpdateLoop --See above. This will handle Adds, Changes or Deletes.

--*** For SecondTable Adds, Changes or Deletes:
    --delete from Flank where the old value is immediately before and after the new flank value.
    --They may or may not get be assigned a new value. Let FlankUpdate figure it out.

    --execute deletes for both beforextstamp and afterxtstamp
    --then update flank

    delete *
    from flank
    where beforextstamp between (
                    select min(beforextstamp)
                    from flank
                    where beforextstamp >= '3/16/2009 10:00:46 AM'
                    ) and (
                    select max(beforextstamp)
                    from flank
                    where beforextstamp <= '3/16/2009 10:00:46 AM'
                    );

    delete *
    from flank
    where afterxtstamp between (
                    select min(afterxtstamp)
                    from flank
                    where afterxtstamp >= '3/16/2009 10:00:46 AM'
                    ) and (
                    select max(afterxtstamp)
                    from flank
                    where afterxtstamp <= '3/16/2009 10:00:46 AM'
                    );

    execute FlankUpdateLoop

--*** Final Report Query***--
    --Should execute without issues including 'deferred execution' problem.
    --Add filters as needed.
    select FirstTable.XmitID
        ,FirstTable.ReceivID
        ,ReceiverDetails.Lat
        ,ReceiverDetails.Lon
        ,BeforeTable.Latitude * (1 - ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp))) + AfterTable.Latitude * ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp)) as Xmit_Lat
        ,BeforeTable.Longitude * (1 - ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp))) + AfterTable.Longitude * ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp)) as Xmit_Lon
        ,FirstTable.RecTStamp as RecTStamp_basic
    from (((
        FirstTable
    inner join Flank on FirstTable.RecTStamp = Flank.RecTStamp)
    inner join SecondTable as BeforeTable on Flank.BeforeXTStamp = BeforeTable.XTStamp)
    inner join SecondTable as AfterTable on Flank.AfterXTStamp = AfterTable.XTStamp)
    inner join ReceiverDetails on FirstTable.ReceivID = ReceiverDetails.ReceivID
    order by FirstTable.RecTStamp;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.