Access(Jet)SQL:TableB中的DateTime戳位于TableA中每个DateTime戳的侧面


21

第一句话

您可以放心地忽略以下(和包括)JOIN的部分:如果仅想破解代码,则从Start入手。的背景结果只是作为背景。如果您想查看最初的代码,请查看2015年10月6日之前的编辑历史记录。


目的

最终,我想根据表中可用GPS数据的DateTime时间戳(直接在表中观察数据的侧面)来计算发射机(XXmit)的内插GPS坐标。SecondTableFirstTable

我的近期目标实现的最终目标是要弄清楚如何最好地加入FirstTableSecondTable得到这些侧翼的时间点。以后,我可以使用这些信息,并假设沿着等矩形坐标系进行线性拟合,就可以计算GPS中间坐标(用奇特的话说,我不在乎地球是这个范围的球体)。


问题

  1. 有没有更有效的方法来生成最接近的前后时间戳?
    • 由我自己解决,方法是仅抓住“之后”,然后仅获取与“之后”相关的“之前”。
  2. 是否有一种不涉及(A<>B OR A=B)结构的更直观的方法。
    • Byrdzeye提供了基本的替代方法,但是我的“现实世界”经验与他的所有4种执行相同策略的加入策略并不一致。但是,他对替代连接样式的解决也深表感谢。
  3. 您可能还有其他想法,窍门和建议。
    • 到目前为止,byrdzeyePhrancis在这方面都非常有帮助。我发现Phrancis的建议非常出色,并在关键阶段提供了帮助,因此在这里我将给予他优势。

我仍然很感激我在问题3方面能获得的任何其他帮助。项目 符号反映了我认为对个人问题最有帮助的人。


表定义

半视觉表示

第一表

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

第二表

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
(外部链接)


加入:开始

我目前的“内心”加入策略

首先创建一个具有列顺序和(RecTStamp, ReceivID, XmitID)所有索引/已排序复合主键的FirstTable_rekeyed 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(...)”中,以下操作将在一两秒钟内完成

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)))

(慢)

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; 

背景

我有一个将近100万个条目的遥测表(别名为A),它具有基于DateTime图章,发送器ID和记录设备ID 的复合主键。由于无法控制的情况,我的SQL语言是Microsoft Access中的标准Jet DB(用户将使用2007年及更高版本)。由于发送器ID,这些条目中只有大约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 JOININNER JOIN在上面显示出来的代码,以使返回的行的数量或身份没有影响。我似乎也不能放弃ON子句或说ON (1=1)。仅使用逗号联接(而不是INNERor LEFT OUTER JOIN)会导致Count(select * from A) * Count(select * from B)此查询返回的行,而不是每个表A仅返回一行,因为(A <> B OR A = B)显式JOIN返回。这显然不合适。FIRST给定复合主键类型,似乎无法使用。

第二种JOIN风格虽然可以说更清晰易读,但其速度较慢。这可能是因为JOIN在较大的表上以及CROSS JOIN在两个选项中都找到两个时,需要另外两个in。

另外:IIFMIN/ 替换子句MAX似乎返回相同数量的条目。
MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
适用于“ Before”(MAX)时间戳,但不适用于“ After”(MIN),如下所示:
MIN(-(B.XTStamp>A.RecTStamp)*B.XTStamp)
因为条件的最小值始终为0 FALSE。此0小于任何后纪DOUBLE(该DateTime域是Access中的子集,该计算将其转换为该域)。的IIFMIN/ MAX方法的通过零提出了AfterXTStamp值工作,因为分割(候补FALSE)产生空值,其中,集合函数MIN和MAX跳过。

下一步

进一步讲,我希望在第二个表中找到直接位于第一个表中时间戳旁边的时间戳,并根据到这些点的时间距离对第二个表中的数据值进行线性插值(即,如果从第一个表是“ before”和“ after”之间距离的25%,我希望计算值的25%来自与“ after”点关联的第二个表值数据,而75%来自“ before” )。使用修订后的联接类型作为内胆的一部分,并在以下建议的答案之后产生了...

    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分钟。


