views:

2340

answers:

6

Hello everyone,

I have a table structure like (OrderID [uniqueidentifier], OrderDesciption [nvarchar]), I am using ADO.Net + C# + VSTS 2008 + SQL Server 2008. The table is big, and I want to let client give me two inputs, begin range index and end range index, and I will return specific rows of the table which is in the range (between begin range index and end range index).

For example, if the client inputs to me 50, 100, and I want to return the 50th row until the 100th row.

thanks in advance, George

+2  A: 

Basically, your best bet in SQL Server 2005 and 2008 is a CTE - Common Table Expression - with a ROW_NUMBER() function in it - something like this:

WITH MyOrders AS
(
  SELECT
    OrderID,
    OrderDescription,
    ROW_NUMBER() OVER (ORDER BY OrderID) as 'RowNum'
  FROM YourOrders
)
SELECT * FROM MyOrders
WHERE RowNum BETWEEN 50 AND 100

But this requires a useful and suitable ORDER BY clause, and ordering by a GUID is really not a good idea. DATETIME or an ever-increasing ID would be best.

Marc

marc_s
Hi Marc, sorry for my bad description, I do not want to let client specify range by the value of a GUID orderID, but specify range by the physical row number of the table. Any ideas how to implement this?
George2
Well, my CTE above in my post does this - it's just that selecting by a GUID is a really bad way of ordering your data - it's basically just totally random...... but if that's what you're looking for, my code snippet should achieve this.
marc_s
Confused about this statement -- "ROW_NUMBER() OVER (ORDER BY OrderID) as 'RowNum'" -- as orderID is random GUID, I want to retrieve range by physical row range in database table, not by the range of value of order ID. Looks like from the statement I quoted you select row range by orderID?
George2
I agree Marc, but your statement has some error when executing in SQL Server management studio, I have posted my SQL code executing and output error message, what is wrong?
George2
I have added a new column called OrderCreateTime, and I think if we SELECT xxx, ROW_NUMBER() OVER (ORDER BY OrderCreateTime) should be better than order by order by order ID, correct? And why?
George2
@George2: yes, selecting by OrderCreateTime would be much better - since it defines a real order - by date/time the order was created. The GUID for Order ID is just a random number which really doesn't have an ordering characteristic.
marc_s
The "ROW_NUMBER()" function must be defined over an ordering definition. Only when you put all your rows into an ORDER (of any kind), only then can you attach a ROW_NUMBER to them. A SQL Server row set per se doesn't have any order - that's why you have to define an order by issuing a OVER (ORDER BY (column name)) in order for the ROW_NUMBER function to know how to attach row numbers to rows.
marc_s
Since OrderID is GUID and it is already a clustered index (because clients will frequently query order information by order ID), so do you think create a normal index on OrderCreateTime is an acceptable solution?
George2
Thanks! A further question, do you think using CTE is better for my scenario or using plain T-SQL like Kiewic suggests is better? And why?
George2
I can only second Marc Gravell's recommendation: GUID's make for very poorly performing CLUSTERED indices - it would be MUCH BETTER to have an "OrderID INT IDENTITY" and put the CLUSTERED Index on this column.
marc_s
In the end, the execution plan for Kiewic's query and mine are identical. I prefer the CTE - it seems clearer to me - but that's a subjective, personal opinion.
marc_s
+1  A: 

SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY OrderId) as row FROM Orders) a WHERE row > 5 and row <= 10

balint
I have tried your solution, but something wrong, SELECT * FROM [dbo].[OrderStatus] (SELECT *, ROW_NUMBER() OVER (ORDER BY [OrderID]) as row FROM [dbo].[OrderStatus]) where row > 0 and row < 100Error messages are,Msg 156, Level 15, State 1, Line 2Incorrect syntax near the keyword 'SELECT'.Msg 102, Level 15, State 1, Line 2Incorrect syntax near ')'.
George2
I have added a new column called OrderCreateTime, and I think if we SELECT xxx, ROW_NUMBER() OVER (ORDER BY OrderCreateTime) should be better than order by order by order ID, correct? And why?
George2
you've missed naming the select ".. from Orders) >>a<< where.."
balint
Confused, a stands for?
George2
+2  A: 

