最初の言葉
あなたは安全に(を含む)以下のセクションを無視することができます結合します皮切りあなただけのコードの亀裂を取りたい場合。背景と結果がちょうど文脈としての役割を果たす。最初にコードがどのように表示されたかを確認するには、2015年10月6日より前の編集履歴をご覧ください。
目的
最終的には、表の観測値に直接隣接する表の利用可能なGPSデータのDateTimeスタンプに基づいて、送信機(X
またはXmit
)の補間GPS座標を計算SecondTable
しますFirstTable
。
究極の目標を達成するための私の当面の目標は、これらの側面の時点を得るためにどのように参加FirstTable
するのSecondTable
が最善かを見つけることです。後で、その情報を使用して、正距円筒座標系に沿った線形近似を仮定して中間GPS座標を計算できます(このスケールでは、地球が球体であることを気にしないと言った派手な言葉)。
ご質問
- 最も近い前後のタイムスタンプを生成するより効率的な方法はありますか?
- 「after」を取得し、「after」に関連する場合にのみ「before」を取得することで、自分で修正しました。
(A<>B OR A=B)
構造を含まない、より直感的な方法はありますか。- Byrdzeyeは基本的な選択肢を提供しましたが、私の「実世界」の経験は、同じことを実行する彼の4つの結合戦略すべてとは一致しませんでした。しかし、代替結合スタイルに対処したことに対する彼の完全な信用。
- あなたが持つかもしれない他の考え、トリックやアドバイス。
- 両方Thusfar byrdzeyeとPhrancisはこの点で非常に役立っています。私は、ことがわかっPhrancis'アドバイスが、私はここに彼に端をあげるので良好、レイアウトおよび重要な段階での支援を提供しました。
質問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
(外部リンク)
JOINs:スタート
私の現在の「内臓」JOIN Strategy
最初に、列の順序で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)))
2番目(遅い)
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;
バックグラウンド
DateTime
スタンプ、トランスミッターID、および録音デバイスIDに基づく複合主キーを持つ100万件未満のエントリのテレメトリテーブル(エイリアスA)があります。私が制御できない状況のため、私のSQL言語はMicrosoft Accessの標準のJet DBです(ユーザーは2007以降のバージョンを使用します)。トランスミッタIDのため、これらのエントリのうち約200,000のみがクエリに関連しています。
1つのDateTime
主キーを持つ約50,000のエントリを含む2番目のテレメトリテーブル(エイリアスB)があります
最初のステップでは、2番目のテーブルから最初のテーブルのスタンプに最も近いタイムスタンプを見つけることに焦点を当てました。
参加結果
私が発見した癖...
...デバッグ中
それは書くことが本当に奇妙な感じ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)
。コンマを使用して(INNER
またはではなくLEFT OUTER
JOIN
)結合するCount(select * from A) * Count(select * from B)
と、(A <> B OR A = B)明示的にJOIN
返されるように、テーブルAごとに1行だけではなく、このクエリで行が返されます。これは明らかに適切ではありません。FIRST
複合主キータイプが指定されている場合は使用できないようです。
2番目のJOIN
スタイルは、間違いなく読みやすいとはいえ、速度が遅いという問題があります。これは、両方のオプションにJOIN
ある2つCROSS JOIN
のs と同様に、より大きなテーブルに対して2つの内部がさらに必要になるためです。
余談: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
ゼロによる除算ためAfterXTStamp値作業のために提案された方法交互に(FALSE
)集計関数MINとMAXはスキップnull値を生成します。
次のステップ
これをさらに進めると、最初のテーブルのタイムスタンプに直接隣接する2番目のテーブルのタイムスタンプを見つけ、それらのポイントまでの時間距離に基づいて2番目のテーブルのデータ値の線形補間を実行します(つまり、最初のテーブルは「前」と「後」の間の道の25%です。計算された値の25%は、「後」ポイントに関連付けられた2番目のテーブル値データから、「前」からは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分です。