Truy cập (Jet) SQL: Tem DateTime trong TableB đặt bên cạnh mỗi tem DateTime trong TableA


21

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ềnkế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 ( Xhoặc Xmit) dựa trên tem DateTime của dữ liệu GPS có sẵn trong bảng SecondTabletrự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

  1. 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".
  2. 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ế.
  3. Bất kỳ suy nghĩ, thủ thuật và lời khuyên nào bạn có thể có.
    • Do đó, cả byrdzeyePhrancis đề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 DateTimetem, 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 JOINlogic 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 JOININNER JOINtrong đ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 INNERhoặ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ư JOINtrả về rõ ràng (A <> B HOẶC A = B) . Điều này rõ ràng là không phù hợp. FIRSTdường như không có sẵn để sử dụng một loại khóa chính tổng hợp.

JOINPhong 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 JOINs bên trong được yêu cầu so với bảng lớn hơn cũng như hai CROSS JOINs được tìm thấy trong cả hai tùy chọn.

Ngoài ra: Thay thế IIFmệnh đề bằng MIN/ MAXxuấ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 MAXdấ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 DateTimetrườ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 IIFMIN/ MAXCá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ựkèm theo [08080011.txt]

Câu trả lời:


10

Trước tiên tôi phải khen bạn về lòng can đảm của bạn để làm điều gì đó như thế này với Access DB, điều mà theo kinh nghiệm của tôi là rất khó để làm bất cứ điều gì giống như SQL. Dù sao, để xem xét.


Đầu tiên tham gia

Thay vào đó, các IIFlựa chọn trường của bạn có thể được hưởng lợi từ việc sử dụng câu lệnh Switch . Đôi khi có vẻ như vậy, đặc biệt là với SQL, rằng một SWITCH(thường được gọi là CASEtrong SQL điển hình) khá nhanh khi chỉ thực hiện các so sánh đơn giản trong cơ thể của a SELECT. Cú pháp trong trường hợp của bạn sẽ gần như giống hệt nhau, mặc dù một công tắc có thể được mở rộng để bao gồm một khối lớn các so sánh trong một trường. Một cái gì đó để xem xét.

  SWITCH (
    expr1, val1,
    expr2, val2,
    val3        -- default value or "else"
  )

Một chuyển đổi cũng có thể giúp dễ đọc, trong các báo cáo lớn hơn. Trong ngữ cảnh:

  MAX(SWITCH(B.XTStamp <= A.RecTStamp,B.XTStamp,Null)) as BeforeXTStamp,
  --alternatively MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp) as BeforeXTStamp,
  MIN(SWITCH(B.XTStamp>A.RecTStamp,B.XTStamp,Null)) as AfterXTStamp

Đối với việc tham gia, tôi nghĩ (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)là tốt như bạn sẽ nhận được, cho những gì bạn đang cố gắng làm. Nó không nhanh như vậy, nhưng tôi cũng không mong đợi nó sẽ như vậy.


Tham gia lần thứ hai

Bạn nói điều này là chậm hơn. Nó cũng ít đọc hơn từ quan điểm mã. Đưa ra các kết quả thỏa đáng như nhau giữa 1 và 2, tôi muốn nói là 1. Ít nhất thì rõ ràng bạn đang cố gắng làm gì theo cách đó. Các truy vấn con thường không nhanh lắm (mặc dù thường không thể tránh khỏi), đặc biệt trong trường hợp này, bạn sẽ tham gia thêm vào mỗi mục, điều này chắc chắn phải làm phức tạp kế hoạch thực hiện.

Một nhận xét, tôi thấy rằng bạn đã sử dụng cú pháp nối ANSI-89 cũ. Tốt nhất nên tránh điều đó, hiệu suất sẽ giống hoặc tốt hơn với cú pháp nối hiện đại hơn và chúng ít mơ hồ hơn hoặc dễ đọc hơn, khó mắc lỗi hơn.

