Justin Zimmerman
Justin Zimmerman

Reputation: 35

Find Previous and next value in access using SQL

I am using a Microsoft Access 2010 database to import values from one table and append them to a summary table.

One of the issues I am having is finding the previous and next value from the select statement.

This would look as follows.

JOINT          JOINT AHEAD             JOINT BEHIND
100103           200203
200203           300303                  100103
300303                                   200203

I would like to create this using a SQL code

Upvotes: 2

Views: 2094

Answers (2)

HansUp
HansUp

Reputation: 97100

Be cautious when considering correlated subqueries. They can be very slow. And if you build a query which includes two correlated subqueries, you will magnify the problem.

If your source table contains a smallish number of rows (say a few dozen), the slowness may not be an issue. However, if the table includes a thousand rows you will most certainly notice it. And if your JOINT field is not indexed, the performance could be painfully slow.

If you will be running your query from within an Access session, you can use domain functions (DMin and DMax) instead of correlated subqueries. Domain functions are often criticized as slow. However, in this situation they can be dramatically faster than correlated subqueries.

Correction: You don't need to run your query from within an Access session for it to be able to use the DMin() and DMax() functions. I attached a VBScript example which opens an ADO Recordset based on my qryDomainFunctions. It works without error and correctly reports RecordCount: 1000

I created a table, joints, with a long integer field joint as primary key and added 1000 rows. Then I created these 2 queries:

qryCorrelatedSubqueries:

SELECT
    a.joint,
    (SELECT TOP 1 joint 
    FROM joints b 
    WHERE b.joint>a.joint 
    ORDER BY joint) AS Ahead,
    (SELECT TOP 1 joint 
    FROM joints b 
    WHERE b.joint<a.joint 
    ORDER BY joint DESC) AS Behind
FROM joints AS a;

qryDomainFunctions:

SELECT
    j.joint,
    DMin("joint","joints","joint > " & [joint]) AS joint_ahead,
    DMax("joint","joints","joint < " & [joint]) AS joint_behind
FROM joints AS j;

Here is a transcript from the Immediate window where I compared the speed of those 2 queries, using the QueryDuration function below. That function returns duration in milliseconds.

? QueryDuration("qryDomainFunctions")
 0 

? QueryDuration("qryCorrelatedSubqueries")
 889

Note that both those queries benefit from the index on the joints field. When I dropped the index, compacted the db, and re-ran the tests I got these results:

? QueryDuration("qryDomainFunctions")
 16 

? QueryDuration("qryCorrelatedSubqueries")
 4570

This is the module with the code I used. QueryDuration is by no means the last word on performance measurement. However it's good enough to give us a rough idea of the relative speeds of those 2 queries.

Option Compare Database
Option Explicit

Private Declare Function apiGetTickCount Lib "kernel32" _
 Alias "GetTickCount" () As Long

Public Function QueryDuration(ByVal pQueryName As String) As Long
    Dim db As DAO.Database
    Dim lngStart As Long
    Dim lngDone As Long
    Dim rs As DAO.Recordset

    Set db = CurrentDb()
    lngStart = apiGetTickCount() ' milliseconds '
    Set rs = db.OpenRecordset(pQueryName, dbOpenSnapshot)
    If Not rs.EOF Then
        rs.MoveLast
    End If
    lngDone = apiGetTickCount()
    rs.Close
    Set rs = Nothing
    Set db = Nothing
    QueryDuration = lngDone - lngStart
End Function

DomainFunctionsQuery.vbs:

Option Explicit
Dim cn, rs
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
    "Data Source='database1.mdb'"
Set rs = CreateObject("ADODB.Recordset")
rs.CursorLocation = 3 ' adUseClient '
rs.Open "qryDomainFunctions", cn, 3 ' adOpenStatic = 3 '
WScript.Echo "RecordCount: " & rs.RecordCount
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing

Upvotes: 1

Fionnuala
Fionnuala

Reputation: 91356

How about:

SELECT a.JOINT, 
      (SELECT TOP 1 Joint 
       FROM Joint b 
       WHERE b.JOINT>a.JOINT 
       ORDER BY Joint) AS Ahead,
      (SELECT TOP 1 Joint 
       FROM Joint b 
       WHERE b.JOINT<a.JOINT 
       ORDER BY Joint DESC) AS Behind
FROM Joint AS a;

Upvotes: 1

Related Questions