MySQL select unique records on two columns - mysql

How do I select rows where two columns are unique?
Given table
id col1 col2
1 a 222
2 b 223
3 c 224
4 d 224
5 b 225
6 e 226
How do remove the duplicates in col1 and the duplicates in col2, to get rows unique to whole table,
So that result is
id col1 col2
1 a 222
6 e 226
Is there a better way than using sub queries?
SELECT * FROM table WHERE id
IN (SELECT id FROM table WHERE col1
IN (SELECT col1 FROM table GROUP BY col1 HAVING(COUNT(col1)=1))
GROUP BY col2 HAVING(COUNT(col2)=1))

This should work using exists:
select *
from yourtable y
where not exists (
select 1
from yourtable y2
where y.id != y2.id
and (y.col1 = y2.col1
or y.col2 = y2.col2))
SQL Fiddle Demo
Here's an alternative solution using an outer join as I've read mysql sometimes doesn't do well with exists:
select *
from yourtable y
left join yourtable y2 on y.id != y2.id
and (y.col1 = y2.col1
or y.col2 = y2.col2)
where y2.id is null;
More Fiddle

You can also do this by aggregating along each dimension:
select t.*
from table t join
(select col1
from table t
group by col1
having count(*) = 1
) t1
on t.col1 = t1.col1 join
(select col2
from table t
group by col2
having count(*) = 1
) t2
on t.col2 = t2.col2;
This method seems like a very direct translation of the user requirements.

Related

mysql select distinct n row that has a certain value

Considering here is my table query:
id name number Code
1 red 1 A
2 red 3 B
3 blue 3 C
4 blue 5 A
5 purple 2 D
6 yellow 3 D
7 yellow 4 C
Now I need to query to get 2 random row such that there is 1 name is red and 1 number is 3, kinda like this:
SELECT * FROM table WHERE name = "red" LIMIT 1 and number = 3 LIMIT 1
So like row 1+3,1+6 or 2 + any other row.
Here is my query:
SELECT * FROM table
group by name,number
having count(name="red") = 1
and count(number=3) = 1
ORDER BY RAND()
LIMIT 2;
However, it seems like it just query the row randomly and not satisfying my requirement. Can anyone show me what is wrong ?
Thank you.
I think that this will do what you want:
select t1.*
from tablename t1
inner join (
select t1.id id1, t2.id id2
from tablename t1 inner join tablename t2
on t2.id > t1.id
and ('red' in (t1.name, t2.name)) + ('3' in (t1.number, t2.number)) = 2
order by rand() limit 1
) t2 on t1.id in (t2.id1, t2.id2)
Note that the row with the highest probability to be returned is id = 2, because it can be combined with any other row of the table.
See the demo.
If you can live with odd formatting...
select x.id x_id
, x.name x_name
, x.number x_number
, x.code x_code
, y.id y_id
, y.name y_name
, y.number y_number
, y.code y_code
from my_table x
join my_table y
on y.id <> x.id
where x.name = 'red'
and y.number = 3
order by rand()
limit 1;
https://www.db-fiddle.com/f/6JmLKq1RwaPrSwS3zx4Qmt/0
Previously, I posted this solution, but it too has some flaws, I think. But TB liked it, so I'll keep it here...
select *
from my_table where name = 'red'
union distinct
select *
from my_table where number = 3
order by rand()
limit 2

How to return duplicates within a table that share the same value within another column in MySQL?

How would I return the ProductNumbers where the Number is duplicated when it has the same year ?
This is all within the same table.
in this example below, I would expect ProductNumber 123 and 456 to be returned.
Explain reasoning if possible, thank you!
ProductNumber Numb Year
123 45 1
456 45 1
789 45 2
109 54 2
Here's one option using exists:
select *
from yourtable t
where exists (
select 1
from yourtable t2
where t.productnumber != t2.productnumber
and t.numb = t2.numb
and t.year = t2.year
)
Using exists, we check to see if there are other records in the same table whose productnumber is different, but have the same numb and year values.
You can use EXISTS() :
SELECT *
FROM Table T1
WHERE EXISTS
(
SELECT 1
FROM
(SELECT Numb ,Year
FROM Table
GROUP BY Numb
,Year
HAVING COUNT(1)>1
) T2
WHERE T1.Numb = T2.Tumb
AND T1.Year = T2.Year
)
You can also use INNER JOIN
SELECT t1.ProductNumber
FROM Products t1
INNER JOIN Products as t2
ON t1.ProductNumber != t2.ProductNumber
AND t1.Numb = t2.Numb
AND t1.Year = t2.Year

Filter Results in SQL

