I am wondering if there is away (possibly a better way) to order by the order of the values in an IN() clause.
The problem is that I have 2 queries, one that gets all of the IDs and the second that retrieves all the information. The first creates the order of the IDs which I want the second to order by. The IDs are put in an IN() clause in the correct order.
So it'd be something like (extremely simplified):
SELECT id FROM table1 WHERE ... ORDER BY display_order, name
SELECT name, description, ... WHERE id IN ([id's from first])
The issue is that the second query does not return the results in the same order that the IDs are put into the IN() clause.
One solution I have found is to put all of the IDs into a temp table with an auto incrementing field which is then joined into the second query.
Is there a better option?
Note: As the first query is run "by the user" and the second is run in a background process, there is no way to combine the 2 into 1 query using sub queries.
I am using MySQL, but I'm thinking it might be useful to have it noted what options there are for other DBs as well.
Use MySQL's FIELD() function:
SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])
FIELD() will return the index of the first parameter that is equal to the first parameter (other than the first parameter itself).
FIELD('a', 'a', 'b', 'c')
will return 1
FIELD('a', 'c', 'b', 'a')
will return 3
This will do exactly what you want if you paste the ids into the IN() clause and the FIELD() function in the same order.
See following how to get sorted data.
SELECT ...
FROM ...
WHERE zip IN (91709,92886,92807,...,91356)
AND user.status=1
ORDER
BY provider.package_id DESC
, FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10
Two solutions that spring to mind:
order by case id when 123 then 1 when 456 then 2 else null end asc
order by instr(','||id||',',',123,456,') asc
(instr() is from Oracle; maybe you have locate() or charindex() or something like that)
If you want to do arbitrary sorting on a query using values inputted by the query in MS SQL Server 2008+, it can be done by creating a table on the fly and doing a join like so (using nomenclature from OP).
SELECT table1.name, table1.description ...
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx)
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx
If you replace the VALUES statement with something else that does the same thing, but in ANSI SQL, then this should work on any SQL database.
Note:
The second column in the created table (orderTbl.orderIdx) is necessary when querying record sets larger than 100 or so. I originally didn't have an orderIdx column, but found that with result sets larger than 100 I had to explicitly sort by that column; in SQL Server Express 2014 anyways.
SELECT ORDER_NO, DELIVERY_ADDRESS
from IFSAPP.PURCHASE_ORDER_TAB
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126')
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)
worked really great
Ans to get sorted data.
SELECT ...
FROM ...
ORDER BY FIELD(user_id,5,3,2,...,50) LIMIT 10
The IN clause describes a set of values, and sets do not have order.
Your solution with a join and then ordering on the display_order column is the most nearly correct solution; anything else is probably a DBMS-specific hack (or is doing some stuff with the OLAP functions in standard SQL). Certainly, the join is the most nearly portable solution (though generating the data with the display_order values may be problematic). Note that you may need to select the ordering columns; that used to be a requirement in standard SQL, though I believe it was relaxed as a rule a while ago (maybe as long ago as SQL-92).
Use MySQL FIND_IN_SET function:
SELECT *
FROM table_name
WHERE id IN (..,..,..,..)
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
For Oracle, John's solution using instr() function works. Here's slightly different solution that worked -
SELECT id
FROM table1
WHERE id IN (1, 20, 45, 60)
ORDER BY instr('1, 20, 45, 60', id)
I just tried to do this is MS SQL Server where we do not have FIELD():
SELECT table1.id
...
INNER JOIN
(VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
) AS X(id,sortorder)
ON X.id = table1.id
ORDER BY X.sortorder
Note that I am allowing duplication too.
Give this a shot:
SELECT name, description, ...
WHERE id IN
(SELECT id FROM table1 WHERE...)
ORDER BY
(SELECT display_order FROM table1 WHERE...),
(SELECT name FROM table1 WHERE...)
The WHEREs will probably take a little tweaking to get the correlated subqueries working properly, but the basic principle should be sound.
My first thought was to write a single query, but you said that was not possible because one is run by the user and the other is run in the background. How are you storing the list of ids to pass from the user to the background process? Why not put them in a temporary table with a column to signify the order.
So how about this:
The user interface bit runs and inserts values into a new table you create. It would insert the id, position and some sort of job number identifier)
The job number is passed to the background process (instead of all the ids)
The background process does a select from the table in step 1 and you join in to get the other information that you require. It uses the job number in the WHERE clause and orders by the position column.
The background process, when finished, deletes from the table based on the job identifier.
I think you should manage to store your data in a way that you will simply do a join and it will be perfect, so no hacks and complicated things going on.
I have for instance a "Recently played" list of track ids, on SQLite i simply do:
SELECT * FROM recently NATURAL JOIN tracks;
Im currently working on a project that requires MySql database and im having a hard time constructing the query that i want get.
i want to get the previous 10 rows from the specific WHERE condition on my mysql query.
for example
My where is date='December';
i want the last 10 months to as a result.
Feb,march,april,may,june,july,aug,sept,oct,nov like that.
Another example is.
if i have a 17 strings stored in my database. and in my where clause i specify that WHERE strings='eyt' limit 3
Test
one
twi
thre
for
payb
six
seven
eyt
nayn
ten
eleven
twelve
tertin
fortin
fiftin
sixtin
the result must be
payb
six
seven
Thanks in advance for your suggestions or answers
If you are using PDO this is the right syntax:
$objStmt = $objDatabase->prepare('SELECT * FROM calendar ORDER BY id DESC LIMIT 10');
You can change ASC to DESC in order to get either the first or the last 10.
Here's a solution:
select t.*
from mytable t
inner join (select id from mytable where strings = 'eyt' order by id limit 1) x
on t.id < x.id
order by t.id desc
limit 3
Demo: http://sqlfiddle.com/#!9/7ffc4/2
It outputs the rows in descending order, but you can either live with that, or else put that query in a subquery and reverse the order.
Re your comment:
x in the above query is called a "correlation name" so we can refer to columns of the subquery as if they were columns of a table. It's required when you use a subquery as a table.
I chose the letter x arbitrarily. You can use anything you like as a correlation name, following the same rules you would use for any identifier.
You can also optionally define a correlation name for any simple table in the query (like mytable t above), so you can refer to columns of that table using a convenient abbreviated name. For example in t.id < x.id
Some people use the term "table alias" but the technical term is "correlation name".
I was thought how to make a complicated (as I think) SELECT query from table with 3 kind of comments (first - positive comments, second - negative comments and third - neutral comments). Using PHP I would like to SELECT and diplay first negative comments, and right after negative comments diplay all other type of comments. How to diplay them with one SELECT query together with LIMIT that I use to separate for pagination?
Example of table:
id - unique id
type - (value 1-positive, 2-negative, 3-neutral)
text - value
I was thought first SELECT * FROM comments WHERE type='2' ORDER BY id LIMIT 0,100
while(){
...
}
Right after that second
SELECT * FROM commetns WHERE type!='2' ORDER BY id LIMIT 0,100
while(){
...
}
But how use LIMIT for pagination if there is two different SELECT queries?
Use a combination of UNION and LIMIT.
However, you need to determine the bind variables, and specify the number of rows you want to display.
(SELECT * FROM comments WHERE type = '2' LIMIT ?)
UNION
(SELECT * FROM comments WHERE type != '2' LIMIT ?);
SQLFiddle: http://sqlfiddle.com/#!9/6d682/1
Use an IF statement in the ORDER BY clause to change the type 2 to sort first:-
SELECT *
FROM comments
ORDER BY IF(type = 2, 0, type)
LIMIT 1, 20
This will give you all the negative (type 2) comments first, followed by the other comments (whether positive or neutral). You would probably want to add an extra column to the sort just for consistency of display.
I didn't get your case exactly, but I think you may use OR operator to get what you want:
SELECT * from comments WHERE type=2 OR type=-2 ORDER BY type DESC
you can use union to merge group of tables, but you must have the same columns in all the tables, for example:
select commetns,'nagtive' as type from nagtive_tbl limit 10
union
select commetns,'positive' as type from positive_tbl limit 10
union
select commetns,'neutral' as type from neutral_tbl limit 10
this query return table with all the comments from different tables each row contain is type so you can filter it while you building the lists on the client.
I must be missing context, otherwise you would just be fine using:
SELECT * from comments ORDER BY type ASC LIMIT 0,10
So by ordering the type, you'll first get all the items with type 1, followed by 2 and 3.
Just using the limit will chop them in the pieces you want and it will still respect the order.
I would like get the id and the number of id. So, I write this command sql:
SELECT count(id), id
FROM tblExample
It's doesn't work. Have you a solution for me ? For to get the value of my id and the number of id.
Or a function PHP for count my resultset.
Just add a GROUP BY id:
SELECT id, COUNT(id)
FROM tblExample
GROUP BY id;
Demo
Update:
The query you posted:
SELECT count(id), id
FROM tblExample;
Won't work in most of the RDBMS and it shouldn't. In SQL Server, you will got an error; saying that:
Column 'id' is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
Strangely though, MySQL allow this(The OP didn't say what RDBMS he is using), and in this case, it will get an arbitrary value (this is also depends on an option to set), for the id column, and the COUNT in this case would be all the id's count.
But it is not recommended to do so.
Let's say I have table with column 'URL' whrere I store urls like this
one/two
one/two/three
alpha/omega
And I want to get data from database for specific url and if it is not found I remove the last part of url and search again:
Example:
I have url like one/two/three/four/five.
I do search for "one/two/three/four/five"
if not found search again for "one/two/three/four"
if not found search again for "one/two/three"
if not found search again for "one/two"
I would like to have something like:
SELECT * FROM db WHERE url=one/two/three/four/five
UNION
SELECT * FROM db WHERE url=one/two/three/four/five
UNION
SELECT * FROM db WHERE url=one/two/three/four
UNION
SELECT * FROM db WHERE url=one/two/three
UNION
SELECT * FROM db WHERE url=one/two
UNION
SELECT * FROM db WHERE url=one
but I want to stop searching if the row is found.
Is this possible or do I have to do it with separated queries.
Thanks for help.
I thing that this is the most elegant approach to your question. This statement is independent depth path and you don't need to split constant url in subsequent selects:
SELECT
*
FROM
db
WHERE
concat( 'one/two/three/four/five' , '/') like concat( url , '/%')
ORDER BY
LENGTH (url) desc
LIMIT 1
I have tested this query in MySQL, also you can check it! (in MSSQL syntax)
Just replace UNION with UNION ALL and add LIMIT 1 at the end.
P.S. UNION ALL would not make much difference in this particular example, but it is useful to know the difference: http://www.mysqlperformanceblog.com/2007/10/05/union-vs-union-all-performance/
Let's say I have table with column 'URL' whrere I store urls like this
one/two
one/two/three
alpha/omega
Don't do that, it's a horrible design and the proof is the problem you are having running such a simple query; store each URl on a DIFFERENT ROW. Read up on Normalization.
You could use a regexp to search in a single query, but it'll get you all rows:
SELECT * FROM db WHERE url REGEXP "^one((/two)|(/two/three)|(two/three/four)|(/two/three/four/five))?$"
so if you only want the results from the first WHERE you'll have to do multiple queries.
If you really want to have a single query and don't care about a little overhead in the search you could do
SELECT * FROM db WHERE url REGEXP "^one((/two)|(/two/three)|(two/three/four)|(/two/three/four/five))?$" ORDER BY length(url) DESC LIMIT 1
This will get you the first possible result only, but the query inside will have to get all possible results first -> less efficient, but more compact.
I hope this helps!