Priority condition in MySQL - mysql

I like to select a row by priority on MySQL. I don't know if it is possible, but see the example below:
id name
1 John
2 Doe
3 Mary
4 Jhonny
Supposing that I don't know the ID, but by a specific reason, I need select by priority: 3, 2, 1... On this case, the MARY will be selected (#3). But if I will need select by order 5, 2, 1, the DOE will be selected (#2).
Ok, I can do it by using IN(5, 2, 1). The problem is that if I don't have any results (like IN(5, 6, 7)), I need at least one row (don't matter what).
For instance: select 5, 6, 7... nothing found... then, select the first found (like JOHN).
It's possible?
Bye!
Edit: I just thought of this solution, but I do not know how fast it should be, but it works well. Accept a response that has a better benchmark for nobody lose.
ORDER BY FIND_IN_SET(`id`, '5,6,7') DESC

SELECT *
FROM your_table
ORDER BY
(CASE id
WHEN 5 THEN 1
WHEN 6 THEN 2
WHEN 7 THEN 3
ELSE id+10
END)
LIMIT $some_limit;
The trick is make use on order by
So, the matching id of 5 will give the priority 1,
matching id of 6 will give the priority 2,
matching id of 7 will give the priority 3,
otherwise, least id will return
assuming you are using unsigned for id

At first you need remap ID column to order column, let's do it with help of special union (first column is you id, second is order instruction):
SELECT 5 as ID, 1 as ord
UNION ALL SELECT 2 , 2
UNION ALL SELECT 1 , 3
Is it clear?
if so - let's do full example:
SELECT m.* from MyTable m
LEFT OUTER JOIN (
SELECT 5 as ID, 1 as ord
UNION ALL SELECT 2 , 2
UNION ALL SELECT 1 , 3
) o ON m.ID = o.ID
ORDER BY COALESCE(o.ord, 100)

SELECT U.username, id
FROM tbluser U
ORDER BY FIND_IN_SET(`username`, 'Tester') DESC, username ACS
This priorities (gives first) the "Tester" and then sort it A-Z

Related

How can I easily INSERT data in a table from multiple columns from another table?