Suppose I have a table
id value
------ ---------
10 123
10 422
11 441
11 986
12 674
13 648
I need a query which will return only those id's which have 2 or more values associated with them. So, in that case it will only return ID 10 & 11, but i need al the records.
so the result looks like:
id value
------ ---------
10 123
10 422
11 441
11 986
Thank you.
select a2.*
from MyTable a2
inner join
(
select a1.id
from MyTable a1
group by a1.id
having count(*) > 1
) a3
on a3.id = a2.id
Assuming a UNIQUE KEY can be formed on (id,value)...
SELECT DISTINCT x.*
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.value <> x.value
If a UNIQUE KEY cannot be formed on (id,value), then this isn't really a table in a strict RDBMS sense.
You can use this query :
SELECT * from table where id in
( SELECT id FROM table group by id having count(id) > 1 )
With mysql 8+ or mariadb 10.2+, you would use the count window function:
select id, value
from (
select id, value, count(id) over (partition by id) as num_values
from sometable
) foo
where num_values > 1;

Show only differences between two rows in mysql

Having a (MySQL) audit table containing rows that are similar, is it possible to view only those columns that have different values?
For example, a table containing four columns where column key is primary key, and column id is the identifier to match rows:
key id col1 col2
1 123 B C
2 123 A C
3 456 B C
4 789 B A
5 789 B B
6 987 A C
In the example above I need the query to return only row 1, 2, 4, and 5 as they have matching id, and differing values in col1 and col2, ie B,A and B,A.
key id col1 col2
1 123 B
2 123 A
4 789 A
5 789 B
I know it might not be very efficient solution, but gives what you want. HERE try this:
SELECT A.ID, (CASE A.col1 WHEN B.col1 THEN NULL ELSE B.col1 END), (CASE A.col2 WHEN B.col2 THEN NULL ELSE B.col2 END) FROM tblName A
FULL OUTER JOIN tblName B
ON
A.ID=B.ID
WHERE
(A.col1=B.col1 AND A.Col2<>B.Col2)
OR
(A.col2<>B.col2 AND A.Col1=B.Col1)
INNER JOIN should give same result
This is a bit contrived, in the sense that adding more rows will give very different results - but anyway...
SELECT x.my_key
, x.id
, IF(y.col1=x.col1,'',x.col1) col1
, IF(y.col2=x.col2,'',x.col2) col2
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.my_key <> x.my_key
WHERE (y.col1 <> x.col1 OR y.col2 <> x.col2)
ORDER
BY my_key;
Thanks for all responses which guided me.
Using your suggestions I made the sql like this:
SELECT
T1.KEY,
T1.ID,
CASE T2.COL1_DISTINCT_VALUES WHEN 1 THEN NULL ELSE T1.COL1 END AS COL1,
CASE T2.COL2_DISTINCT_VALUES WHEN 1 THEN NULL ELSE T1.COL2 END AS COL2
FROM
TAB1 T1
INNER JOIN
(
SELECT
ID,
COUNT(DISTINCT COL1) AS COL1_DISTINCT_VALUES,
COUNT(DISTINCT COL2) AS COL2_DISTINCT_VALUES
FROM
TAB1
GROUP BY
ID
) T2
ON T1.ID=T2.ID
WHERE
T2.COL1_DISTINCT_VALUES > 1
OR T2.COL2_DISTINCT_VALUES > 1
ORDER BY
KEY,ID;

Left outer join to two tables

I have a table 1 with a one to many relationship to table 2.
Table 1 also has a one to many relationship with table 3
I want to combine the results of the join but all im getting is repeated values
Here is the structure:
table 1
reportnumber
1
2
3
table 2
reportnumber col1
1 a
1 b
2 c
3 a
table 3
reportnumber col2
1 x
1 y
1 z
2 w
expected result set
reportnumber col1 col2
1 a x
1 b y
1 z
2 c w
3 a
I'm sure this is possible with a left outer join but i just cant get the syntax right
Any clues?
This is what im trying
select * from table1 a
left outer join table2 b on a.reportnumber=b.reportnumber
left outer join table3 on a.reportnumer=c.reportnumber
But the results look like this
reportnumber col1 col2
1 a x
1 a y
1 a z
1 b x
1 b y
1 b z
...
This isn't easy in MySQL, but you can do it with variables. This has little to do with a join. Or, it has a lot to do with join, but you don't have the right join keys and you don't have full outer join.
The solution is to enumerate the rows from each table with the data columns. Then aggregate using the enumeration and reportnumber:
select reportnumber, max(col1) as col1, max(col2) as col2
from ((select t2.reportnumber, col1, null as col2, #rn2 := #rn2 + 1 as rn
from table2 t2 cross join
(select #rn2 := 0) const
) union all
(select t3.reportnumber, null, t3.col2, #rn3 := #rn3 + 1 as rn
from table3 t3 cross join
(select #rn3 := 0) const
)
) t
group by reportnumber, rn;