I want to have a union of two request BUT the order by rand() of the second one doesn't work..
(select * from survey
where is_priority = 1)
union all (
select * from (
select * from survey
order by rand()
) as t );
The result look like this :
I speculate that you want is_priority = 1 first followed by the rest in random order.
If so, you should not do this with union all. Just add the right keys to the order by:
select s.*
from survey s
order by (s.is_priority = 1) desc, -- put priority 1 first in the result set
rand();
Admittedly, this puts the top priority rows in random order (amongst them). But you haven't specified an order for them (and this is a non-issue if there is only one row where the priority condition is true).
I have a scenario where I need to obtain a set of results in ascending order but only given the last ID in the descending sequence.
The order will always be sequentially ascending as the ID is auto generated however, I cannot guarantee that ID sequence is unbroken. i.e. I cannot say 8 - 3 = 5 therefore do a forward lookup from 5.
What is the most efficient was of achieving this. If possibly I want to avoid manipulating the collection in the application logic but can as a last resort.
You could do with an inner select:
SELECT *
FROM (
SELECT *
FROM your_table
WHERE id <= X
ORDER BY id DESC
LIMIT Y
) d
ORDER BY id ASC
You can define your own rownum with query and apply mod to get every 3rd record regardless of it's id, e.g.:
SELECT *
FROM (SELECT id, name, #rownum := #rownum + 1 as `num`
FROM test, (SELECT #rownum := 0) a ) b
WHERE b.num % 3 = 0;
Here's the SQL Fiddle.
mytable has an auto-incrementing id column which is an integer, and for all intents and purposes in this case you can safely assume that the higher ID represents a more recent value. mytable also has an indexed column called group_id which is a foreign key to the groups table.
I want a quick and dirty query to select the 5 most recent rows for each group_id from mytable.
If there were only three groups, this would be easy, as I could do this:
SELECT * FROM `mytable` WHERE `group_id` = 1 ORDER BY `id` DESC LIMIT 5
UNION ALL
SELECT * FROM `mytable` WHERE `group_id` = 2 ORDER BY `id` DESC LIMIT 5
UNION ALL
SELECT * FROM `mytable` WHERE `group_id` = 3 ORDER BY `id` DESC LIMIT 5
However, there is not a fixed number of groups. Groups are determined by the what's in the groups table, so there is an indeterminate number of them.
My thoughts so far:
I could grab a CURSOR on the groups table and build a new SQL query string, then EXECUTE it. However, that seems really messy and I'm hoping there's a better way of doing it.
I could grab a CURSOR on the groups table and insert things into a temporary table, then select from that. However, that also seems really messy.
I don't know if I could just grab a CURSOR and then start returning rows directly from there. Is there perhaps something similar to SQL Server's #table type variables?
What I'm hoping most of all is that I'm overthinking this and there is a way to do this in a SELECT statement.
To get n most recent rows per group can be best handled by window functions in other RDBMS (SQL Server,Postgre Sql,Oracle etc), But unfortunately MySql don't have any window functions so for alternative there is a solution to use user defined variables to assign a rank for rows that belong to same group in this case ORDER BY group_id,id desc is important to order the results properly per group
SELECT c.*
FROM (
SELECT *,
#r:= CASE WHEN #g = group_id THEN #r + 1 ELSE 1 END rownum,
#g:=group_id
FROM mytable
CROSS JOIN(SELECT #g:=NULL ,#r:=0) t
ORDER BY group_id,id desc
) c
WHERE c.rownum <=5
Above query will give you 5 recent rows for each group_id and if you want to get more than 5 rows just change where filter of outer query to your desired number WHERE c.rownum <= n
This question already has answers here:
Implement paging (skip / take) functionality with this query
(6 answers)
Closed 1 year ago.
I have this query with MySQL:
select * from table1 LIMIT 10,20
How can I do this with SQL Server?
Starting SQL SERVER 2005, you can do this...
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 10 AND 20;
or something like this for 2000 and below versions...
SELECT TOP 10 * FROM (SELECT TOP 20 FROM Table ORDER BY Id) ORDER BY Id DESC
Starting with SQL SERVER 2012, you can use the OFFSET FETCH Clause:
USE AdventureWorks;
GO
SELECT SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
ORDER BY SalesOrderID
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
GO
http://msdn.microsoft.com/en-us/library/ms188385(v=sql.110).aspx
This may not work correctly when the order by is not unique.
If the query is modified to ORDER BY OrderDate, the result set returned is not as expected.
This is how I limit the results in MS SQL Server 2012:
SELECT *
FROM table1
ORDER BY columnName
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
NOTE: OFFSET can only be used with or in tandem to ORDER BY.
To explain the code line OFFSET xx ROWS FETCH NEXT yy ROW ONLY
The xx is the record/row number you want to start pulling from in the table, i.e: If there are 40 records in table 1, the code above will start pulling from row 10.
The yy is the number of records/rows you want to pull from the table.
To build on the previous example: If table 1 has 40 records and you began pulling from row 10 and grab the NEXT set of 10 (yy).
That would mean, the code above will pull the records from table 1 starting at row 10 and ending at 20. Thus pulling rows 10 - 20.
Check out the link for more info on OFFSET
This is almost a duplicate of a question I asked in October:
Emulate MySQL LIMIT clause in Microsoft SQL Server 2000
If you're using Microsoft SQL Server 2000, there is no good solution. Most people have to resort to capturing the result of the query in a temporary table with a IDENTITY primary key. Then query against the primary key column using a BETWEEN condition.
If you're using Microsoft SQL Server 2005 or later, you have a ROW_NUMBER() function, so you can get the same result but avoid the temporary table.
SELECT t1.*
FROM (
SELECT ROW_NUMBER OVER(ORDER BY id) AS row, t1.*
FROM ( ...original SQL query... ) t1
) t2
WHERE t2.row BETWEEN #offset+1 AND #offset+#count;
You can also write this as a common table expression as shown in #Leon Tayson's answer.
SELECT *
FROM (
SELECT TOP 20
t.*, ROW_NUMBER() OVER (ORDER BY field1) AS rn
FROM table1 t
ORDER BY
field1
) t
WHERE rn > 10
Syntactically MySQL LIMIT query is something like this:
SELECT * FROM table LIMIT OFFSET, ROW_COUNT
This can be translated into Microsoft SQL Server like
SELECT * FROM
(
SELECT TOP #{OFFSET+ROW_COUNT} *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS rnum
FROM table
) a
WHERE rnum > OFFSET
Now your query select * from table1 LIMIT 10,20 will be like this:
SELECT * FROM
(
SELECT TOP 30 *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS rnum
FROM table1
) a
WHERE rnum > 10
SELECT TOP 10 * FROM table;
Is the same as
SELECT * FROM table LIMIT 0,10;
Here's an article about implementing Limit in MsSQL Its a nice read, specially the comments.
This is one of the reasons I try to avoid using MS Server... but anyway. Sometimes you just don't have an option (yei! and I have to use an outdated version!!).
My suggestion is to create a virtual table:
From:
SELECT * FROM table
To:
CREATE VIEW v_table AS
SELECT ROW_NUMBER() OVER (ORDER BY table_key) AS row,* FROM table
Then just query:
SELECT * FROM v_table WHERE row BETWEEN 10 AND 20
If fields are added, or removed, "row" is updated automatically.
The main problem with this option is that ORDER BY is fixed. So if you want a different order, you would have to create another view.
UPDATE
There is another problem with this approach: if you try to filter your data, it won't work as expected. For example, if you do:
SELECT * FROM v_table WHERE field = 'test' AND row BETWEEN 10 AND 20
WHERE becomes limited to those data which are in the rows between 10 and 20 (instead of searching the whole dataset and limiting the output).
In SQL there's no LIMIT keyword exists. If you only need a limited number of rows you should use a TOP keyword which is similar to a LIMIT.
Must try. In below query, you can see group by, order by, Skip rows, and limit rows.
select emp_no , sum(salary_amount) from emp_salary
Group by emp_no
ORDER BY emp_no
OFFSET 5 ROWS -- Skip first 5
FETCH NEXT 10 ROWS ONLY; -- limit to retrieve next 10 row after skiping rows
Easy way
MYSQL:
SELECT 'filds' FROM 'table' WHERE 'where' LIMIT 'offset','per_page'
MSSQL:
SELECT 'filds' FROM 'table' WHERE 'where' ORDER BY 'any' OFFSET 'offset'
ROWS FETCH NEXT 'per_page' ROWS ONLY
ORDER BY is mandatory
This is a multi step approach that will work in SQL2000.
-- Create a temp table to hold the data
CREATE TABLE #foo(rowID int identity(1, 1), myOtherColumns)
INSERT INTO #foo (myColumns) SELECT myData order By MyCriteria
Select * FROM #foo where rowID > 10
SELECT
*
FROM
(
SELECT
top 20 -- ($a) number of records to show
*
FROM
(
SELECT
top 29 -- ($b) last record position
*
FROM
table -- replace this for table name (i.e. "Customer")
ORDER BY
2 ASC
) AS tbl1
ORDER BY
2 DESC
) AS tbl2
ORDER BY
2 ASC;
-- Examples:
-- Show 5 records from position 5:
-- $a = 5;
-- $b = (5 + 5) - 1
-- $b = 9;
-- Show 10 records from position 4:
-- $a = 10;
-- $b = (10 + 4) - 1
-- $b = 13;
-- To calculate $b:
-- $b = ($a + position) - 1
-- For the present exercise we need to:
-- Show 20 records from position 10:
-- $a = 20;
-- $b = (20 + 10) - 1
-- $b = 29;
If your ID is unique identifier type or your id in table is not sorted you must do like this below.
select * from
(select ROW_NUMBER() OVER (ORDER BY (select 0)) AS RowNumber,* from table1) a
where a.RowNumber between 2 and 5
The code will be
select * from limit 2,5
better use this in MSSQLExpress 2017.
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) as [Count], * FROM table1
) as a
WHERE [Count] BETWEEN 10 and 20;
--Giving a Column [Count] and assigning every row a unique counting without ordering something then re select again where you can provide your limits.. :)
One of the possible way to get result as below , hope this will help.
declare #start int
declare #end int
SET #start = '5000'; -- 0 , 5000 ,
SET #end = '10000'; -- 5001, 10001
SELECT * FROM (
SELECT TABLE_NAME,TABLE_TYPE, ROW_NUMBER() OVER (ORDER BY TABLE_NAME) as row FROM information_schema.tables
) a WHERE a.row > #start and a.row <= #end
If i remember correctly (it's been a while since i dabbed with SQL Server) you may be able to use something like this: (2005 and up)
SELECT
*
,ROW_NUMBER() OVER(ORDER BY SomeFields) AS [RowNum]
FROM SomeTable
WHERE RowNum BETWEEN 10 AND 20
I'm about to throw in the towel with this.
Preface: I want to make this work with any N, but for the sake of simplicity, I'll set N to be 3.
I've got a query (MySQL, specifically) that needs to pull in data from a table and sort based on top 3 values from that table and after that fallback to other sort criteria.
So basically I've got something like this:
SELECT tbl.id
FROM
tbl1 AS maintable
LEFT JOIN
tbl2 AS othertable
ON
maintable.id = othertable.id
ORDER BY
othertable.timestamp DESC,
maintable.timestamp DESC
Which is all basic textbook stuff. But the issue is I need the first ORDER BY clause to only get the three biggest values in othertable.timestamp and then fallback on maintable.timestamp.
Also, doing a LIMIT 3 subquery to othertable and join it is a no go as this needs to work with an arbitrary number of WHERE conditions applied to maintable.
I was almost able to make it work with a user variable based approach like this, but it fails since it doesn't take into account ordering, so it'll take the FIRST three othertable values it finds:
ORDER BY
(
IF(othertable.timestamp IS NULL, 0,
IF(
(#rank:=#rank+1) > 3, null, othertable.timestamp
)
)
) DESC
(with a #rank:=0 preceding the statement)
So... any tips on this? I'm losing my mind with the problem. Another parameter I have for this is that since I'm only altering an existing (vastly complicated) query, I can't do a wrapping outer query. Also, as noted, I'm on MySQL so any solutions using the ROW_NUMBER function are unfortunately out of reach.
Thanks to all in advance.
EDIT. Here's some sample data with timestamps dumbed down to simpler integers to illustrate what I need:
maintable
id timestamp
1 100
2 200
3 300
4 400
5 500
6 600
othertable
id timestamp
4 250
5 350
3 550
1 700
=>
1
3
5
6
4
2
And if for whatever reason we add WHERE NOT maintable.id = 5 to the query, here's what we should get:
1
3
4
6
2
...because now 4 is among the top 3 values in othertable referring to this set.
So as you see, the row with id 4 from othertable is not included in the ordering as it's the fourth in descending order of timestamp values, thus it falls back into getting ordered by the basic timestamp.
The real world need for this is this: I've got content in "maintable" and "othertable" is basically a marker for featured content with a timestamp of "featured date". I've got a view where I'm supposed to float the last 3 featured items to the top and the rest of the list just be a reverse chronologic list.
Maybe something like this.
SELECT
id
FROM
(SELECT
tbl.id,
CASE WHEN othertable.timestamp IS NULL THEN
0
ELSE
#i := #i + 1
END AS num,
othertable.timestamp as othertimestamp,
maintable.timestamp as maintimestamp
FROM
tbl1 AS maintable
CROSS JOIN (select #i := 0) i
LEFT JOIN tbl2 AS othertable
ON maintable.id = othertable.id
ORDER BY
othertable.timestamp DESC) t
ORDER BY
CASE WHEN num > 0 AND num <= 3 THEN
othertimestamp
ELSE
maintimestamp
END DESC
Modified answer:
select ilv.* from
(select sq.*, #i:=#i+1 rn from
(select #i := 0) i
CROSS JOIN
(select m.*, o.id o_id, o.timestamp o_t
from maintable m
left join othertable o
on m.id = o.id
where 1=1
order by o.timestamp desc) sq
) ilv
order by case when o_t is not null and rn <=3 then rn else 4 end,
timestamp desc
SQLFiddle here.
Amend where 1=1 condition inside subquery sq to match required complex selection conditions, and add appropriate limit criteria after the final order by for paging requirements.
Can you use a union query as below?
(SELECT id,timestamp,1 AS isFeatured FROM tbl2 ORDER BY timestamp DESC LIMIT 3)
UNION ALL
(SELECT id,timestamp,2 AS isFeatured FROM tbl1 WHERE NOT id in (SELECT id from tbl2 ORDER BY timestamp DESC LIMIT 3))
ORDER BY isFeatured,timestamp DESC
This might be somewhat redundant, but it is semantically closer to the question you are asking. This would also allow you to parameterize the number of featured results you want to return.