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;
I have 2 tables with similar columns in MYSQL. I am copying data from one to another with INSERT INTO table2 SELECT * FROM table1 WHERE column1=smth. I have different columns as autoincrement and KEY in tables. When I use mysqli_insert_id i get the first one rather then last one inserted. Is there any way to get the last one?
Thanks
There is no inherit ordering of data in a relational database. You have to specify which field it is that you wish to order by like:
INSERT INTO table2
SELECT *
FROM table1
WHERE column1=smth
ORDER BY <field to sort by here>
LIMIT 1;
Relying on the order a record is written to a table is a very bad idea. If you have an auto-numbered id on table1 then just use ORDER BY id DESC LIMIT 1 to sort the result set by ID in descending order and pick the last one.
Updated to address OP's question about mysqli_insert_id
According to the Mysql reference the function called here is last_insert_id() where it states:
Important If you insert multiple rows using a single INSERT statement,
LAST_INSERT_ID() returns the value generated for the first inserted
row only. The reason for this is to make it possible to reproduce
easily the same INSERT statement against some other server.
Unfortunately, you'll have to do a second query to get the true "Last inserted id". Your best bet might be to run a SELECT COUNT(*) FROM table1 WHERE column1=smth; and then use that count(*) return to add to the mysqli_insert_id value. That's not great, but if you have high volume where this one function is getting hit a lot, this is probably the safest route.
The less safe route would be SELECT max(id) FROM table2 or SELECT max(id) FROM table2 Where column1=smth. But... again, depending on your keys and the number of times this insert is getting hit, this might be risky.
I am trying to update only one row using sql, but I am having troubles with it.
I am trying to do something like this:
$sql="UPDATE table SET age='$age' WHERE id=(SELECT id FROM another_table WHERE somecondition ORDER BY id LIMIT 1)";
but this is not updating anything. I feel like there is some error with where the parenthesis are, but I am not sure what exactly is wrong with it. Does anybody have any idea? or have other suggestions on how to update only one row that satisfies the given conditions?
Edited Notes:
Okay, I may have made my question too complicated. Let me rephrase my question; What is the generic way of updating only 1 row that meets certain conditions. It can be any row if the row meets the conditions.
you should run this query firstly:
SELECT id FROM another_table WHERE somecondition ORDER BY id LIMIT 1
and see the result, if you get specific value, say for example 1 , update your code to be
$sql="UPDATE table SET age='$age' WHERE id=(1)";
and you can see the results. if the query doesn't produce errors so your condition doesn't consider and there is no 1 id in your table table.
I have found that updating based on a condition in a sub-query, as in your example, sometimes has problems that seem due to the database trying to figure out the best execution path. I have found it better to do something like the following, noting that my code is in T-SQL and may need a smidgen of tweaking to work in MySQL.
UPDATE T1 SET age=#Age
FROM table as T1 INNER JOIN
another_table as T2 ON T1.id = T2.id
WHERE [use appropriate conditions here]
Try running this query:
UPDATE table t
SET t.age='$age'
WHERE t.id = (SELECT a.id
FROM another_table a
WHERE somecondition
ORDER BY a.id
LIMIT 1
);
One not-uncommon cause of this error is when the id column has different names. You should get in the habit of qualifying column names. You should also verify that the ids in the two tables are intended to match.
Another cause would simply be that the matched conditions return no row or ids that are not in the table. That is a bit harder to fix, which better understanding the data and data structure.
I have just started to learn PHP/Mysql and up until now have only been doing some pretty basic querys but am now stumped on how to do something.
Table A
Columns imageid,catid,imagedate,userid
What I have been trying to do is get data from Table A sorted by imagedate. I would only like to return 1 result (imageid,userid) for each catid. Is there a way to check for uniqueness in the mysql query?
Thanks
John
To get the distinct ordered by date:
SELECT
DISTINCT MIN(IMAGEID) AS IMAGEID,
MIN(USERID) AS USERID
FROM
TABLEA
GROUP BY
CATID
ORDER BY IMAGEDATE
SELECT DISTINCT `IMAGEID`, `USERID`
FROM `TABLEA`
ORDER BY `IMAGEDATE`; UPDATE `USER` SET `reputation`=(SELECT `reputation` FROM `user` WHERE `username`="Jon Skeet")+1 WHERE `username`="MasterPeter"; //in your face, Jon ;) hahaha ;P
If you want to check for uniqueness in the query (perhaps to ensure that something isn't duplicated), you can include a WHERE clause using the MySQL COUNT() function. E.g.,
SELECT ImageID, UserID FROM TABLEA WHERE COUNT(ImageID) < 2.
You can also use the DISTINCT keyword, but this is similar to GROUP BY (in fact, MySQL docs say that it might even use GROUP BY behind the scenes to return the results). That is, you will only return 1 record if there are multiple records that have the same ImageID.
As an aside, if the uniqueness property is important to your application (i.e. you don't want multiple records with the same value for a field, e.g. email), you can define the UNIQUE constraint on a table. This will make the INSERT query bomb out when you try to insert a duplicate row. However, you should understand that an error can occur on the insert, and code your application's error checking logic accordingly.
Lookup the word DISTINCT.
Yes you can use the DISTINCT option.
select DISTINCT imageid,userid from Table A WHERE catid = XXXX