What is the SQL for 'next' and 'previous' in a table?

后端 未结 7 858
抹茶落季
抹茶落季 2020-12-05 12:21

I have a table of items, each of which has a date associated with it. If I have the date associated with one item, how do I query the database with SQL to get the \'previous

相关标签:
7条回答
  • 2020-12-05 12:25

    My own attempt at the set solution, based on TheSoftwareJedi.

    First question:

    select date from test where date = 8
    union all
    select max(date) from test where date < 8
    union all
    select min(date) from test where date > 8
    order by date;
    

    Second question:

    While debugging this, I used the data set:

    (key:date) 1:1,2:3,3:8,4:8,5:19,10:19,11:67,15:45,16:8,17:3,18:1
    

    to give this result:

    select * from test2 where date = 8
    union all
    select * from (select * from test2
                       where date = (select max(date) from test2 
                                         where date < 8)) 
        where key = (select max(key) from test2 
                        where date = (select max(date) from test2 
                                          where date < 8))
    union all
    select * from (select * from test2
                       where date = (select min(date) from test2 
                                         where date > 8)) 
        where key = (select min(key) from test2 
                        where date = (select min(date) from test2 
                                          where date > 8))
    order by date,key;
    

    In both cases the final order by clause is strictly speaking optional.

    0 讨论(0)
  • 2020-12-05 12:29

    Self-joins.

    For the table:

    /*
    CREATE TABLE [dbo].[stackoverflow_203302](
        [val] [int] NOT NULL
    ) ON [PRIMARY]
    */
    

    With parameter @val

    SELECT cur.val, MAX(prv.val) AS prv_val, MIN(nxt.val) AS nxt_val
    FROM stackoverflow_203302 AS cur
    LEFT JOIN stackoverflow_203302 AS prv
        ON cur.val > prv.val
    LEFT JOIN stackoverflow_203302 AS nxt
        ON cur.val < nxt.val
    WHERE cur.val = @val
    GROUP BY cur.val
    

    You could make this a stored procedure with output parameters or just join this as a correlated subquery to the data you are pulling.

    Without the parameter, for your data the result would be:

    val         prv_val     nxt_val
    ----------- ----------- -----------
    1           NULL        3
    3           1           8
    8           3           19
    19          8           45
    45          19          67
    67          45          NULL
    

    For the modified example, you use this as a correlated subquery:

    /*
    CREATE TABLE [dbo].[stackoverflow_203302](
        [ky] [int] NOT NULL,
        [val] [int] NOT NULL,
        CONSTRAINT [PK_stackoverflow_203302] PRIMARY KEY CLUSTERED (
            [ky] ASC
        )
    )
    */
    
    SELECT cur.ky AS cur_ky
            ,cur.val AS cur_val
            ,prv.ky AS prv_ky
            ,prv.val AS prv_val
            ,nxt.ky AS nxt_ky
            ,nxt.val as nxt_val
    FROM (
        SELECT cur.ky, MAX(prv.ky) AS prv_ky, MIN(nxt.ky) AS nxt_ky
        FROM stackoverflow_203302 AS cur
        LEFT JOIN stackoverflow_203302 AS prv
            ON cur.ky > prv.ky
        LEFT JOIN stackoverflow_203302 AS nxt
            ON cur.ky < nxt.ky
        GROUP BY cur.ky
    ) AS ordering
    INNER JOIN stackoverflow_203302 as cur
        ON cur.ky = ordering.ky
    LEFT JOIN stackoverflow_203302 as prv
        ON prv.ky = ordering.prv_ky
    LEFT JOIN stackoverflow_203302 as nxt
        ON nxt.ky = ordering.nxt_ky
    

    With the output as expected:

    cur_ky      cur_val     prv_ky      prv_val     nxt_ky      nxt_val
    ----------- ----------- ----------- ----------- ----------- -----------
    1           1           NULL        NULL        2           3
    2           3           1           1           3           8
    3           8           2           3           4           19
    4           19          3           8           5           67
    5           67          4           19          6           45
    6           45          5           67          NULL        NULL
    

    In SQL Server, I prefer to make the subquery a Common table Expression. This makes the code seem more linear, less nested and easier to follow if there are a lot of nestings (also, less repetition is required on some re-joins).

    0 讨论(0)
  • 2020-12-05 12:42

    Try this...

    SELECT TOP 3 * FROM YourTable
    WHERE Col >= (SELECT MAX(Col) FROM YourTable b WHERE Col < @Parameter)
    ORDER BY Col
    
    0 讨论(0)
  • 2020-12-05 12:46

    Select max(element) From Data Where Element < 8

    Union

    Select min(element) From Data Where Element > 8

    But generally it is more usefull to think of sql for set oriented operations rather than iterative operation.

    0 讨论(0)
  • 2020-12-05 12:46

    If your RDBMS supports LAG and LEAD, this is straightforward (Oracle, PostgreSQL, SQL Server 2012)

    These allow to choose the row either side of any given row in a single query

    0 讨论(0)
  • 2020-12-05 12:47
    SELECT 'next' AS direction, MIN(date_field) AS date_key
    FROM table_name
      WHERE date_field > current_date
    GROUP BY 1 -- necessity for group by varies from DBMS to DBMS in this context
    UNION
    SELECT 'prev' AS direction, MAX(date_field) AS date_key
      FROM table_name
      WHERE date_field < current_date
    GROUP BY 1
    ORDER BY 1 DESC;
    

    Produces:

    direction  date_key
    ---------  --------
    prev              3
    next             19
    
    0 讨论(0)
提交回复
热议问题