FROM (FirstTable AS A INNER JOIN 
  (select top 1 B1.XTStamp, A1.RecTStamp 
   from SecondTable as B1
   inner join FirstTable as A1
     on B1.XTStamp <= A1.RecTStamp
   order by B1.XTStamp DESC) AS AbyB1 --MAX (time points before)

Đặt tên

Tôi nghĩ rằng cách mà những thứ của bạn được đặt tên là không có ích gì cả, và khó hiểu nhất là khó hiểu. A, B, A1, B1vv như bí danh bảng tôi nghĩ có thể tốt hơn. Ngoài ra, tôi nghĩ rằng tên trường không tốt lắm, nhưng tôi nhận ra bạn có thể không kiểm soát được việc này. Tôi sẽ chỉ nhanh chóng trích dẫn Bộ luật không mã về chủ đề đặt tên, và để nó ở đó ...

“Lời công kích!” Đã trả lời nữ tu. Verb động từ danh từ khám phá của bạn!


Truy vấn "bước tiếp theo"

Tôi không thể hiểu ý nghĩa của nó như thế nào khi nó được viết, tôi phải đưa nó đến một trình soạn thảo văn bản và thực hiện một số thay đổi về phong cách để làm cho nó dễ đọc hơn. Tôi biết trình soạn thảo SQL của Access vượt quá khó khăn, vì vậy tôi thường viết các truy vấn của mình trong một trình soạn thảo tốt như Notepad ++ hoặc Sublime Text. Một số thay đổi về phong cách mà tôi đã áp dụng để làm cho nó dễ đọc hơn:

  • 4 dấu cách thụt lề thay vì 2 dấu cách
  • Không gian xung quanh toán tử toán học và so sánh
  • Đặt niềng răng và thụt tự nhiên hơn (Tôi đã sử dụng niềng răng kiểu Java, nhưng cũng có thể là kiểu chữ C, theo sở thích của bạn)

Vì vậy, hóa ra đây là một truy vấn rất phức tạp. Để hiểu rõ về nó, tôi phải bắt đầu từ truy vấn trong cùng, IDtập dữ liệu của bạn , mà tôi hiểu là giống như Tham gia đầu tiên của bạn. Nó trả về ID và dấu thời gian của các thiết bị trong đó dấu thời gian trước / sau là gần nhất, trong tập hợp con của thiết bị mà bạn quan tâm. Vì vậy, thay vì IDtại sao không gọi nó ClosestTimestampID.

Tham Detgia của bạn chỉ được sử dụng một lần:

nhập mô tả hình ảnh ở đây

Thời gian còn lại, nó chỉ tham gia các giá trị bạn đã có ClosestTimestampID. Vì vậy, thay vào đó chúng ta sẽ có thể làm điều này:

    ) AS ClosestTimestampID
    INNER JOIN SecondTable AS TL1 
        ON ClosestTimestampID.BeforeXTStamp = TL1.XTStamp) 
    INNER JOIN SecondTable AS TL2 
        ON ClosestTimestampID.AfterXTStamp = TL2.XTStamp
    WHERE ClosestTimestampID.XmitID IN (<limited subset S>)

Có thể không phải là một hiệu suất lớn, nhưng bất cứ điều gì chúng ta có thể làm để giúp trình tối ưu hóa Jet DB kém sẽ giúp!


Tôi không thể rung cảm giác rằng các tính toán / thuật toán cho BeforeWeightAfterWeightmà bạn sử dụng để nội suy có thể được thực hiện tốt hơn, nhưng tiếc là tôi không phải là rất tốt với những người.

Một đề xuất để tránh sự cố (mặc dù không lý tưởng tùy thuộc vào ứng dụng của bạn) sẽ là chia các truy vấn con lồng nhau của bạn thành các bảng của riêng chúng và cập nhật chúng khi cần. Tôi không chắc chắn tần suất bạn cần làm mới dữ liệu nguồn của mình, nhưng nếu không quá thường xuyên, bạn có thể nghĩ đến việc viết một số mã VBA để lên lịch cập nhật các bảng và bảng dẫn xuất và chỉ để lại truy vấn ngoài cùng của bạn từ các bảng thay vì nguồn ban đầu. Chỉ là một suy nghĩ, như tôi đã nói không lý tưởng nhưng được cung cấp công cụ bạn có thể không có lựa chọn.


Mọi thứ cùng nhau:

SELECT
    InGPS.XmitID,
    StrDateIso8601Msec(InGPS.RecTStamp) AS RecTStamp_ms,
       -- StrDateIso8601MSec is a VBA function returning a TEXT string in yyyy-mm-dd hh:nn:ss.lll format
    InGPS.ReceivID,
    RD.Receiver_Location_Description,
    RD.Lat AS Receiver_Lat,
    RD.Lon AS Receiver_Lon,
    InGPS.Before_Lat * InGPS.BeforeWeight + InGPS.After_Lat * InGPS.AfterWeight AS Xmit_Lat,
    InGPS.Before_Lon * InGPS.BeforeWeight + InGPS.After_Lon * InGPS.AfterWeight AS Xmit_Lon,
    InGPS.RecTStamp AS RecTStamp_basic
