Kata-kata pertama
Anda dapat dengan aman mengabaikan bagian di bawah ini (dan termasuk) GABUNG: Memulai jika Anda hanya ingin mengambil sedikit kode. The latar belakang dan hasil hanya berfungsi sebagai konteks. Silakan lihat histori edit sebelum 2015-10-06 jika Anda ingin melihat seperti apa kode itu pada awalnya.
Objektif
Pada akhirnya saya ingin menghitung koordinat GPS yang diinterpolasi untuk pemancar ( X
atau Xmit
) berdasarkan perangko DateTime dari data GPS yang tersedia dalam tabel SecondTable
yang secara langsung mengapit pengamatan dalam tabel FirstTable
.
Tujuan jangka pendek saya untuk mencapai tujuan utamanya adalah untuk mencari tahu cara terbaik untuk bergabung FirstTable
ke SecondTable
untuk mendapatkan orang-mengapit titik waktu. Kemudian saya dapat menggunakan informasi itu saya dapat menghitung koordinat GPS menengah dengan asumsi pemasangan linier sepanjang sistem koordinat equirectangular (kata-kata mewah untuk mengatakan saya tidak peduli bahwa Bumi adalah bola pada skala ini).
Pertanyaan
- Apakah ada cara yang lebih efisien untuk menghasilkan prangko waktu sebelum dan sesudah?
- Diperbaiki sendiri dengan hanya meraih "setelah," dan kemudian mendapatkan "sebelum" hanya karena terkait dengan "setelah".
- Apakah ada cara yang lebih intuitif yang tidak melibatkan
(A<>B OR A=B)
struktur. - Pikiran, trik, dan saran lain yang mungkin Anda miliki.
- Demikian pula, baik byrdzeye dan Phrancis telah cukup membantu dalam hal ini. Saya menemukan bahwa saran Phrancis sangat baik diletakkan dan memberikan bantuan pada tahap kritis, jadi saya akan memberinya keunggulan di sini.
Saya masih akan menghargai setiap bantuan tambahan yang dapat saya terima sehubungan dengan pertanyaan 3. Bulletpoints mencerminkan siapa yang saya percaya paling membantu saya pada pertanyaan individu.
Definisi Tabel
Representasi semi-visual
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
Tabel kedua
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
Tabel 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
Tabel ValidXmitters
Field (and primary key)
XmitID | TEXT(25) -- primary, unique, no null, simple
Biola SQL ...
... sehingga Anda bisa bermain dengan definisi dan kode tabel. Pertanyaan ini untuk MSAccess, tetapi seperti yang ditunjukkan Phrancis, tidak ada gaya biola SQL untuk Access. Jadi, Anda dapat pergi ke sini untuk melihat definisi dan kode tabel saya berdasarkan jawaban Phrancis :
http://sqlfiddle.com/#!6/e9942/4 (tautan eksternal)
BERGABUNG: Memulai
Strategi GINJAL "batin" saya saat ini
Pertama buat FirstTable_rekeyed dengan urutan kolom dan kunci primer majemuk (RecTStamp, ReceivID, XmitID)
semua diindeks / diurutkan ASC
. Saya juga membuat indeks pada setiap kolom secara individual. Kemudian isi seperti itu.
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;
Kueri di atas mengisi tabel baru dengan 153006 catatan dan kembali dalam hitungan 10 detik atau lebih.
Berikut ini selesai dalam satu atau dua detik ketika seluruh metode ini dibungkus dalam "SELECT Count (*) FROM (...)" ketika metode subquery TOP 1 digunakan
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))
Permintaan JOIN "nyali batin" sebelumnya
Pertama (fastish ... tapi tidak cukup bagus)
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)))
Kedua (lebih lambat)
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;
Latar Belakang
Saya memiliki tabel telemetri (alias A) dengan jumlah hanya di bawah 1 juta entri dengan kunci primer majemuk berdasarkan DateTime
cap, ID Pemancar, dan ID Perangkat Perekaman. Karena keadaan di luar kendali saya, bahasa SQL saya adalah Jet DB standar di Microsoft Access (pengguna akan menggunakan versi 2007 dan yang lebih baru). Hanya sekitar 200.000 entri ini yang relevan dengan kueri karena ID Pemancar.
Ada tabel telemetri kedua (alias B) yang melibatkan sekitar 50.000 entri dengan DateTime
kunci primer tunggal
Untuk langkah pertama, saya fokus pada menemukan cap waktu terdekat ke perangko di tabel pertama dari tabel kedua.
GABUNGKAN Hasil
Keanehan yang Saya Temukan ...
... selama debugging
Rasanya sangat aneh untuk menulis JOIN
logika seperti FROM FirstTable as A INNER JOIN SecondTable as B ON (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)
yang ditunjukkan oleh @byrdzeye dalam komentar (yang telah menghilang) adalah bentuk cross-join. Perhatikan bahwa mengganti LEFT OUTER JOIN
untuk INNER JOIN
dalam kode di atas muncul untuk membuat dampak dalam kuantitas atau identitas dari garis kembali. Saya juga sepertinya tidak bisa meninggalkan klausa ON atau mengatakan ON (1=1)
. Hanya menggunakan koma untuk bergabung (bukan INNER
atau LEFT OUTER
JOIN
) menghasilkan Count(select * from A) * Count(select * from B)
baris yang dikembalikan dalam kueri ini, bukan hanya satu baris per tabel A, sebagai JOIN
pengembalian eksplisit (A <> B ATAU A = B) . Ini jelas tidak cocok. FIRST
tampaknya tidak tersedia untuk digunakan mengingat jenis kunci utama majemuk.
Gaya Kedua JOIN
, meskipun bisa dibilang lebih terbaca, menderita lebih lambat. Ini mungkin karena dua inner JOIN
tambahan diperlukan terhadap tabel yang lebih besar serta keduanya CROSS JOIN
ditemukan di kedua opsi.
Selain: Mengganti IIF
klausa dengan MIN
/ MAX
muncul untuk mengembalikan jumlah entri yang sama.
MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
berfungsi untuk MAX
cap waktu "Sebelum" ( ), tetapi tidak bekerja langsung untuk "Setelah" ( MIN
) sebagai berikut:
MIN(-(B.XTStamp>A.RecTStamp)*B.XTStamp)
karena minimum selalu 0 untuk FALSE
kondisi. Angka 0 ini lebih kecil dari pada setiap periode pasca DOUBLE
( DateTime
bidang mana yang merupakan bagian dari Access dan perhitungan ini mengubah bidang menjadi). Metode IIF
dan MIN
/ / MAX
Alternatif yang diusulkan untuk nilai AfterXTStamp berfungsi karena pembagian dengan nol ( FALSE
) menghasilkan nilai nol, yang fungsi agregatnya MIN dan MAX lewati.
Langkah selanjutnya
Mengambil ini lebih lanjut, saya ingin menemukan cap waktu di tabel kedua yang secara langsung mengapit cap waktu di tabel pertama dan melakukan interpolasi linier dari nilai data dari tabel kedua berdasarkan jarak waktu ke titik-titik tersebut (yaitu jika cap waktu dari tabel pertama adalah 25% dari jalan antara "sebelum" dan "setelah", saya ingin 25% dari nilai yang dihitung berasal dari data nilai tabel 2 yang terkait dengan titik "setelah" dan 75% dari "sebelum" ). Menggunakan tipe gabungan yang direvisi sebagai bagian dari nyali batin, dan setelah jawaban yang disarankan di bawah ini saya ...
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;
... yang mengembalikan 152928 catatan, menyesuaikan (setidaknya kira-kira) ke jumlah akhir dari catatan yang diharapkan. Run time mungkin 5-10 menit pada i7-4790, RAM 16GB, tanpa SSD, sistem Win 8.1 Pro saya.
Referensi 1: MS Access Dapat Menangani Nilai Waktu Milidetik - Benar-benar dan menyertai file sumber [08080011.txt]
Menambahkan jawaban kedua, tidak lebih baik dari yang pertama tetapi tanpa mengubah salah satu persyaratan yang disajikan, ada beberapa cara untuk mengalahkan Access menjadi pengajuan dan tampak tajam. 'Mewujudkan' komplikasi sedikit demi sedikit dengan menggunakan 'pemicu'. Tabel akses tidak memiliki pemicu sehingga memotong dan menyuntikkan proses kasar.
sumber