I want to take all phone numbers from the companies table, and put that in a dedicated phone numbers table, is there an easy way to do this using (if possible) only one query?
example data from the companies table (tel3 and tel4 could have phone numbers):
id
tel
tel2
tel3
tel4
1
32772373636
32724522341
2
32783675626
3
32968381949
expected example output in phonenrs table:
id
company_id
phonenr
1
1
32772373636
2
1
32724522341
3
2
32783675626
4
3
32968381949
You could use an insert-select statement from a query that union alls the phone numbers:
INSERT INTO numbers (company_id, phonenr)
SELECT it, tel FROM numbers WHERE tel IS NOT NULL
UNION ALL
SELECT it, tel2 FROM numbers WHERE tel2 IS NOT NULL
UNION ALL
SELECT it, tel3 FROM numbers WHERE tel3 IS NOT NULL
UNION ALL
SELECT it, tel4 FROM numbers WHERE tel4 IS NOT NULL
To get the EXACT match to your intended output, first we need to add a row id for your new id column. Then,to make sure the sorting precedence of tel > tel2 > tel3 > tel4, we can perform a trick to do so. Here is the code written and tested in workbench:
select #row_id:=#row_id+1 as id, id as company_id,trim(leading '0' from phone ) as phonenr
from
(select id,ifnull(concat('000',tel),1) as phone from companies
union all
select id,ifnull(concat('00',tel2),1) from companies
union all
select id,ifnull(concat('0',tel3),1) from companies
union all
select id,ifnull(tel4,1) from companies
) t1,
(select #row_id:=0) t2
where phone !=1
order by company_id,phone
;
-- result:
# id, company_id, phonenr
1, 1, 32772373636
2, 1, 32724522341
3, 2, 32783675626
4, 3, 32968381949
As you can see, by adding different number of leading zero to the phone,we can manipulate the sorting precedence. Without it, I got 32724522341 instead of 32772373636 for the first line.

How to return records in MySQL with "custom" ordering

I'm trying to return records based on their IDs in MySQL without ordering.
But when I run the query it will order them from the lowest ID number to highest one.
SELECT * FROM events WHERE id=11 or id=4 or id=9 or id=5
The result will like these: 4,5,9,11
How can return like this : 11,4,9,5
Try using ORDER BY FIELD (id, ...):
SELECT *
FROM events
WHERE id IN (4, 5, 9, 11)
ORDER BY FIELD (id, 11, 4, 9, 5)
Demo
As to why your current query is showing the 4,5,9,11 order, even without your using an explicit ORDER BY clause, one explanation is that the id column is the clustered primary key for your table. In that case, the data would actually be stored in this order on disk, and when selecting, this would be the natural order returned.
Edit:
On other database vendors, which don't support FIELD, we can order using a CASE expression:
SELECT *
FROM events
WHERE id IN (4, 5, 9, 11)
ORDER BY
CASE WHEN id = 11 THEN 1
WHEN id = 4 THEN 2
WHEN id = 9 THEN 3
WHEN id = 5 THEN 4
ELSE 5 END;
I believe you want to use FIELD()
SELECT * FROM events WHERE id=11 or id=4 or id=9 or id=5
ORDER BY FIELD(id, 11,4,8,5)
Or the more ANSI SQL method (works also for other databases vendors)
SELECT
events.*
FROM (
SELECT
11 AS id
, 1 AS position
UNION ALL
SELECT
4 AS id
, 2 AS position
UNION ALL
SELECT
8 AS id
, 3 AS position
UNION ALL
SELECT
5 AS id
, 4 AS position
) AS sorting
INNER JOIN
events
ON
sorting.id = events.id
ORDER BY
sorting.position ASC
Or the better ANSI SQL like it should (works also for other databases vendors)
SELECT * FROM events WHERE id=11 or id=4 or id=9 or id=5
ORDER BY CASE WHEN id = 11 THEN 1
WHEN id = 4 THEN 2
WHEN id = 8 THEN 3
WHEN id = 5 THEN 4
ELSE 5
END
** Updateded
As you want fixed order, you can use ORDER BY FIELD :
SELECT * FROM events
WHERE id IN (4, 5, 9, 11)
ORDER BY FIELD (id, 11, 4, 9, 5)

Find duplicates where entries within id are duplicate

id class count day
1 2 5 5
2 2 4 5
3 2 4 5
3 2 4 5
4 2 5 3
4 1 5 3
4 2 5 3
So I have a query for finding all duplicates based on multiple columns, however, this also returns id's where not all entries are duplicate.
In above example, the query should only show/count id 3, as all the entries with id 3 are duplicate. Even though id 4 also has a duplicate, it should not show as it has another entry that is unique.
Any idea how to do this?
If you need rows with id where there is no row with the same id and unique row values then use NOT IN and HAVING
select *
from your_table t1
where t1.id not in(
select id
from your_table
group by id, class, count, day
having count(*) = 1
)
You can use this query : http://sqlfiddle.com/#!9/1a2536/8
select id
from test
group by id
having count(distinct id,class,count,day) = 1 and count(*)>1
you group each rows by id and count how many different row the group has, if the distinct total is 1 and the total row is > 1 , there is only duplicate rows for this id.
It's quite easy, a quick note it's a very bad idea to name a column count :
SELECT id, class, `count`,day, COUNT(*)
FROM myTable
GROUP BY id, class, `count`,day
HAVING COUNT(*) > 1
edit : I misread the question so here is my solution :
SELECT test.id, test.class, test.count, test.day , count(*), t.countID
FROM (SELECT id, Count(id) AS countID FROM test GROUP BY id ) t
INNER JOIN test on t.id = test.id
GROUP BY test.id, test.class, test.count, test.day
HAVING Count(*) > 1 AND t.countID = count(*)
I came up with this :
SELECT *
FROM (SELECT id,
Count(id) AS matched
FROM test
GROUP BY id,
class,
count,
day) t
GROUP BY id , matched
HAVING Count(id) = 1
AND matched >= 2
There is maybe a more efficient way to do it but it is easier to understand this way, first we group by every column to find duplicate data. Then the first part of the having eliminates the entries that actually have different variants by id and then we keep only the lines that actually only have duplicates.
Edit : compatible with "only_full_group_by" mode

GROUP_CONCAT on two tables

I have two tables, with independent ids (can't be connected via joins), I want to query and get a GROUP_CONCAT of both columns.
Example: table "a" has ids: 1, 2, 3. table "b" has the ids: 10, 11.
End result should be: 1, 2, 3, 10, 11
I have tried a few queries:
SELECT CONCAT_WS(',', GROUP_CONCAT(a.id), GROUP_CONCAT(b.id)) AS combined FROM a, b
SELECT GROUP_CONCAT(a.id, b.id) AS combined FROM a, b
These queries are returning me duplicate results though 8as in, all results from a twice and all results from b twice as well)
Try union all:
select group_concat(ab.id) as ids
from ((select id from a
) union all
(select id from b
)
) ab;
Your queries are doing cross join's between the tables, so data after the cross join is:
a.id b.id
1 10
1 11
2 10
2 11
3 10
3 11
After the union all, the data is:
ab.id
1
2
3
10
11
GROUP_CONCAT(DISTINCT [])
will help
https://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat
The following query will generate that you want.
You can play with the table_position dynamic column for deciding which table goes first.
Select group_concat(id order by table_position) from
(
select id, 1 as table_position from a
union all
select id, 2 as table_position from b
)
If you want duplicates, use union all. If you don't want duplicates, use union.
In either case, the query you need is as follows:
select group_concat(id) from
(select id from a
union
select id from b) as ids;

Adding one extra row to the result of MySQL select query

I have a MySQL table like this
id Name count
1 ABC 1
2 CDF 3
3 FGH 4
using simply select query I get the values as
1 ABC 1
2 CDF 3
3 FGH 4
How I can get the result like this
1 ABC 1
2 CDF 3
3 FGH 4
4 NULL 0
You can see Last row. When Records are finished an extra row in this format
last_id+1, Null ,0 should be added. You can see above. Even I have no such row in my original table. There may be N rows not fixed 3,4
The answer is very simple
select (select max(id) from mytable)+1 as id, NULL as Name, 0 as count union all select id,Name,count from mytable;
This looks a little messy but it should work.
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM
(
SELECT 1 as ID
UNION
SELECT 2 as ID
UNION
SELECT 3 as ID
UNION
SELECT 4 as ID
) a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID IN (1,2,3,4)
UPDATE 1
You could simply generate a table that have 1 column preferably with name (ID) that has records maybe up 10,000 or more. Then you could simply join it with your table that has the original record. For Example, assuming that you have a table named DummyRecord with 1 column and has 10,000 rows on it
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM DummyRecord a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID >= 1 AND
a.ID <= 4
that's it. Or if you want to have from 10 to 100, then you could use this condition
...
WHERE a.ID >= 10 AND
a.ID <= 100
To clarify this is how one can append an extra row to the result set
select * from table union select 123 as id,'abc' as name
results
id | name
------------
*** | ***
*** | ***
123 | abc
Simply use mysql ROLLUP.
SELECT * FROM your_table
GROUP BY Name WITH ROLLUP;
select
x.id,
t.name,
ifnull(t.count, 0) as count
from
(SELECT 1 AS id
-- Part of the query below, you will need to generate dynamically,
-- just as you would otherwise need to generate 'in (1,2,3,4)'
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
) x
LEFT JOIN YourTable t
ON t.id = x.id
If the id does not exist in the table you're selecting from, you'll need to LEFT JOIN against a list of every id you want returned - this way, it will return the null values for ones that don't exist and the true values for those that do.
I would suggest creating a numbers table that is a single-columned table filled with numbers:
CREATE TABLE `numbers` (
id int(11) unsigned NOT NULL
);
And then inserting a large amount of numbers, starting at 1 and going up to what you think the highest id you'll ever see plus a thousand or so. Maybe go from 1 to 1000000 to be on the safe side. Regardless, you just need to make sure it's more-than-high enough to cover any possible id you'll run into.
After that, your query can look like:
SELECT n.id, a.*
FROM
`numbers` n
LEFT JOIN table t
ON t.id = n.id
WHERE n.id IN (1,2,3,4);
This solution will allow for a dynamically growing list of ids without the need for a sub-query with a list of unions; though, the other solutions provided will equally work for a small known list too (and could also be dynamically generated).