I've been doing a lot of reading on alternatives to the LIMIT clause for SQL SERVER. It's so frustrating that they still refuse to adapt it. Anyway, I really havn't been able to get my head around this. The query I'm trying to convert is this...
SELECT ID, Name, Price, Image FROM Products ORDER BY ID ASC LIMIT $start_from, $items_on_page
Any assistance would be much appreciated, thank you.
In SQL Server 2012, there is support for the ANSI standard OFFSET / FETCH syntax. I blogged about this and here is the official doc (this is an extension to ORDER BY). Your syntax converted for SQL Server 2012 would be:
SELECT ID, Name, Price, Image
FROM Products
ORDER BY ID ASC
OFFSET (#start_from - 1) ROWS -- not sure if you need -1
-- because I don't know how you calculated #start_from
FETCH NEXT #items_on_page ROWS ONLY;
Prior to that, you need to use various workarounds, including the ROW_NUMBER() method. See this article and the follow-on discussion. If you are not on SQL Server 2012, you can't use standard syntax or MySQL's non-standard LIMIT but you can use a more verbose solution such as:
;WITH o AS
(
SELECT TOP ((#start_from - 1) + #items_on_page)
-- again, not sure if you need -1 because I
-- don't know how you calculated #start_from
RowNum = ROW_NUMBER() OVER (ORDER BY ID ASC)
/* , other columns */
FROM Products
)
SELECT
RowNum
/* , other columns */
FROM
o
WHERE
RowNum >= #start_from
ORDER BY
RowNum;
There are many other ways to skin this cat, this is unlikely to be the most efficient but syntax-wise is probably simplest. I suggest reviewing the links I posted as well as the duplicate suggestions noted in the comments to the question.
For SQL Server 2005 and 2008
This is an example query to select rows from 11 to 20 from Report table ordered by LastName.
SELECT a.* FROM
(SELECT *, ROW_NUMBER() OVER (ORDER BY LastName) as row FROM Report) a
WHERE a.row > 10 and a.row <= 20
Try this:
SELECT TOP $items_on_page ID, Name, Price, Image
FROM (SELECT TOP $start_from + $items_on_page - 1 * FROM Products ORDER BY ID) as T
ORDER BY ID DESC
EDIT: Explanation-
No getting around the subquery, but this is an elegant solution.
Say you wanted 10 items per page, starting on the 5th row, this would give you the bottom 10 rows of the top 14 rows. Essentially LIMIT 5,10
you can use ROW COUNT : Returns the number of rows affected by the last statement. when you don, you reset the rowcont.
SET ROWCOUNT 100
or
you can try using TOP query
SELECT TOP 100 * FROM Sometable ORDER BY somecolumn
If you allow the application to store a tad of state, you can do this using just TOP items_on_page. What you do is to store the ID of the last item you retrieved, and for each subsequent query add AND ID > [last ID]. So you get the items in batches of items_on_page, starting where you left off each time.
Related
let's say I have a table CData with the columns CName, Amount1, Amount2.
Now I want to use a query to get calculate the difference between Amount1 and Amount2 for each distinct CName and, as a result of the query, get the ~1000 rows with the biggest difference and the 1000~ rows with the smallest (or most negative) difference. It doesn't matter if the results come in one table or two.
1) I am aware of the function TOP and so I could do this with two queries and sort by Difference (once ascending, once descending). Is there a way to do this in one query, though? This would save some time.
2) General question: When I define a field in my query (in this example "Difference"), can I somehow use it to, for example, sort the data by it? Like this (well, it's not working, but to give you an idea of what I mean):
SELECT CData.CName, CData.Amount2-CData.Amount1 AS Difference
FROM CData
GROUP BY CData.CName
ORDER BY Difference
Or do I always have to do the following:
...
ORDER BY CData.Amount2-CData.Amount1
Not much of a difference in this example, I just wanted to know if that's possible in general.
Sort the first time ASC (Ascending) and the second time DESC (Descending)
SELECT TOP 1000
CData.CName,
CData.Amount2 - CData.Amount1 AS Difference
FROM CData
GROUP BY CData.CName
ORDER BY CData.Amount2 - CData.Amount1 ASC
SELECT TOP 1000
CData.CName,
CData.Amount2 - CData.Amount1 AS Difference
FROM CData
GROUP BY CData.CName
ORDER BY CData.Amount2 - CData.Amount1 DESC
which aggregate functino do you want to perform for your differences? Avg? Sum?
SELECT CName, avg(Amount2-Amount1) AS Difference
FROM CData
GROUP BY CName
btw, to do it in 'one' query, you could use a union query on two subqueries, one with the TOP 1000 asc, one with the TOP 1000 desc
looks like Access is not allowing you to use an alias in the ORDER BY Clause, if you use the QBE grid you can change the format from the UI to SQL and it repeats the calculation in the ORDER BYclause.
Hi, John.
Check out the SO tour for instructions on how to use options such as formatting code.
Not sure if this will work for you, but you can try something like:
select * from
(SELECT TOP 3
CName, Date_Sale, Sum(Amount) AS SumA, 99999-Sum(Amount) as srt
FROM
Data
GROUP BY
CName, Date_Sale
UNION
SELECT TOP 3
CName, Date_Sale, Sum(Amount) AS SumA, Sum(Amount) as srt
FROM
Data
GROUP BY
CName, Date_Sale) u
order by
srt
We have to use Limit keyword in SQL server 2008/2012.
We need to apply limit for every query where start index will change every time. When I was googling found TOP but it won't work for us. Can anyone please share how to use LIMIT keyword in sql server where every time start index change.
We need query in SQL server like below -
SELECT * from STOCK LIMIT 11, 10000 (where 11=start index, 10000=size)
This is probably not a good long-term solution for you, but if the table has an Identity field you could do something like this:
SELECT TOP 1000 * FROM STOCK
WHERE Id > 11
ORDER BY Id
You could use a CTE with a window function, like in the answer to this SO question -> Skip first row in SQL Server 2005?
You would have to use something like the ROW_NUMBER() function and then specify what you want from there.
select top 10000 *, row_number() over (order by [YourFieldName]) as row from Stock where row > 11
We have upgraded to SQL server 2012 and replaced query with OFFSET and FETCH. Sample is below.
SELECT ITEM_ID, PRICE FROM MENU ORDER BY ITEM_ID ASC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
I try to get 10 random rows of a table. Many similar questions are out there but I have got an additional condition: I want this list to update weekly, 52 times a year. These 10 items are going to be the "random featured ones of this week."
So, I found this snippet of getting random rows that change each time you invoke the script
select top 10 percent * from [yourtable] order by newid();
The problem with it is that I can't simply add a seed, which in this case would be the week-of-year. Any idea how to implement this?
Your code sample is T-SQL (SQL Server). So, to do this in SQL Server is pretty easy:
select *
from (select t.*,
row_number() over (partition by datepart(wk, thedate) order by newid()) as seqnum
from [yourtable] t
) t
where seqnum <= 10;
MySQL does not support row_number(), so the approach would be different in that database.
I have a blog SQL system with 'posts' table like this (id, theme, title, content, created).
I would like to get the last 3 titles of each post theme, to display in a table, but I didn't get it.
I tried this SQL code:
SELECT *
FROM posts
GROUP BY theme
ORDER BY created DESC
LIMIT 0,3
But this gives me the last 3 posts of the table!!!
Can you please help me?
Thanks in advance.
You're doing some parts right, but in others you're not understanding the use of the GROUP BY clause.
The LIMIT above works on the whole query results. Besides you need an aggregate function for the GROUP BY to be applied on, e.g. SUM, AVG, etc. You cannot simply SELECT * and expect grouping by theme.
The GROUP BY returns one row per item you're grouping by, for instance, the latest, and not three rows.
Therefore you need to perform the query per theme you're grouping by, but in this case you don't need to group by at all. Write a loop that executes this query for each theme you require:
SELECT *
FROM posts
WHERE theme = "your-theme"
ORDER BY created DESC
LIMIT 0,3
Josvic Zammit's answer is in parts right (+1 from me) but you don't have to perform the query per theme. This is a way you can do it in MySQL, although it would surely be easier to "perform the query per theme" in PHP or whatever.
SELECT * FROM
(
SELECT
IF(#prev != sq.theme, #rownum:=1, #rownum:=#rownum + 1) AS rownumber, #prev:=sq.theme, sq.*
FROM
(
SELECT
*
FROM
posts
, (SELECT #rownum:=0, #prev:=0) r
ORDER BY theme, created DESC
) sq
) q
WHERE q.rownumber <= 3
Note that I unfortunately can't test it right now, but it should work. Also note, that this query might be slower than performing a simpler query per theme.
I need to select sample rows from a set. For example if my select query returns x rows then if x is greater than 50 , I want only 50 rows returned but not just the top 50 but 50 that are evenly spread out over the resultset. The table in this case records routes - GPS locations + DateTime.
I am ordering on DateTime and need a reasonable sample of the Latitude & Longitude values.
Thanks in advance
[ SQL Server 2008 ]
To get sample rows in SQL Server, use this query:
SELECT TOP 50 * FROM Table
ORDER BY NEWID();
If you want to get every n-th row (10th, in this example), try this query:
SELECT * From
(
SELECT *, (Dense_Rank() OVER (ORDER BY Column ASC)) AS Rank
FROM Table
) AS Ranking
WHERE Rank % 10 = 0;
Source
More examples of queries selecting random rows for other popular RDBMS can be found here: http://www.petefreitag.com/item/466.cfm
Every n'th row to get 50:
SELECT *
FROM table
WHERE row_number() over() MOD (SELECT Count(*) FROM table) / 50 == 0
FETCH FIRST 50 ROWS ONLY
And if you want a random sample, go with jimmy_keen's answer.
UPDATE:
In regard to the requirement for it to run on MS SQL, I think it should be changed to this (no MS SQL Server around to test though):
SELECT TOP 50 *
FROM (
SELECT t.*, row_number() over() AS rn, (SELECT count(*) FROM table) / 50 AS step
FROM table t
)
WHERE rn % step == 0
I suggest that you add a calculated column to your resultset on selection that is obtained as a random number, and then select the top 50 sorted by that column. That will give you a random sample.
For example:
SELECT TOP 50 *, RAND(Id) AS Random
FROM SourceData
ORDER BY Random
where SourceData is your source data table or view. This assumes T-SQL on SQL Server 2008, by the way. It also assumes that you have an Id column with unique ids on your data source. If your ids are very low numbers, it is a good practice to multiply them by a large integer before passing them to RAND, like this:
RAND(Id * 10000000)
If you want an statically correct sample, tablesample is a wrong solution. A good solution as I described in here based on a Microsoft Research paper, is to create a materialized view over your table which includes an additional column like
CAST( ROW_NUMBER() OVER (...) AS BYTE ) AS RAND_COL_, then you can add an index on this column, plus other interesting columns and get statistically correct samples for your queries fairly quickly. (by using WHERE RAND_COL_ = 1).