FROM (
    SELECT 
        ClosestTimestampID.RecTStamp,
        ClosestTimestampID.XmitID,
        ClosestTimestampID.ReceivID,
        ClosestTimestampID.BeforeXTStamp, 
        TL1.Latitude AS Before_Lat, 
        TL1.Longitude AS Before_Lon,
        (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) 
            / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) AS BeforeWeight,
        ClosestTimestampID.AfterXTStamp, 
        TL2.Latitude AS After_Lat, 
        TL2.Longitude AS After_Lon,
        (     (ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) 
            / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS AfterWeight
        FROM (((
            SELECT 
                A.RecTStamp, 
                A.ReceivID, 
                A.XmitID,
                MAX(SWITCH(B.XTStamp <= A.RecTStamp, B.XTStamp, Null)) AS BeforeXTStamp,
                MIN(SWITCH(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)
            WHERE A.XmitID IN (<limited subset S>)
            GROUP BY A.RecTStamp, ReceivID, XmitID
        ) AS ClosestTimestampID
        INNER JOIN FirstTable AS Det 
            ON (Det.XmitID = ClosestTimestampID.XmitID) 
            AND (Det.ReceivID = ClosestTimestampID.ReceivID) 
            AND (Det.RecTStamp = ClosestTimestampID.RecTStamp)) 
        INNER JOIN SecondTable AS TL1 
            ON ClosestTimestampID.BeforeXTStamp = TL1.XTStamp) 
        INNER JOIN SecondTable AS TL2 
            ON ClosestTimestampID.AfterXTStamp = TL2.XTStamp
        WHERE Det.XmitID IN (<limited subset S>)
    ) AS InGPS
INNER JOIN ReceiverDetails AS RD 
    ON (InGPS.ReceivID = RD.ReceivID) 
    AND (InGPS.RecTStamp BETWEEN <valid parameters from another table>)
ORDER BY StrDateIso8601Msec(InGPS.RecTStamp), InGPS.ReceivID;

5
  • Đã thêm thuộc tính và điều kiện lọc.
  • Bất kỳ hình thức tham gia chéo nào cũng được loại bỏ bằng cách sử dụng các truy vấn lồng nhau tối thiểu và tối đa. Đây là mức tăng hiệu suất lớn nhất.
  • Các giá trị sườn tối thiểu và tối đa được trả về bởi truy vấn lồng nhau nhất bên trong là các giá trị khóa chính (quét) được sử dụng để truy xuất các thuộc tính sườn bổ sung (lat và lon) bằng cách tìm kiếm các phép tính cuối cùng (truy cập có tương đương áp dụng).
  • Các thuộc tính bảng chính được lấy và lọc trong truy vấn trong cùng và sẽ giúp thực hiện.
  • Không cần định dạng (StrDateIso8601Msec) giá trị thời gian để sắp xếp. Sử dụng giá trị datetime từ bảng là tương đương.

Các kế hoạch thực thi máy chủ SQL (vì Access không thể hiển thị điều này)
Nếu không có thứ tự cuối cùng bởi vì nó đắt tiền:
Quét chỉ mục cụm [Bộ thu nhận dữ liệu]. [PK_ReceiverDetails]
Tìm kiếm chỉ mục cụm 16% [FirstTable]. [PK_FirstTable] Chi phí
chỉ mục 19% Tìm kiếm [SecondTable]. [PK_SecondTable] Chi phí 16%
Chỉ mục cụm Tìm kiếm [Thứ hai]. [PK_SecondTable] Chi phí 16%
Chỉ mục cụm tìm kiếm [Thứ hai]. [PK_SecondTable] [TL2] Chi phí 16%
Chỉ mục được phân cụm Tìm kiếm [Thứ hai] [TL1] Chi phí 16%

với đơn hàng cuối cùng bằng cách:
Sắp xếp chi phí 36%
Quét chỉ mục theo cụm [Nhận dữ liệu]. [PK_ReceiverDetails] Chi phí
tìm kiếm chỉ mục cụm 10% [FirstTable]. [PK_FirstTable] Chi phí 12%
Clustered Index tìm kiếm [SecondTable]. [PK_SecondTable] Chi phí 10%
Clustered Index tìm kiếm [SecondTable]. [PK_SecondTable] Chi phí 10%
Clustered Index Seek [SecondTable]. [PK_SecondTable] [TL2] Chi phí 10%
Clustered Index Seek [SecondTable]. [ PK_SecondTable] [TL1]

Mã 10% :

select
     ClosestTimestampID.XmitID
    --,StrDateIso8601Msec(InGPS.RecTStamp) AS RecTStamp_ms
    ,ClosestTimestampID.ReceivID
    ,ClosestTimestampID.Receiver_Location_Description
    ,ClosestTimestampID.Lat
    ,ClosestTimestampID.Lon
,[TL1].[Latitude] * (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) + [TL2].[Latitude] * ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS Xmit_Lat
,[TL1].[Longitude] * (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) + [TL2].[Longitude] * ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS Xmit_Lon
    ,ClosestTimestampID.RecTStamp as RecTStamp_basic
from (
        (
            (
                select
                     FirstTable.RecTStamp
                    ,FirstTable.ReceivID
                    ,FirstTable.XmitID
                    ,ReceiverDetails.Receiver_Location_Description
                    ,ReceiverDetails.Lat
                    ,ReceiverDetails.Lon
                    ,(
                        select max(XTStamp) as val
                        from SecondTable
                        where XTStamp <= FirstTable.RecTStamp
                     ) as BeforeXTStamp
                    ,(
                        select min(XTStamp) as val
                        from SecondTable
                        where XTStamp > FirstTable.RecTStamp
                     ) as AfterXTStamp
                from FirstTable
                inner join ReceiverDetails
                on ReceiverDetails.ReceivID = FirstTable.ReceivID
                where FirstTable.RecTStamp between #1/1/1990# and #1/1/2020#
                and FirstTable.XmitID in (100,110)
            ) as ClosestTimestampID
            inner join SecondTable as TL1
            on ClosestTimestampID.BeforeXTStamp = TL1.XTStamp
        )
        inner join SecondTable as TL2
        on ClosestTimestampID.AfterXTStamp = TL2.XTStamp
    )
order by ClosestTimestampID.RecTStamp, ClosestTimestampID.ReceivID;

Hiệu suất kiểm tra truy vấn của tôi đối với truy vấn có chứa tham gia chéo.

FirstTable được tải với 13 bản ghi và SecondTable với 1.000.000.
Các kế hoạch thực hiện cho truy vấn của tôi không thay đổi nhiều so với những gì đã được đăng.
Các kế hoạch thực hiện cho việc tham gia chéo:
Các vòng lặp lồng nhau Chi phí 81% khi sử dụng các INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp
vòng lặp lồng nhau giảm xuống 75% nếu sử dụng Quét chỉ mục CROSS JOIN SecondTable AS B' or ',SecondTable AS B
tổng hợp 8%
[SecondTable] [UK_ID] [B] 6%
Bảng Spool 5%
Một số Tìm kiếm chỉ mục được nhóm và tìm kiếm chỉ mục khác (tương tự như truy vấn của tôi như đã đăng) với Chi phí 0%.

Thời gian thực hiện là 0,007 và 8-9 giây cho truy vấn của tôi và CROSS THAM GIA.
Chi phí so sánh 0% và 100%.

Tôi đã tải FirstTable với 50.000 bản ghi và một bản ghi duy nhất vào ReceiverDetails cho một điều kiện tham gia và chạy truy vấn của tôi.
50.013 trở lại trong khoảng 0,9 đến 1,0 giây.

Tôi đã chạy truy vấn thứ hai với phép nối chéo và cho phép nó chạy trong khoảng 20 phút trước khi tôi giết nó.
Nếu truy vấn nối chéo được lọc để chỉ trả về 13 gốc, thời gian thực hiện lại là 8-9 giây.
Vị trí của điều kiện bộ lọc được chọn nhiều nhất bên trong, bên ngoài chọn nhiều nhất và cả hai. Không khác nhau.

Có một sự khác biệt giữa hai điều kiện tham gia này có lợi cho CROSS THAM GIA, lần đầu tiên sử dụng một vị ngữ, CROSS THAM GIA không:
INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp) CROSS JOIN SecondTable AS B