参考1:MS Access可以处理毫秒时间值-确实以及随附的源文件[08080011.txt]

Answers:


10

首先,我必须赞扬您使用Access DB进行此类操作的勇气,以我的经验,这样做很难像SQL一样执行任何操作。无论如何,请继续进行审查。


第一次加入

您的IIF字段选择可能会受益于使用Switch语句。有时似乎是一种情况,尤其是在使用SQL的情况下,仅在a主体中进行简单比较时,a SWITCH(通常CASE在典型的SQL中是众所周知的)就非常快SELECT。您所用的语法几乎是相同的,尽管可以扩展一个开关以在一个字段中覆盖很大的比较块。需要考虑的事情。

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

在较大的语句中,开关还可以帮助提高可读性。在上下文中:

  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

至于联接本身,(A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)鉴于您要尝试做的事情,我认为这与您将要取得的成就差不多。速度不是那么快,但是我也不会这么想。


第二次加入

你说慢一点 从代码的角度来看,它的可读性也较差。给定1到2之间同样令人满意的结果集,我会说1。至少,很显然,您正在尝试这样做。子查询通常不是很快(尽管通常是不可避免的),特别是在这种情况下,您要在每个子查询中添加额外的联接,这肯定会使执行计划复杂化。

一句话,我看到您使用了旧的ANSI-89连接语法。最好避免这种情况,使用更现代的连接语法,其性能将相同或更好,并且它们不那么模棱两可或更易于阅读,更容易出错。

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)

命名事物

我认为,您的事物的命名方式充其量是无济于事的,而最糟糕的是晦涩的。A, B, A1, B1等作为表别名,我认为可能会更好。另外,我认为字段名称不是很好,但是我知道您可能对此没有控制权。我将快速引用有关命名事物的主题的无代码代码,然后将其保留...

“煽动!”女祭司回答。“动词你的专有名词!”


“下一步”查询

我对它的编写方式不太了解,我不得不将其带到文本编辑器中并进行一些样式更改以使其更具可读性。我知道Access的SQL编辑器非常笨拙,因此我通常使用诸如Notepad ++或Sublime Text这样的优质编辑器来编写查询。我进行了一些样式更改,以使其更具可读性:

  • 缩进4个空格而不是2个空格
  • 数学运算符和比较运算符周围的空间
  • 大括号和缩进的放置更自然(我使用Java样式的大括号,但也可以选择C样式的大括号)

事实证明,这确实是一个非常复杂的查询。为了理解这一点,我必须从最里面的查询开始,即您的ID数据集,据我了解,该数据集与您的“首次联接”相同。它会在您感兴趣的设备子集中返回前/后时间戳最接近的设备的ID和时间戳。因此,ID为什么不调用它呢ClosestTimestampID

您的Det联接仅使用一次:

在此处输入图片说明

在其余时间中,它仅合并您已经拥有的值ClosestTimestampID。因此,相反,我们应该能够做到这一点:

    ) 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>)

也许不是很大的性能提升,但是我们可以采取任何措施来帮助较差的Jet DB优化器都能帮助您!


我不能动摇的感觉是,计算/算法BeforeWeightAfterWeight您使用插值可以做得更好,但不幸的是,我不是很好的那些。

一种避免崩溃的建议(尽管根据您的应用程序这不是理想的选择)是将嵌套的子查询分解成自己的表并在需要时进行更新。我不确定您需要多长时间刷新一次源数据,但是如果不是那么频繁,您可能会考虑编写一些VBA代码来安排表和派生表的更新,而只保留最外面的查询从那些表而不是原始来源。就像我说的那样,不理想,但考虑到工具,您可能别无选择。


一切都在一起:

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
  • 添加了其他属性和过滤条件。
  • 通过使用最小和最大嵌套查询,可以消除任何形式的交叉连接。这是最大的性能提升。
  • 最内层嵌套查询返回的最小和最大侧面值是主键值(扫描),这些键用于通过查找最终计算来检索其他侧面属性(纬度和经度)(访问确实具有适用的等效项)。
  • 在最里面的查询中检索和过滤主表属性,这应该有助于提高性能。
  • 无需格式化(StrDateIso8601Msec)时间值即可进行排序。使用表中的datetime值是等效的。