You can use ROW_NUMBER in TSQL (2005 onwards) to do this:

SELECT  ID, Foo, Bar
FROM     (SELECT  ROW_NUMBER() OVER (ORDER BY ID ASC) AS Row,
          ID, Foo, Bar
FROM    SomeTable) tmp
WHERE   Row >= 50 AND Row <= 100

Or with LINQ-to-SQL etc:

var qry = ctx.Table.Skip(50).Take(50); // or similar
Marc Gravell
Something wrong output from SQL Server management studio. I tried your SQL Statement in this way,select [OrderStatus].[OrderID], [OrderStatus].[OrderStatus]from(SELECT ROW_NUMBER() OVER (ORDER BY Date DESC) AS Row,[OrderStatus].[OrderID], [OrderStatus].[OrderStatus] from [OrderStatus])where Row >= 0 and Row < 100Error message is,Msg 156, Level 15, State 1, Line 5Incorrect syntax near the keyword 'where'.
George2
Where is your fix? I am confused. Could you point it out please? :-)
George2
The addition of "tmp"
Marc Gravell
I have added a new column called OrderCreateTime, and I think if we SELECT xxx, ROW_NUMBER() OVER (ORDER BY OrderCreateTime) should be better than order by order by order ID, correct? And why?
George2
tmp? I did not see in your current post, there is a term called "tmp"? Confused. :-)
George2
If OrderID is an IDENTITY column, it will likely be increasing with time anyway, and is a simpler data type. Of course, if you have a clustered index on OrderCreateTime then it is ideal for ordering...
Marc Gravell
see "FROM SomeTable) tmp" - this alias is all that is required to make it work.
Marc Gravell
Thanks Marc, since OrderID is GUID and it is already a clustered index (because clients will frequently query order information by order ID), so do you think create a normal index on OrderCreateTime is an acceptable solution?
George2
A Guid usually makes a very poor *clustered* index (but an excellent non-clustered index). Are you using "combing" to generate increasing guids? If not, I would recommend switching the clustered index to OrderCreateTime. A non-clustered index is also good at sorting - but just not **as** good, so if needs be, a non-clustered index on OrderCreateTime would do - but you are rarely doing equality matches on things that involve time, so *generally* times make a poor non-clustered index.
Marc Gravell
+1  A: 

Try this, the result will be ordered by OrderID column. Change MyTable with your table.

SELECT * 
FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY OrderID) AS row FROM MyTable) a 
WHERE row > 50 AND row <= 100

The selected rows are from 50 to 100, but notice that the 50th row is not included in the result.

Kiewic
I execute your statement but something from SQL Server Management Studio.select *from(SELECT ROW_NUMBER() OVER (ORDER BY [OrderStatus].[OrderID]) AS Row from [OrderStatus])where Row >= 0 and Row < 100Error message is,Msg 156, Level 15, State 1, Line 4Incorrect syntax near the keyword 'where'.
George2
Hey, your erase the letter **a** that was after the **SELECT** on the third line. It's an alias and it is necessary.
Kiewic
Confused, a stands for what?
George2
I have added a new column called OrderCreateTime, and I think if we SELECT *, ROW_NUMBER() OVER (ORDER BY OrderCreateTime) should be better than order by order by order ID, correct and why?
George2
The "a" gives a name to the anonymous query inside the parenthesis.
Kiewic
+1  A: 

Here's the best comparison of ADO.NET paging techniques I've seen:

http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

a little old but still worth reading

zvolkov
That post ages badly - the SQL Server 2005 "OVER" keyword is ideal for efficient paging, but this article doesn't mention it.
Marc Gravell
@Marc, a further question, do you think using CTE is better for my scenario or using plain T-SQL like Kiewic suggests is better? And why?
George2
A: 

i tried the query SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY item_number) AS row FROM t_item_master) a WHERE row > 6000 AND row <= 68908 but it returns many rows as usual not the expected rows between 6000 and 68908

bry
This isn't a forum. Ask it as a new question and put a reference to this question into it. (You should also delete this answer afterwards)
Oliver