Từ đầu tiên
Bạn có thể yên tâm bỏ qua các phần bên dưới (và bao gồm) THAM GIA: Bắt đầu nếu bạn chỉ muốn bẻ khóa mã. Các nền và kết quả chỉ làm bối cảnh. Vui lòng xem lịch sử chỉnh sửa trước 2015-10-06 nếu bạn muốn xem mã ban đầu trông như thế nào.
Mục tiêu
Cuối cùng, tôi muốn tính toán tọa độ GPS nội suy cho máy phát ( X
hoặc Xmit
) dựa trên tem DateTime của dữ liệu GPS có sẵn trong bảng SecondTable
trực tiếp quan sát trong bảng FirstTable
.
Mục tiêu trước mắt của tôi để đạt được mục tiêu cuối cùng là để tìm ra cách để tham gia tốt nhất FirstTable
để SecondTable
để có được những thời điểm chầu. Sau này tôi có thể sử dụng thông tin đó Tôi có thể tính toán tọa độ GPS trung gian giả sử khớp tuyến tính dọc theo hệ tọa độ tương đương (những từ ưa thích để nói rằng tôi không quan tâm Trái đất là một hình cầu ở quy mô này).
Câu hỏi
- Có cách nào hiệu quả hơn để tạo ra tem thời gian trước và sau gần nhất không?
- Tự sửa lỗi bằng cách chỉ lấy "sau", và sau đó chỉ nhận được "trước" vì nó liên quan đến "sau".
- Có cách nào trực quan hơn mà không liên quan đến
(A<>B OR A=B)
cấu trúc.- Byrdzeye cung cấp các lựa chọn thay thế cơ bản, tuy nhiên trải nghiệm "thế giới thực" của tôi không phù hợp với cả 4 chiến lược tham gia của anh ấy đều thực hiện như nhau. Nhưng tín dụng đầy đủ cho anh ta để giải quyết các phong cách tham gia thay thế.
- Bất kỳ suy nghĩ, thủ thuật và lời khuyên nào bạn có thể có.
- Do đó, cả byrdzeye và Phrancis đều khá hữu ích trong vấn đề này. Tôi thấy rằng lời khuyên của Phrancis đã được đưa ra một cách xuất sắc và cung cấp trợ giúp ở giai đoạn quan trọng, vì vậy tôi sẽ cho anh ta lợi thế ở đây.
Tôi vẫn sẽ đánh giá cao bất kỳ trợ giúp bổ sung nào mà tôi có thể nhận được liên quan đến câu hỏi 3. Phản ánh người mà tôi tin rằng đã giúp tôi nhiều nhất cho câu hỏi cá nhân.
Bảng định nghĩa
Đại diện bán trực quan
Đầu tiên
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
Bảng thứ hai
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
Bảng NhậnDetails
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 bảng
Field (and primary key)
XmitID | TEXT(25) -- primary, unique, no null, simple
Câu đố SQL ...
... để bạn có thể chơi với các định nghĩa và mã bảng Câu hỏi này dành cho MSAccess, nhưng như Phrancis đã chỉ ra, không có kiểu fiddle SQL nào cho Access. Vì vậy, bạn sẽ có thể vào đây để xem định nghĩa và mã bảng của tôi dựa trên câu trả lời của Phrancis :
http://sqlfiddle.com/#!6/e9942/4 (liên kết ngoài)
THAM GIA: Bắt đầu
Chiến lược "nội tâm" hiện tại của tôi
Đầu tiên tạo một FirstTable_Vkeyed với thứ tự cột và khóa chính ghép (RecTStamp, ReceivID, XmitID)
tất cả được lập chỉ mục / sắp xếp ASC
. Tôi cũng đã tạo các chỉ mục trên từng cột riêng lẻ. Sau đó điền vào như vậy.
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;
Truy vấn trên điền vào bảng mới với 153006 bản ghi và trả về trong vòng 10 giây hoặc lâu hơn.
Sau đây hoàn thành trong vòng một hoặc hai giây khi toàn bộ phương thức này được gói trong "Đếm CHỌN (*) TỪ (...)" khi phương thức truy vấn con TOP 1 được sử dụng
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))
Truy vấn "ruột bên trong" trước đó
Đầu tiên (nhanh ... nhưng không đủ tốt)
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)))
Thứ hai (chậm hơn)
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;
Lý lịch
Tôi có một bảng từ xa (có biệt danh là A) chỉ dưới 1 triệu mục với khóa chính ghép dựa trên DateTime
tem, ID máy phát và ID thiết bị ghi. Do hoàn cảnh nằm ngoài tầm kiểm soát của tôi, ngôn ngữ SQL của tôi là Jet DB tiêu chuẩn trong Microsoft Access (người dùng sẽ sử dụng các phiên bản 2007 trở lên). Chỉ có khoảng 200.000 mục này có liên quan đến truy vấn vì ID máy phát.
Có một bảng từ xa thứ hai (bí danh B) liên quan đến khoảng 50.000 mục với một DateTime
khóa chính
Bước đầu tiên, tôi tập trung vào việc tìm các dấu thời gian gần nhất với các tem trong bảng đầu tiên từ bảng thứ hai.
Kết quả THAM GIA
Những điều kỳ quặc mà tôi đã khám phá ...
... trên đường đi trong quá trình gỡ lỗi
Nó cảm thấy thực sự kỳ lạ được viết JOIN
logic như FROM FirstTable as A INNER JOIN SecondTable as B ON (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)
mà như @byrdzeye chỉ ra trong một chú thích (có từ biến mất) là một hình thức cross-tham gia. Lưu ý rằng thay LEFT OUTER JOIN
vì INNER JOIN
trong đoạn code trên sẽ xuất hiện để làm cho không có tác động về số lượng hoặc nhận dạng của các dòng trả lại. Tôi dường như cũng không thể bỏ qua mệnh đề ON hoặc nói ON (1=1)
. Chỉ cần sử dụng dấu phẩy để nối (chứ không phải INNER
hoặc LEFT OUTER
JOIN
) dẫn đến Count(select * from A) * Count(select * from B)
các hàng được trả về trong truy vấn này, thay vì chỉ một dòng trên mỗi bảng A, như JOIN
trả về rõ ràng (A <> B HOẶC A = B) . Điều này rõ ràng là không phù hợp. FIRST
dường như không có sẵn để sử dụng một loại khóa chính tổng hợp.
JOIN
Phong cách thứ hai , mặc dù có thể dễ đọc hơn, nhưng bị chậm hơn. Điều này có thể là do hai JOIN
s bên trong được yêu cầu so với bảng lớn hơn cũng như hai CROSS JOIN
s được tìm thấy trong cả hai tùy chọn.
Ngoài ra: Thay thế IIF
mệnh đề bằng MIN
/ MAX
xuất hiện để trả về cùng số lượng mục nhập.
MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
hoạt động cho MAX
dấu thời gian "Trước" ( ), nhưng không hoạt động trực tiếp cho "Sau" ( MIN
) như sau:
MIN(-(B.XTStamp>A.RecTStamp)*B.XTStamp)
vì mức tối thiểu luôn là 0 cho FALSE
điều kiện. Giá trị 0 này nhỏ hơn bất kỳ hậu kỷ nguyên nào DOUBLE
(mà một DateTime
trường là tập con của Access và phép tính này biến đổi trường thành). Các phương thức IIF
và MIN
/ MAX
Các phương án được đề xuất cho giá trị AfterXTStamp hoạt động vì phép chia cho zero ( FALSE
) tạo ra các giá trị null, mà các hàm tổng hợp MIN và MAX bỏ qua.
Bước tiếp theo
Tiếp tục, tôi muốn tìm các dấu thời gian trong bảng thứ hai trực tiếp trượt các dấu thời gian trong bảng đầu tiên và thực hiện phép nội suy tuyến tính của các giá trị dữ liệu từ bảng thứ hai dựa trên khoảng cách thời gian đến các điểm đó (nghĩa là nếu dấu thời gian từ bảng đầu tiên là 25% giữa "trước" và "sau", tôi muốn 25% giá trị được tính đến từ dữ liệu giá trị bảng thứ 2 được liên kết với điểm "sau" và 75% từ "trước" ). Sử dụng loại tham gia được sửa đổi như một phần của can đảm bên trong và sau các câu trả lời được đề xuất bên dưới, tôi tạo ra ...
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;
... Trả về 152928 hồ sơ, tuân thủ (ít nhất là xấp xỉ) với số lượng hồ sơ dự kiến cuối cùng. Thời gian chạy có lẽ là 5-10 phút trên i7-4790, RAM 16 GB, không có SSD, hệ thống Win 8.1 Pro.
Tham khảo 1: Quyền truy cập MS có thể xử lý các giá trị thời gian một phần nghìn giây - Tập tin nguồn thực sự và kèm theo [08080011.txt]