첫 단어
아래의 섹션 (및 JOIN 포함)을 무시 해도됩니다. 배경 과 결과는 단지 상황에 맞는 역할을한다. 코드가 처음에 어떻게 보이는지 보려면 2015-10-06 이전의 편집 기록을 참조하십시오.
목표
궁극적 으로 표 에서 관측을 직접 옆에있는 표에서 사용 가능한 GPS 데이터의 DateTime 스탬프를 기반으로 송신기 ( X
또는 Xmit
)에 대한 보간 GPS 좌표를 계산하려고 SecondTable
합니다.FirstTable
.
가장 참여에 궁극적 인 목표를 달성하기 나의 즉각적인 목표는 방법을 알아낼 것이다 FirstTable
하는 SecondTable
그 측면 시간 포인트를 획득 할 수 있습니다. 나중에 그 정보를 사용할 수 있습니다. 나는 직각 좌표계를 따라 선형 피팅을 가정하고 중간 지구 GPS 좌표를 계산할 수 있습니다.
질문
- 가장 가까운 전후 타임 스탬프를 생성하는보다 효율적인 방법이 있습니까?
- "after"를 잡고 "after"와 관련된 "before"를 얻는 것만으로 스스로 고쳤습니다.
- 더 직관적 인 방법이 있습니까?
(A<>B OR A=B)
구조를- Byrdzeye 는 기본적인 대안을 제공했지만, 나의 "실제"경험은 그의 4 가지 조인 전략이 모두 같은 성과를 거두지 못했습니다. 그러나 대체 조인 스타일을 해결 한 것에 대해 전적으로 공로를 인정했습니다.
- 다른 생각, 요령 및 조언이있을 수 있습니다.
질문 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에서 하위 집합이며이 계산으로 필드를 변환합니다). IIF
및 MIN
/ MAX
0으로 나누기 때문에 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]