Get result from mysql orderd by IN clause - mysql

I have the following query
SELECT * FROM invoice WHERE invoice_id IN (13, 15, 9, 27)
My result is:
invoice_id | invoice_number | ...
------------------------------------
9 | 201006003 |
13 | 201006020 |
15 | 201006022 |
27 | 201006035 |
which is the result set I want except that is ordered by the invoice_id (which is an autoincrement value).
Now I want the result in the order I specified in my query (13, 15, ...). Is there a way to achive that?
The background is that I have a DataTable bound to a DataGridView. The user can filter and sort the result but if he want's to print the result I don't use the DataTable for printing because it only contains the most important columns and instead I pull the whole records from the database and pass it to my printing control.
I also tried to extend the existing DataTable with the missing results but that seems to slower than using the IN (...) query.

It's ugly, but you could do:
ORDER BY CASE invoice_id WHEN 13 THEN 0 WHEN 15 THEN 1 WHEN 9 THEN 2 WHEN 27 THEN 3 ELSE 4 END
Actually, there's the FIELD function:
ORDER BY FIELD(invoice_id, 13, 15, 9, 27)
The FIELD function returns the position of the first argument in the list of the rest.
Or, if you're generating it dynamically, you could do:
WHERE invoice_id IN ({list}) ORDER BY FIND_IN_SET(invoice_id, '{list}')

You want the FIELD order by parameter.
SELECT * FROM invoice WHERE invoice_id IN (13, 15, 9, 27) ORDER BY FIELD (invoice_id, 13, 15, 9, 27)

Related

Counting Entities that all share the same first 3 characters

I am able to find the first 3 characters of a name using Substring and getting an output of what they are in a column by using the range between 1, 3. However I am unsure of getting the formal of:
Name | Count
-------------
Jan | 5
Feb | 3
My current code is:
SELECT SUBSTRING(Name, 1, 3)
From Table
Which is giving me the necessary output, should I be using a HAVING Clause involving COUNT?
Kind Regards.
Just add a COUNT(*)... GROUP BY like this:
SELECT SUBSTRING(Name, 1, 3), COUNT(*)
FROM Table
GROUP BY SUBSTRING(Name, 1, 3)

Nested Set Query to retrieve all ancestors of each node

I have a MySQL query that I thought was working fine to retrieve all the ancestors of each node, starting from the top node, down to its immediate node. However when I added a 5th level to the nested set, it broke.
Below are example tables, queries and SQL Fiddles:
Four Level Nested Set:
CREATE TABLE Tree
(title varchar(20) PRIMARY KEY,
`tree` int,
`left` int,
`right` int);
INSERT Tree
VALUES
("Food", 1, 1, 18),
('Fruit', 1, 2, 11),
('Red', 1, 3, 6),
('Cherry', 1, 4, 5),
('Yellow', 1, 7, 10),
('Banana', 1, 8, 9),
('Meat', 1, 12, 17),
('Beef', 1, 13, 14),
('Pork', 1, 15, 16);
The Query:
SELECT t0.title node
,(SELECT GROUP_CONCAT(t2.title)
FROM Tree t2
WHERE t2.left<t0.left AND t2.right>t0.right
ORDER BY t2.left) ancestors
FROM Tree t0
GROUP BY t0.title;
The returned result for node Banana is Food,Fruit,Yellow - Perfect. You can see this here SQL Fiddle - 4 Levels
When I run the same query on the 5 level table below, the 5th level nodes come back in the wrong order:
CREATE TABLE Tree
(title varchar(20) PRIMARY KEY,
`tree` int,
`left` int,
`right` int);
INSERT Tree
VALUES
("Food", 1, 1, 24),
('Fruit', 1, 2, 13),
('Red', 1, 3, 8),
('Cherry', 1, 4, 7),
('Cherry_pie', 1, 5, 6),
('Yellow', 1, 9, 12),
('Banana', 1, 10, 11),
('Meat', 1, 14, 23),
('Beef', 1, 15, 16),
('Pork', 1, 17, 22),
('Bacon', 1, 18, 21),
('Bacon_Sandwich', 1, 19, 20);
The returned result for Bacon_Sandwich is Bacon,Food,Meat,Pork which is not the right order, it should be Food,Meat,Pork,Bacon - You can see this here SQL Fiddle - 5 Levels
I am not sure what is happening because I don't really understand subqueries well enough. Can anyone shed any light on this?
EDIT AFTER INVESTIGATION:
Woah!! Looks like writing all this out and reading up about ordering with GROUP_CONCAT gave me some inspiration.
Adding ORDER BY to the actual GROUP_CONCAT function and removing from the end of the subquery solved the issue. I now receive Food,Meat,Pork,Bacon for the node Bacon_Sandwich
SELECT t0.title node
,(SELECT GROUP_CONCAT(t2.title ORDER BY t2.left)
FROM Tree t2
WHERE t2.left<t0.left AND t2.right>t0.right
) ancestors
FROM Tree t0
GROUP BY t0.title;
I still have no idea why though. Having ORDER BY at the end of the subquery works for 4 levels but not for 5?!?!
If someone could explain what the issue is and why moving the ORDER BY fixes it, I'd be most grateful.
First it's important to understand that you have an implicit GROUP BY
If you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.
To make the point more understandable I'll leave out subqueries and reduce the problem to the banana. Banana is the set [10, 11]. The correct sorted ancestors are those:
SELECT "banana" as node, GROUP_CONCAT(title ORDER by `left`)
FROM Tree WHERE `left` < 10 AND `right` > 11
GROUP BY node;
The ORDER BY must be in GROUP_CONCAT() as you want the aggregation function to sort. ORDER BY outside sorts by the aggregated results (i.e. the result of GROUP_CONCAT()). The fact that it worked until level 4 is just luck. ORDER BY has no effect on an aggregate function. You would get the same results with or without the ORDER BY:
SELECT GROUP_CONCAT(title)
FROM Tree WHERE `left` < 10 AND `right` > 11
/* ORDER BY `left` */
It might help to understand what
SELECT GROUP_CONCAT(title ORDER BY left) FROM Tree WHERE … ORDER BY left does:
Get a selection (WHERE) which results in three rows in an undefined order:
("Food")
("Yellow")
("Fruit")
Aggregate the result into one row (implicit GROUP BY) in order to be able to use an aggregate function:
(("Food","Yellow", "Fruit"))
Fire the aggregate function (GROUP_CONCAT(title, ORDER BY link)) on it. I.e. order by link and then concatenate:
("Food,Fruit,Yellow")
And now finally it sorts that result (ORDER BY). As it's only one row, sorting changes nothing.
("Food,Fruit,Yellow")
You can get the result using JOIN or SUB-QUERY.
Using JOIN:
SELECT t0.title node, GROUP_CONCAT(t2.title ORDER BY t2.left) ancestors
FROM Tree t0
LEFT JOIN Tree t2 ON t2.left < t0.left AND t2.right > t0.right
GROUP BY t0.title;
Check this SQL FIDDLE DEMO
Using SUB-QUERY:
SELECT t0.title node,
(SELECT GROUP_CONCAT(t2.title ORDER BY t2.left)
FROM Tree t2 WHERE t2.left<t0.left AND t2.right>t0.right) ancestors
FROM Tree t0
GROUP BY t0.title;
Check this SQL FIDDLE DEMO
OUTPUT
| NODE | ANCESTORS |
|----------------|-----------------------|
| Bacon | Food,Meat,Pork |
| Bacon_Sandwich | Food,Meat,Pork,Bacon |
| Banana | Food,Fruit,Yellow |
| Beef | Food,Meat |
| Cherry | Food,Fruit,Red |
| Cherry_pie | Food,Fruit,Red,Cherry |
| Food | (null) |
| Fruit | Food |
| Meat | Food |
| Pork | Food,Meat |
| Red | Food,Fruit |
| Yellow | Food,Fruit |
In your sub query you had used ORDER BY after WHERE clause which won't affect the output. By default GROUP_CONCAT() function will orders the output string in ascending order of column value. It won't consider you explicit ORDER BY clause.
If you check your output of first query which returns the data in ascending order of title column. So the returned result for node Banana is Food,Fruit,Yellow.
But in your second result for Bacon_Sandwich is Bacon,Food,Meat,Pork because in ascending order Bacon comes first than Food will come.
If you want to order the result based on left column than you have to specify ORDER BY inside the GROUP_CONCAT() function as above. Check my both queries.
I prefer that you use JOIN instead of SUB-QUERY for improving performance.