Chạy phần RecentTimestampID trên hệ thống của tôi sẽ trả về 152928 bản ghi ngay lập tức khi được gói gọn trong Đếm (*). MSAccess của tôi bị khóa khi trả lại các bản ghi thực tế ở giai đoạn đó - có thể các bảng tạm thời từ phương thức khác đang ăn cắp tất cả các loại bộ nhớ. Tôi nghĩ rằng truy vấn cuối cùng mà tôi tạo ra từ phương pháp của bạn sẽ rất giống với những gì tôi đang sử dụng. Mà tôi cho là một điều tốt :)
mpag

1
Trong nhận xét ban đầu của bạn, bạn chỉ ra rằng bạn đã lấy lại một số hồ sơ ngay lập tức. Điều này rất quan trọng liên quan đến cách thức hoạt động của truy cập, đưa ra chiến lược Access và đặt kỳ vọng cho thời gian thực hiện. Nó được gọi là thực hiện hoãn lại. (Nó bị sập khi bạn nhấn bản ghi cuối cùng.) Số lượng bản ghi trả về giới hạn trên dự kiến ​​sẽ có trong truy vấn cuối cùng là gì?
byrdzeye

Tôi tin 152928
mpag

Bản chất của các giá trị DateTime trong cả hai bảng là các bản ghi mới được thêm vào. Chúng có phải là tem thời gian hiện tại hoặc giá trị gần đây hoặc hoàn toàn ngẫu nhiên?
byrdzeye

