Find rows with same value of a column - mysql

I need to perform a query that find values based on 1 field if the 2nd field is same
Example table:
id, what, why
1, 2, 2
2, 3, 4
3, 3, 2
So I want the results to return what 2 and 3 because they both have why of 2.
But the why (2) is unknown so I only what to know if the combination of what (2 and 3) have the same why value. Makes sense? Any help is appreciated thanks.
Another example maybe clearer
id, building, color
1, house, white
2, garage, red
3, garage, white
I query where building = house and building = garage and results are only given if they have matching color.

SELECT what
FROM YourTable A
WHERE EXISTS(SELECT 1 FROM YourTable
WHERE what <> A.what
AND why = A.why)
Here is a demo of this.

You can do it like this:
select *
from test
where why in (
select why
from test
group by why
having count(*) > 1 -- Use = 2 if you want exactly two items
)
Demo on sqlfiddle.
The inner query finds all such whys that have duplicates, and uses their values to filter the rows of the outer query.

Use a self-join.
SELECT t1.what
FROM Table t1
JOIN Table t2
ON t1.why = t2.why and t1.what != t2.what

Would something like this work:
SELECT GROUP_CONCAT(what) as what, why
FROM YourTable
GROUP BY why
HAVING COUNT(*) > 1
DEMO: http://sqlfiddle.com/#!2/62da8/5

Related

SQL only select the rows which has a max of 1 duplicate

I'm playing with some SQL, and trying to figure out how to select rows with a max of 1 duplicate.
Suppose I have a table looking like this:
CODE:
1234567
1234567
1234567
4567890
4567890
2414351
Then I only want to show those rows with a max of 1 duplicate, so the result should show:
CODE:
4567890
4567890
2414351
Have tried to play with max and count functions but can't seem to produce the right result.
You want codes which either appear only once, or have at most one duplicate:
SELECT t1.CODE
FROM yourTable t1
INNER JOIN
(
SELECT CODE
FROM yourTable
GROUP BY CODE
HAVING COUNT(*) <= 2
) t2
ON t1.CODE = t2.CODE
Output:
Demo here:
Rextester
Use Group by and having
select code from table Group by code having Count(*)=2

using select statement in then clause of case

I'm trying to include select statement in the then of case statement but the output is not as expected. I know there is different method to do this but can it be done the way i'm trying to do.
Using the following example data:
create table example(name varchar(10));
insert into example values
('abc'),('bcd'),('xyz');
I have tried this query (here is the fiddle):
select
case when ((select * from example where name='abc')>=1)
then (select * from example where name='abc')
else (select count(*) from example)
end
from example
But it outputs
3
3
3
Expected output if name='abc' exist
name
abc
if not the count(*)
Thanks in advance
Your subquery in the example is (select * from example where name='abc') which is a result set, not a scalar value. Currently it "works" because it is comparing the only column in the table to the value 1 but if you had more than one column in the table it would error out. Perhaps you intended (select count(*) from example where name='abc')?
Similarly, the THEN clause in a case can only be used to provide a single column value. In order to do this, perhaps you meant the following:
select
case when exists (select * from example where name='abc')
then (select name from example where name='abc')
else (select count(*) from example)
end
from example
But even here you will get three rows and there is no correlation between the rows in example and the result set, so I am not really sure what you're trying to do. I imagine there is a higher purpose though so I will leave it at that.
This should do the trick
select distinct
case when ((select count(name) from example where name='abc')>=1)
then (select * from example where name='abc')
else (select count(*) from example)
end
from example
Let me know if it works.
Point 1:
For the query, you are trying, the from example in the last will cause to loop through all the records and fetch all the records. To restrict that, you have to remove that.
Point 2:
You can't combine multi row select * in a true condition with a single row count(*) in a false condition. You should limit to select a single row.
Example:
select
case when ( select count(*) from example where name='abc' ) >= 1
then ( select * from example where name='abc' limit 1 )
else ( select count(*) from example )
end as name
No need to bother with the complex queries.
SELECT COUNT(*) AS ct
FROM example
GROUP BY name = 'abc'
ORDER BY name = 'abc' DESC
LIMIT 1;
If you really want to use CASE just for the sake of using it:
SELECT
CASE name
WHEN 'abc' THEN 'abc'
ELSE 'others'
END AS name, COUNT(*) AS ct
FROM example
GROUP BY name = 'abc'
ORDER BY name = 'abc' DESC
LIMIT 1;
Try below query, which will work even you enter a second duplicate row as value 'abc'. Mostly above suggested queries will not work as you enter this duplicate row while as per your query condition (>=1), there can be multiple rows for name as 'abc'.
SELECT
CASE WHEN b.cnt>=1
THEN a.name
ELSE (SELECT COUNT(*) FROM EXAMPLE)
END
FROM (SELECT DISTINCT NAME FROM EXAMPLE WHERE NAME='abc') a
JOIN (SELECT NAME,COUNT(*) AS cnt FROM EXAMPLE WHERE NAME='abc') b
ON a.name=b.name

Get list of IDs not present in table

