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 a bit of code that counts the number of rows upon an SQL query and then does another query to grab information on the last four rows. I've used ROWNUM, but it doesn't work.
$newscount = $db->query("
SELECT *
FROM news
");
$counter = $newscount->rowCount();
$grabnewsmsg = $db->query("
SELECT *
FROM news
WHERE ROWNUM >= $counter-4 -- this particular part doesn't owrk
ORDER BY updateno DESC -- an A_I column
");
I've commented the specific areas I'm having problems. The A_I part is fine, since there should be a unique identifier for each row, but ROWNUM just doesn't work despite what I have read on other sites in addition to other questions/answers on SO. It returns an error column rownum does not exist.
I want to get information on solely the last four rows ($query->rowCount()-4), but I can't select via a certain updateno threshhold. If a user deletes a row, the A_I column cannot be appropriately used to determine the row number.
Additionally, I've tried the below:
$grabnewsmsg = $db->query("
SELECT *
FROM news
ORDER BY updateno DESC
LIMIT 0,4
");
And while this gives the desired results, I'm still not sure why ROWNUM doesn't work.
You need to understand the meaning of that AUTO_INC thingy. It is called an unique identifier, and for a reason. "Unique" means no other row should have the same identifier ever.
Yet it has absolutely nothing to do with whatever enumeration. So -
I have an autoincrementing column titled 'updateno' which corresponds to the number of the row.
this is what you are doing wrong.
As a matter of fact, you don't need such a field at all. If you wnat to enumerate your fields - do in on the fly. If you want an identifier - use a conventional name for this - "id"
While for the whatever "rownum" feature you need another mysql operator, namely LIMIT
I got an odd problem. Whenever I add a new entry to the database it gives it a higher id number, but somehow sees it as a lower number and puts it below the older ones.
And when sorting DESC in PHP I also get this order, does anybody know whats going wrong here?
I use $res = mysql_query("SELECT * FROM data ORDER BY 'id' DESC");to sort them, and it gives the same order as in the pic. (Not sure why i'm being downvoted here but ok..)
Pic:
Your query is:
SELECT * FROM data ORDER BY 'id' DESC
You are sorting by the string 'id', note the syntax highlighting when not within quotes. This means you get them in a random order. Remove the '. If you meant to escape the column id you should use back-ticks:
SELECT * FROM data ORDER BY `id` DESC
When you insert data on the tables, it does not mean that the new number (or the highest) always on the lowest row. It randomly inserts the record. The only way you can sort it when you retrieve the rows in by using ORDER BY clause, example
SELECT *
FROM tableName
ORDER BY ID DESC
So assume that ID is numeric. If you're ID is stored as string then you should convert it to numeric,
SELECT *
FROM tableName
ORDER BY CAST(ID AS SIGNED) DESC
UPDATE 1
It should be
$res = mysql_query("SELECT * FROM data ORDER BY `id` DESC");
not
$res = mysql_query("SELECT * FROM data ORDER BY 'id' DESC");
what you have done was you have surrounded the ID with single quote forcing the server to read it as String and not Numeric
When you are not using any ORDER BY clause, the order in PHPMyAdmin is kind of random. Indeed, new rows could take place of old ones.
I am curious how you sort DESC in PHP though.
Don't absolutely trust in GUI web interface because sometimes (not always) it bugs with session management, however you can run SQL Queries using it.
1- To run a SQL query, click on "SQL" on the navigation bar.
2- Enter your SQL in the box provided.
SELECT *
FROM tableName
ORDER BY ID DESC
3- Click "Go".
What SQL query would I use to display the newest entry?
Details:
id is the primary field. I have other fields but that are not related to when they were added.
ORDER BY SomeColumn DESC
LIMIT 1
or
use the MAX() function
Since you didn't give any details about your table it is hard to answer
SELECT * from yourTable ORDER BY `id` DESC LIMIT 1;
Another (better) way would be to have a "date_added" column (date_added TIMESTAMP DEFAULT CURRENT_TIMESTAMP) so you could order by that column descending instead. Dates are more reliable than ID-assignment.
not sure if this is what your looking for but I use mysql_insert_id() after inserting a new row
The auto incremented ID columns are not always the latest records inserted, I've remember really painful experience with this behavior. Conditions where specific, it was mysql 4.1.x at the time and there was almost 1 million records, where 1 out of 3 deleted everiday, and others re inserted in the next 24hours. It made a huge mess when I realize ordering them via ID was not ordering them by age....
Since then, I use a specific column for doing age related sorts, and populating these fields with date = NOW() at each row insert.
Of course it will work to found the latest record as you want, doing an ORDER BY date DESC LIMIT 0,1on your query
SELECT Primary_Key_Field FROM table ORDER BY Primary_Key_Field DESC LIMIT 1
Replace Primary_Key_Field and table obviously :)
I try to use the SQL statement
SELECT * FROM table ORDER BY column
via an PDO-Object in PHP. Problem is, that I always get an error (Call to a member function fetchall() on a non-object - that means, the query did not return a PDO-object) when using the names of all columnname EXCEPT for ID. When I query
SELECT * FROM table ORDER BY ID
it works. ID is the PRIMARY INTEGER KEY, all other columns are TEXT or NUMERIC, neither of them would works with the ORDER BY clause.
Any ideas?
It could be an issue with temporary files as you've suggested in your comment to Frank Heikens's answer.
http://www.sqlite.org/tempfiles.html says:
2.6 Transient Indices
SQLite may make use of transient indices to implement SQL language features such as:
* An ORDER BY or GROUP BY clause
* The DISTINCT keyword in an aggregate query
* Compound SELECT statements joined by UNION, EXCEPT, or INTERSECT
Each transient index is stored in its own temporary file.
If and where files are created is controlled by SQLITE_TEMP_STORE, PRAGMA temp_store and PRAGMA temp_store_directory , see http://www.sqlite.org/pragma.html
Replace your ORDER BY statement with
ORDER BY CAST(COLUMN AS REAL).
It can sort REAL values.