bảng đầu tiên có tem DateTime là 2013 trở lên. Bảng thứ hai có tem DateTime trong vòng một vài tháng vào giữa năm 2015. Nếu các giá trị mới được thêm vào, chúng có thể sẽ (nhưng không được bảo đảm) sau bộ hiện tại. Các giá trị mới có thể được thêm vào một trong hai bảng.
mpag

2

Thêm câu trả lời thứ hai, không tốt hơn câu trả lời thứ nhất nhưng không thay đổi bất kỳ yêu cầu nào được đưa ra, có một số cách để đánh bại Access vào trình và có vẻ linh hoạt. 'Vật chất hóa' các biến chứng một chút tại hiệu ứng thời gian bằng cách sử dụng 'kích hoạt'. Các bảng truy cập không có các kích hoạt để chặn và tiêm các quy trình thô.

--*** Create a table for flank values.
    create table Flank (
         RecTStamp      datetime not null
        ,BeforeXTStamp  datetime null
        ,AfterXTStamp   datetime null
        ,constraint PK_Flank primary key clustered ( RecTStamp asc )
        )

--*** Create a FlankUpdateLoop sub. (create what is missing)
    -- loop until rowcount < 5000 or rowcount = 0
    -- a 5K limit appears to be manageable for Access, especially for the initial population.
    insert into Flank (
         RecTStamp
        ,BeforeXTStamp
        ,AfterXTStamp
        )
    select top 5000 FirstTable.RecTStamp
        ,(
            select max(XTStamp) as val
            from SecondTable
            where XTStamp <= FirstTable.RecTStamp
            ) as BeforeXTStamp
        ,(
            select min(XTStamp) as val
            from SecondTable
            where XTStamp > FirstTable.RecTStamp
            ) as AfterXTStamp
    from FirstTable
    left join Flank
        on FirstTable.RecTStamp = Flank.RecTStamp
    where Flank.RecTStamp is null;

--*** For FirstTable Adds, Changes or Deletes:
    delete from Flank where Flank.RecTStamp = CRUD_RecTStamp
    execute FlankUpdateLoop --See above. This will handle Adds, Changes or Deletes.

--*** For SecondTable Adds, Changes or Deletes:
    --delete from Flank where the old value is immediately before and after the new flank value.
    --They may or may not get be assigned a new value. Let FlankUpdate figure it out.

    --execute deletes for both beforextstamp and afterxtstamp
    --then update flank

    delete *
    from flank
    where beforextstamp between (
                    select min(beforextstamp)
                    from flank
                    where beforextstamp >= '3/16/2009 10:00:46 AM'
                    ) and (
                    select max(beforextstamp)
                    from flank
                    where beforextstamp <= '3/16/2009 10:00:46 AM'
                    );

    delete *
    from flank
    where afterxtstamp between (
                    select min(afterxtstamp)
                    from flank
                    where afterxtstamp >= '3/16/2009 10:00:46 AM'
                    ) and (
                    select max(afterxtstamp)
                    from flank
                    where afterxtstamp <= '3/16/2009 10:00:46 AM'
                    );

    execute FlankUpdateLoop

--*** Final Report Query***--
    --Should execute without issues including 'deferred execution' problem.
    --Add filters as needed.
    select FirstTable.XmitID
        ,FirstTable.ReceivID
        ,ReceiverDetails.Lat
        ,ReceiverDetails.Lon
        ,BeforeTable.Latitude * (1 - ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp))) + AfterTable.Latitude * ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp)) as Xmit_Lat
        ,BeforeTable.Longitude * (1 - ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp))) + AfterTable.Longitude * ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp)) as Xmit_Lon
        ,FirstTable.RecTStamp as RecTStamp_basic
    from (((
        FirstTable
    inner join Flank on FirstTable.RecTStamp = Flank.RecTStamp)
    inner join SecondTable as BeforeTable on Flank.BeforeXTStamp = BeforeTable.XTStamp)
    inner join SecondTable as AfterTable on Flank.AfterXTStamp = AfterTable.XTStamp)
    inner join ReceiverDetails on FirstTable.ReceivID = ReceiverDetails.ReceivID
    order by FirstTable.RecTStamp;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.