Say I have a list of ids, e.g. (1, 3, 9, 2, 4, 86), and a table with a column id. I want to find all of the numbers in my list where there is not a matching row.
i.e. if the mysql table was like this:
id letter
1 a
2 b
3 c
4 d
5 e
6 f
7 g
And I have the list (1, 3, 9, 2, 4, 86), I want a query that will return (9, 86).
The only thing I can think of, is to build a really big virtual table, like:
select 1 as n union select 3 as n union select 9 as n union ....
Which I can then join against. Is there a better way? I would like to be able to do this all within mysql. As a side note (although I don't expect it to be relevant), my table has around 10,000 rows, and the list I'm using has ~100 numbers in it.
You have to first create a table that will contain the elements of the LIST
i.e (1, 3, 9, 2, 4, 86)
create table t
(
num int
)
insert into t
values
(1),(3),(9),(2),(4),(86)
Now you can use NOT IN
SELECT num
FROM t
WHERE num not in (select id from letter_table);
SQL Fiddle
From Comments.
Edit:
There is a way in which you don't have to create a table
select N from
(select 1 as N
union all
select 3 as N
union all
select 9 as N
union all
select 2 as N
union all
select 4 as N
union all
select 86 as N)t1
where t1.N
not in (select id from letter_table)
Please refer the New SQL Fiddle.
I think OP want's the Edited Part.
P.S. Make Sure table t1 doesn't exists in your DB
Create a table which contains IDs and than you can do it eaasily. See a demonstration here
SELECT
S.id,
'' AS `letter`
FROM sequence S
WHERE S.id NOT IN(SELECT
id
FROM mytable)
SQL Fiddle Demo
Assuming you use the temp table or the UNION method in #Luv's answer, consider replacing the NOT IN with an outer join as it'll likely perform better (test with your actual environment & data, of course):
SELECT num
FROM t
LEFT OUTER JOIN letter_table
ON t.num = letter_table.id
WHERE letter_table.id IS NULL;
If you use the UNION method, replace FROM t with FROM ([big UNION here]) t.

mysql select rows with same ids and preserve their order?

just a quick question:
i have to have one single query that has multiple rows - some rows are identicle - and the order of rows must be preserved in the result -
some idea of what im refering to:
SELECT id,date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY id=1 DESC,id=2 DESC,id=1 DESC,id=3 DESC;
unfortunately mysql result is this:
1,2,3
not 1,2,1,3
it removes the duplicate which i have to have in my result to display in multiple panels on the same webpage -
i really dont want to loop thru each id one by one to get them the way i want to display -
is there a way to actually have one single query that will preserve the order and pull out rows based on request whether its unique or not -
Your query as it stands will never work, because duplicate values in a list of values of an IN clause are ignored. The only way to make this work is by using UNION ALL:
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 2
UNION ALL
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 3;
But to be frank, I suspect your data model so far past screwed it's unusable.
try
SELECT
id,
date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY FIND_IN_SET(id, '1,2,1,3')
Another scrupulous way to answer a suspicious question:
SELECT
items.id,
items.date
FROM
items
JOIN
( SELECT 1 AS id, 1 AS ordering
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 3, 4
) AS auxilary
ON
auxilary.id = items.id
ORDER BY
auxilary.ordering
Another approach (untested, but should give you the idea):
CREATE TEMPORARY TABLE tt (id INT, ai int unsigned auto_increment primary key);
INSERT INTO tt (id) VALUES (1), (2), (1), (3);
SELECT
id,
date
FROM items JOIN tt USING (id)
ORDER BY tt.ai;
keeps the given order.
If you want to include the records with id=1 and the order doesn't matter as long as you get them, you can split your query into two queries, one for (1,2,3) union all the other query for id=1 or just do:
... In (1,2)
Union all
... In (1,3)
Example:
Select * from
(Select case id when 1 then 1 when 2 then 2 as pseudocol, othercolumns
From table where Id in (1,2)
Union all
Select case id when 1 then 3 when 3 then 4 as pseudocol, othercolumns
From table where Id in (1,3)) t order by pseudocol
Instead of doing what you are trying to, just select the unique rows you need. In the frontend code, store each unique row once in a key=>value structure, where key is the item ID and value is whatever data you need about that item.
Once you have that you can use frontend logic to output them in the desired order including duplicates. This will reduce the amount of redundant data you are trying to select.
For example This is not usable code - exact syntax required depends on your scripting language
-- setup a display order
displayOrder= [1,2,1,3];
-- select data from database, order doesn't matter here
SELECT id,date
FROM items
WHERE id IN (displayOrder);
-- cache the results in a key=> value array
arrCachedRows = {};
for (.... each db row returned ...) {
arrCachedRows[id] = date;
}
-- Now output in desired order
for (listIndex in displayOrder) {
-- Make sure the index is cached
if (listIndex exists in arrCachedRow) {
echo arrCachedRows[listIndex ];
}
}
If you must persist in using UNION despite my warnings
If you go against the above recommendation and absolutely MUST have them back in 1 query in that order then add on an additional row which will enforce the row order. See below query where I use variable #subIndex to add an incrementing value as subIndex. This in turn lets you reorder by that and it'll be in the requested order.
SELECT
i.*
FROM (
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 2
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 3
) AS i,(SELECT #subIndex:=0) v
ORDER BY i.subIndex
Or a slightly cleaner version that keeps item selection until the outside and hides the subindex
SELECT
items.*
FROM items
-- initialise variable
INNER JOIN (SELECT #subIndex:=0) v
-- create a meta-table with the ids desired in the order desired
INNER JOIN (
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 2 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 3 AS id
) AS i
ON i.id = items.id
-- order by the subindex from i
ORDER BY i.`subIndex` ASC

Priority condition in 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