SQL Server执行计划(因为Access无法显示此
命令)因为没有最终命令,原因是它的价格昂贵:
聚集索引扫描[ReceiverDetails]。[PK_ReceiverDetails]花费16%
聚集索引寻求[FirstTable]。[PK_FirstTable]花费19%
聚集索引寻求[SecondTable]。[PK_SecondTable]成本16%
聚集索引寻求[SecondTable]。[PK_SecondTable]成本16% 聚簇索引寻求[SecondTable]。[PK_SecondTable]成本10% 聚集索引寻求[SecondTable]。[PK_SecondTable]成本10% 聚集索引寻求[SecondTable]。[PK_SecondTable] [TL2]成本10% 聚集索引寻求[SecondTable]。[PK_SecondTable] [TL1]成本10%
聚簇索引寻求[SecondTable]。[PK_SecondTable] [TL2]成本16%
聚簇索引寻求[SecondTable]。[PK_SecondTable] [TL1]成本16%

,最后排序依据:
排序成本36%
聚集索引扫描[ReceiverDetails]。[PK_ReceiverDetails]成本10%
聚集索引寻求[FirstTable]。[PK_FirstTable]成本12%





代码:

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;

针对包含交叉联接的查询对我的查询进行性能测试。

FirstTable加载了13条记录,SecondTable加载了1,000,000条记录。
我的查询的执行计划与已发布的内容相比没有太大变化。
交叉连接的执行计划: 如果使用 流聚合8% 索引扫描[SecondTable] [UK_ID] [B] 6%,则
使用INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp
嵌套循环的嵌套循环成本将从81%下降到75 %。CROSS JOIN SecondTable AS B' or ',SecondTable AS B


表假脱机5%,%。
其他嵌套索引索引和索引查找的(类似于我发布的查询),费用为0%。

我的查询和CROSS JOIN的执行时间是.007和8-9秒。
成本比较0%和100%。

我将具有50,000条记录和一条记录的FirstTable加载到ReceiverDetails中以获取连接条件,然后运行查询。
50013在0.9至1.0秒之间返回。

我使用交叉联接运行了第二个查询,并允许它运行约20分钟,然后杀死了它。
如果交叉联接查询被过滤为仅返回原始的13,则执行时间再次为8-9秒。
过滤条件的放置在最内部选择,最外部选择和两者都进行。没有不同。

这两个连接条件之间的区别在于对CROSS JOIN的支持,第一个使用谓词,而CROSS JOIN则不:
INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp) CROSS JOIN SecondTable AS B


当封装在Count(*)中时,在我的系统上运行ClosestTimestampID部分会立即返回152928条记录。在那个阶段返回实际记录时,我的MSAccess被锁定了-也许其他方法的临时表占用了各种内存。我认为我根据您的方法得出的最终查询与我当前使用的查询非常相似。我认为这是一件好事:)
mpag

1
在您最初的评论中,您表示您立即获得了一些记录。对于访问的工作方式,提出访问策略以及设定执行时间的期望,这一点很重要。它称为延迟执行。(当您击中最后一条记录时,它崩溃了。)预期在最终查询中的返回上限记录数是多少?
byrdzeye

我相信152928
mpag

添加新记录时,两个表中DateTime值的本质是什么。它们是当前时间戳或最新值,还是完全随机?
byrdzeye

第一个表具有2013年或更晚的DateTime戳记。第二个表中的DateTime戳记位于2015年中期的几个月内。如果添加了新值,则它们很可能会(但不能保证)在现有集之后。可以将新值添加到任一表中。
mpag 2015年

2

添加第二个答案并不比第一个答案好,但在不更改所提出的任何要求的情况下,有几种方法可以使Access屈服于提交并显得活泼。使用“触发器”一次有效地“复杂化”并发症。访问表没有触发器,因此可以拦截并注入原始进程。

--*** 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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.