tags:

views:

133

answers:

6

Hello,

Let's say you have a table with a integer primary key named 'table_id'

Is it possible in a single query to extract the row with a specific id AND the X row before it AND the X row after it ?

For exemple, if your ids are (1,2,8,12,16,120,250,354), X is 2 and the id you have is 16, the select should return rows with ids 8,12,16,120,250

i know how to do it with several queries, I'm wondering about doing that in a single pass (sub queries, union and all are fine).

Thanks for any help

+4  A: 

try this:

select table_id from table where id > 16 order by table_id desc limit 2
union all
select table_id from table where id <= 16 order by table_id asc limit 3;
pstanton
Shouldn't that be "select top 3" the second time?
Mark Byers
note mysql understands "limit", not "top"
ʞɔıu
The question is tagged "mysql".
Jordan Ryan Moore
You can't use ORDER BY and UNION ALL that way. And you can't use top in MySQL.
Guffa
i admit, i don't have mysql to test. sorry if this doesn't quite work...
pstanton
+1  A: 

Using MySQL's LIMIT syntax and UNION:

SELECT table_id FROM table WHERE id > 16 ORDER BY table_id ASC LIMIT 2
UNION
SELECT table_id FROM table WHERE id <= 16 ORDER BY table_id DESC LIMIT 3
Tatu Ulmanen
should use "union all", not "union"
ʞɔıu
You can't use ORDER BY and UNION (or UNION ALL) that way.
Guffa
Error 1221: Incorrect usage of UNION and ORDER BY
Guffa
+3  A: 

You can make a union between the items before and the item and the items after, but you have to make them subqueries to order them:

select * from (
  select * from thetable where table_id >= 16 order by table_id limit 3
) x
union all
select * from (
  select * from thetable where table_id < 16 order by table_id desc limit 2
) y
order by table_id
Guffa
A: 

This works in SQL Server using a derived query:

DECLARE @X int, @target_id int

SET @X = 2
SET @target_id = 8

SELECT [table_id]
FROM
(SELECT TOP ((@X * 2) + 1) [table_id]
      ,ABS(@target_id - [table_id]) AS [Diff]
  FROM [tblItems]
  ORDER BY [Diff] ASC) T
ORDER BY table_id ASC
GO
Ben555
This comes nowhere near working as required. It seems you wrote this with the assumption that the numbers would be contiguous; and the requirement quite explicitly excludes that. Try plugging in the sample data provided.
Craig Young
A: 

Here's how I would do it, it's simple and it works for signed/unsigned numbers:

-- Finds the closest 5 productID's to 200
SELECT     productID, abs(productID - 200) as diff
FROM       products
WHERE      productID != 200
ORDER BY   diff, productID
LIMIT      5

If you're using unsigned numbers, then you'll need to cast the column first:

-- Finds the closest 5 productID's to 200 (unsigned)
SELECT     productID, abs(CAST(productID as SIGNED) - 200) as diff
FROM       products
WHERE      productID != 200
ORDER BY   diff, productID
LIMIT      5
Joe Zack
Arg! I totally misread the question.
Joe Zack
A: 

Try the following: Unfortunately, I don't have MySQL, but the MS SQL Server version works.

SELECT  /*TOP (2*2 +1)    --MS SQL Server syntax */
        *
FROM    IDTable
WHERE   IDCol >= (
        SELECT  MIN(IDCol)
        FROM    (
                SELECT  /*TOP 2   --MS SQL Server syntax */
                        IDCol
                FROM    IDTable
                WHERE   IDCol < 16
                ORDER BY IDCol DESC limit 2
                ) t
        )
ORDER BY IDCol limit (2*2 +1)
Craig Young