คำแรก
คุณสามารถละเว้นส่วนด้านล่าง (และรวมถึง) อย่างปลอดภัยเข้าร่วม: เริ่มต้นปิดถ้าคุณเพียงแค่ต้องการที่จะถอดรหัสรหัส พื้นหลังและผลเพียงทำหน้าที่เป็นบริบท โปรดดูประวัติการแก้ไขก่อน 2015-10-06 หากคุณต้องการดูว่ารหัสดูเหมือนเป็นครั้งแรก
วัตถุประสงค์
ในท้ายที่สุดผมต้องการที่จะคำนวณพิกัด GPS หยันสำหรับเครื่องส่งสัญญาณ ( X
หรือXmit
) ตามแสตมป์วันที่และเวลาของข้อมูลจีพีเอสที่มีอยู่ในตารางที่ด้านข้างโดยตรงสังเกตในตารางSecondTable
FirstTable
เป้าหมายทันทีของฉันในการบรรลุเป้าหมายสูงสุดคือการหาวิธีเข้าร่วมFirstTable
ให้ดีที่สุดSecondTable
เพื่อรับคะแนนเวลาขนาบข้างนั้น ต่อมาฉันสามารถใช้ข้อมูลนั้นได้ฉันสามารถคำนวณพิกัด GPS ระดับกลางโดยสมมติว่าการปรับตำแหน่งเชิงเส้นตามระบบพิกัดแบบคู่ขนาน (คำที่น่าประหลาดใจที่จะบอกว่าฉันไม่สนใจว่าโลกเป็นทรงกลมในระดับนี้)
คำถาม
- มีวิธีที่มีประสิทธิภาพมากกว่าในการสร้างการประทับเวลาก่อนและหลังที่ใกล้เคียงที่สุดหรือไม่
- แก้ไขด้วยตัวเองโดยเพียงแค่คว้า "หลัง" แล้วรับ "ก่อน" เฉพาะเมื่อมันเกี่ยวข้องกับ "หลัง"
- มีวิธีที่ง่ายกว่าที่ไม่เกี่ยวข้องกับ
(A<>B OR A=B)
โครงสร้างหรือไม่- Byrdzeyeให้ทางเลือกพื้นฐาน แต่ประสบการณ์ "โลกแห่งความจริง" ของฉันไม่ตรงกับกลยุทธ์การเข้าร่วมทั้ง 4 ของเขาที่ทำแบบเดียวกัน แต่ให้เครดิตเต็มที่กับเขาสำหรับการระบุสไตล์การเข้าร่วมอื่น
- ความคิดเทคนิคและคำแนะนำอื่น ๆ ที่คุณมี
- ดังนั้นไกลทั้งbyrdzeyeและPhrancisค่อนข้างมีประโยชน์ในเรื่องนี้ ฉันพบว่าคำแนะนำของ Phrancisถูกจัดวางอย่างยอดเยี่ยมและให้ความช่วยเหลือในช่วงวิกฤติดังนั้นฉันจะให้เขาได้เปรียบที่นี่
ฉันยังคงขอขอบคุณความช่วยเหลือเพิ่มเติมใด ๆ ที่ฉันสามารถรับได้ในเรื่องข้อ 3 Bulletpoints แสดงว่าฉันเชื่อว่าใครช่วยฉันมากที่สุดในคำถามแต่ละข้อ
คำจำกัดความของตาราง
การเป็นตัวแทนกึ่งภาพ
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
รับตารางรายละเอียด
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 ชี้ให้เห็นไม่มีสไตล์ซอ SQL สำหรับการเข้าถึง ดังนั้นคุณควรจะไปที่นี่เพื่อดูคำจำกัดความของตารางและรหัสของฉันตามคำตอบของวลี :
http://sqlfiddle.com/#!6/e9942/4 (ลิงก์ภายนอก)
เข้าร่วม: การเริ่มต้น
เข้าร่วมกลยุทธ์ "Inner guts" ปัจจุบันของฉัน
แรกสร้าง 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 วินาที
ต่อไปนี้จะเสร็จสิ้นภายในหนึ่งหรือสองวินาทีเมื่อวิธีการทั้งหมดนี้ถูกห่อด้วย "SELECT Count (*) FROM (... )" เมื่อใช้วิธีการสืบค้นย่อย 1 อันดับแรก
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))
คำค้นหา "Inner guts" ก่อนหน้าเข้าร่วม
ครั้งแรก (Fastish ... แต่ไม่ดีพอ)
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;
พื้นหลัง
ฉันมีตาราง telemetry (นามแฝงเป็น A) ของรายการต่ำกว่า 1 ล้านรายการที่มีคีย์หลักประสมที่ขึ้นอยู่กับDateTime
ตราประทับรหัสส่งสัญญาณและรหัสอุปกรณ์บันทึก เนื่องจากสถานการณ์ที่อยู่นอกเหนือการควบคุมของฉันภาษา SQL ของฉันคือ Jet DB มาตรฐานใน Microsoft Access (ผู้ใช้จะใช้ 2007 และรุ่นใหม่กว่า) รายการเหล่านี้มีความเกี่ยวข้องกับแบบสอบถามเพียงประมาณ 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 (1=1)
ฉันยังไม่สามารถดูเหมือนจะทิ้งประโยคเปิดหรือพูด เพียงใช้เครื่องหมายจุลภาคเพื่อเข้าร่วม (แทนที่จะเป็นINNER
หรือLEFT OUTER
JOIN
) ผลลัพธ์ในCount(select * from A) * Count(select * from B)
แถวที่ส่งคืนในแบบสอบถามนี้แทนที่จะเป็นเพียงหนึ่งบรรทัดต่อตาราง A เนื่องจากผลลัพธ์(A <> B OR A = B) JOIN
กลับมาอย่างชัดเจน เห็นได้ชัดว่าไม่เหมาะ FIRST
ดูเหมือนจะไม่พร้อมใช้งานสำหรับประเภทคีย์หลักผสม
JOIN
รูปแบบที่สองแม้ว่าเนื้อหาที่ชัดเจนมากขึ้นทนทุกข์ทรมานจากการถูกช้าลง นี่อาจเป็นเพราะต้องการอีกสองJOIN
s ภายในกับตารางที่มีขนาดใหญ่กว่าและสองCROSS JOIN
s ที่พบในตัวเลือกทั้งสอง
นอกเหนือ: การแทนที่ส่วนIIF
คำสั่งด้วยMIN
/ MAX
ปรากฏขึ้นเพื่อส่งคืนจำนวนรายการเท่ากัน
MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
ใช้งานได้กับการMAX
ประทับเวลา"Before" ( ) แต่ไม่สามารถใช้งานได้โดยตรงกับ "After" ( MIN
) ดังต่อไปนี้:
MIN(-(B.XTStamp>A.RecTStamp)*B.XTStamp)
เนื่องจากค่าต่ำสุดจะเป็น 0 เสมอสำหรับFALSE
เงื่อนไข 0 นี้น้อยกว่ายุคหลังใด ๆDOUBLE
(ซึ่งDateTime
เขตข้อมูลเป็นส่วนย่อยของใน Access และการคำนวณนี้แปลงเขตข้อมูลลงใน) กระบวนการIIF
และMIN
/ MAX
วิธีทางเลือกที่เสนอสำหรับค่า AfterXTStamp ทำงานเนื่องจากการหารด้วยศูนย์ ( FALSE
) สร้างค่า Null ซึ่งฟังก์ชันการรวม MIN และ MAX จะข้ามไป
ขั้นตอนถัดไป
การเพิ่มเติมนี้ฉันต้องการค้นหา timestamps ในตารางที่สองที่ขนาบข้าง timestamps ในตารางแรกโดยตรงและทำการแก้ไขเชิงเส้นตรงของค่าข้อมูลจากตารางที่สองตามระยะเวลาไปยังจุดเหล่านั้น (เช่นถ้า timestamp จาก ตารางแรกคือ 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 ซึ่งเป็นไปตาม (อย่างน้อยประมาณ) กับจำนวนระเบียนสุดท้ายที่คาดไว้ เวลาทำงานอาจเป็น 5-10 นาทีสำหรับ i7-4790, RAM 16GB, ไม่มี SSD, ระบบ Win 8.1 Pro ของฉัน
การอ้างอิง 1: MS Access สามารถจัดการกับค่าเวลามิลลิวินาที - ได้จริงและมาพร้อมกับไฟล์ต้นฉบับ [08080011.txt]