MySQL Query: Filtering search results by tags

Suppose that you have a database table that works as a link between two others. In this case, a link between a book and a tag, such as "fiction" or "gardening". If we have the following dataset:
Book_id | tag_id
----------------
1 | 13
1 | 43
1 | 15
2 | 13
2 | 25
What kind of query would you run to say "Find only the books that have links with tags 13, 43, AND 15"? That is, the more tags you add, the smaller the number of books shown.
I hope that makes sense. Thanks in advance for your time and help!!
Try this
SELECT
Book_id
FROM
book_tags
GROUP BY
Book_id
HAVING
SUM( CASE WHEN tag_id IN (13, 43, 15) THEN 1 END ) >= 3
You only need the books that have tags 13, 43, 15 (all of them), it returns book_id = 1 for your result. SUM() >= 3 specifies the total number of tags you are searching, in this case its 3, i.e 13, 43, 15
SQLFIDDLE
If i understand your question correctly... Like this:
SELECT `Book_id` FROM `table` WHERE `tag_id` IN (13,43,25) LIMIT 0, 10;
you can try something like this:
select book_table.* from book_table, link_table where book_table.id = link_table.book_id and link_table.tag_id in (13, 43, 15)
If you have a separate table for your books..

How to Check a Table with column "nid" for multiple values and delete them if they are present

I want to check a Column with title nid in my table in Mysql called node
I have a list of values say 17, 21 , 45, 48
I can get the following code to find the row with nid being equal to 17 using the code below
DELETE FROM node WHERE nid = 17
how should i edit it to check for all values = 17,21,45,48.
Use the IN clause:
DELETE FROM node WHERE nid IN (17, 21, 45, 48)

SQL using count value as offset

This is a follow on from another question i made
I have this query that counts all the records up to a certain point i choose whcih works fine
SELECT count(*)FROM news WHERE id < 18
this query gives me a count of 7
I am now having problems with the offset which would be the result of the above query
I tried using this query
SELECT * FROM `news` ORDER BY id DESC LIMIT 7,1
but i get id number 13 instead of 18
The ids i should have is 2, 7, 10, 11, 12, 13, 16, 18, 19, 20, 21, 22, 23
I have tried using order by id desc in the count query which does give a different result but still wrong id displayed
I dont see a problem here: You order the result by id DESC which means your result is ordered by other way around and 8th value(0..7) is 13.
Try sorting it by ASC then it will give you 18