views:

717

answers:

10

Hi,

I have an SQL Query that i'm running but I only want to select a specific row. For example lets say my query was:

Select * from Comments

Lets say this returns 10 rows, I only want to select the 8th record returned by this query. I know I can do:

Select Top 5 * from Comments

To get the top 5 records of that query but I only want to select a certain record, is there anything I can put into this query to do that (similar to top).

Thanks

jack

A: 

From the SELECT reference, use the LIMIT keyword:

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15
SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

Note: this is for MySQL, other SQL engines may have a different keyword.

Ben S
+5  A: 

This is a classic interview question.

In Ms SQL 2005+ you can use the ROW_NUMBER() keyword and have the Predicate ROW_NUMBER = n

USE AdventureWorks;
GO
WITH OrderedOrders AS
(
    SELECT SalesOrderID, OrderDate,
    ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
    FROM Sales.SalesOrderHeader 
)  

SELECT * 
FROM OrderedOrders 
WHERE RowNumber = 5;

In SQL2000 you could do something like

SELECT Top 1 *FROM
[tblApplications]
where [ApplicationID] In
(
    SELECT TOP 5 [ApplicationID]
    FROM [dbo].[tblApplications]
    order by applicationId Desc
)
John Nolan
Would you say that this is the correct way of doing things? (If there is such a thing) I'm seeing 3 different methods of achiving what I'm, after on this page but am wondering if any of the three offer any particular advantages (efficiency etc)
Jack Mills
As others have said. Why do you need the 8th specifically? To ask for nth where there is some other predicate is often the preferable option. If you are getting the nth once every so often does it need to be optimised for performance.
John Nolan
The best way to guage "best" in this sort of instance is to test the techniques against a test dataset that closely matches the balance of your live dataset (but is preferably larger if your live dataset is small at the moment) and see what the query planner does in response to them. Look at the apparently query plan, to see if anything nasty like full table scans are starting to turn up where they are not in other methods, and also watch the results coming out of the SQL profiler to see if the techniques differ greatly in the CPU or page read load for other reasons.
David Spillett
@John, that SQL2K query does not do what you think it does.
Peter
That SQL 2000 answer is plain wrong. See Degan's answer
gbn
oops yeah now fixed but untested. I just grabbed a query I had written and altered it without evenb looking at the results, In my defence, I was full of wine :O)
John Nolan
+1  A: 

Well, in T-SQL (the dialect for SQL Server) you can do the following:

SELECT TOP 1 *
  FROM (SELECT TOP 8 *
          FROM Table
         ORDER
            BY SortField)
 ORDER
    BY SortField DESC

This way you get the 8th record.

Paulo Santos
+2  A: 

First, you should say which RDBMS you're using.

Second, you should give careful thought to what it is you're trying to accomplish. Relational Databases are set-based. In general, the order of elements in a set does not matter. You'll want to ask why it matters in this case, then see if there's a better way to embed the concept of order into the query itself.

For instance, in SQL Server 2005 (and other RDBMS), you can use the ROW_NUMBER function to assign a sequential number to each row returned, based on the criteria you specify. You could then select rows based on the row number. Example from Books Online:

USE AdventureWorks;
GO
WITH OrderedOrders AS
(
    SELECT SalesOrderID, OrderDate,
    ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
    FROM Sales.SalesOrderHeader 
) 
SELECT * 
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;
John Saunders
John: will adding a TOP clause in the inner query change the performance? i.e. SELECT TOP N SalesOrderID, OrderDate, ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber' FROM Sales.SalesOrderHeaderwhere N = the max limit of row number (60 in this case)What do you think?
shahkalpesh
I guess the comments above make sense when the N is in the lower range of number of records being selected. e.g. if I want records between 50 to 60 from 10000 records.
shahkalpesh
I think you should try it and find out, looking at the execution plan. SQL Server is capable of noticing that the outer SELECT includes a test on the value of the ROW_NUMBER function in the CTE. It's quite possible that it will not just select all SalesOrderHeader rows and only then filter. You should try it and find out.
John Saunders
I don't have SQL Server installed on my machine. So, I can't try it out. Sorry about that.
shahkalpesh
+3  A: 
SELECT * FROM comments WHERE ...conditions... LIMIT 1 OFFSET 8

OFFSET is a good thing for MySQL

Ben Hughes
+1  A: 

For SQL Server 2005:

select rank() OVER (ORDER BY c.subject, c.date) as rank, c.subject, c.date
   from comments c
   where rank = 8
rein
For some reason I'm getting an error on rank:Invalid column name 'rank'.If I execute everything up until the where I'm getting a table that looks as I think it should including a column called rank, bit odd that I can't use the where along with the rest of the statement
Jack Mills
This won't work - you can't reference the rank column unless you place it in a derived table or a CTE - like this:SELECT * FROM(SELECT rank() OVER (ORDER BY c.subject, c.date) AS rank, c.subject, c.dateFROM comments c) AS aWHERE a.rank = 8
Aaron Alton
+4  A: 

How about

SELECT TOP 1 * FROM 
   (SELECT TOP 8 * FROM Comments ORDER BY foo ASC)
ORDER BY foo DESC
Degan
works for SQL2K. note that if foo isn't unique, which row gets returned is non-determinate.
Peter
This is very inefficient because of the two selects.
vy32
A: 

As you can see, this is not a concept that RDBMSes are very comfortable with. Can you handle it in another abstraction layer?

le dorfier
It might help if I'm a bit clearer in what I'm trying to achieve. Basically I have a page that loads with various blog like posts on the screen, for each blog like post the latest three comments are displayed. I then have a button that when pressed triggers a call to a web service. This web service would then pull the next three comments down to the page. I could hold all the comments on the page and just hide the ones I don't want but I'm trying to keep the site as light weight as possible.
Jack Mills
I agree it shouldn't get as far as the web page. Hopefully your host application has something between the data access layer and the UI layer? Where are you calling the web service from? Can you select which three comments to pull from that code? It wouldn't be a good idea to call a web service from an SQL statement.
le dorfier
@Jack: Yeah, it helped a lot. We now know you're interested in Paging data. It's a classic requirement, and the example I re-posted from Books Online shows exactly how to do it. Consider you can use parameters for the start and end in the BETWEEN.
John Saunders
+1  A: 

I have read the question & your comments on you would want next 3 blog comments etc.

How is your tables structured?
Assume that you have blog post Id & comment Id is generated in ascending order for each blog post, you could do a SELECT based on the current Id.

e.g. if the blogpostId = 101, you get the top 3 comments order by posted Id. Now lets say, you want to get the next 3 comments - you could do a SELECT WHERE commentId between the last comment id shown TO the comment id - 3

But all that depends on how your tables are defined.

shahkalpesh
definitely. make the front end do more bookkeeping.
Peter
+1  A: 

In SQL 2000 where you do not have ROW_NUMBER() function you could use a work-around like this:

SELECT CommentsTableFieldList, IDENTITY(INT, 1,1) as seqNo 
INTO #SeqComments 
FROM Comments

SELECT * FROM #SeqComments 
WHERE seqNo = 8
AlejandroR
that's not the way to create temp tables. use the #-prefix, unless you need to create an ordinary user table that happens to live in tempdb.
Peter
Thanks. Better now.
AlejandroR
Clumsy. Use this: SELECT TOP 1 ... FROM (SELECT TOP 8 ... ORDER BY x ASC) foo ORDER BY x DESC
gbn
Not always you have a column to perform the sorting and its reverse.
AlejandroR