Find values C that exists in every B from A - mysql

I have a table with 3 fields ID,A,B.
I want to find every B that occurs in every A from some ID
For example:
ID | A | B |
----------------------
1 | 10 | 22 |
1 | 10 | 24 |
1 | 11 | 22 |
| | |
2 | 12 | 31 |
2 | 13 | 33 |
| | |
3 | 14 | 34 |
Should return:
ID | B |
-------------
1 | 22 |
3 | 34 |
because for every different value of A from ID 1 , B = 22 occurs.
Same for id 3.
Any idea how I can do it?
Thank you in advance.

Sql Fiddle Demo
You can use this query to validate what rows your need.
SELECT T1.B, T1.ID, COUNT(DISTINCT A), T2.total_id_a
FROM Table1 T1
JOIN (SELECT ID , COUNT(DISTINCT A) as total_id_a
FROM Table1
GROUP BY ID) T2
ON T1.ID = T2.ID
GROUP BY B, ID;
But this will provide the result you want.
SELECT ID, B
FROM (
SELECT T1.ID, T1.B, COUNT(DISTINCT A), T2.total_id_a
FROM Table1 T1
JOIN (SELECT ID , COUNT(DISTINCT A) as total_id_a
FROM Table1
GROUP BY ID) T2
ON T1.ID = T2.ID
GROUP BY B, ID
HAVING COUNT(DISTINCT A) = T2.total_id_a
) T
OUTPUT
| ID | B |
|----|----|
| 1 | 22 |
| 3 | 34 |

# For case ID=1 and B=22 for example above
SELECT DISTINCT t1.ID, t1.B
FROM yourtable AS t1
INNER JOIN yourtable AS t2
ON t2.B = t1.B
AND t2.A <> t1.A
AND t1.ID = t2.ID
UNION
# For case ID=3 and B=34 for example above
SELECT DISTINCT t1.ID, t1.B
FROM yourtable AS t1
INNER JOIN yourtable AS t2
ON t2.B = t1.B
AND t2.A = t1.A
AND t1.ID = t2.ID
LEFT JOIN yourtable AS t3
ON t3.ID = t1.ID
AND (t3.A <> t1.A OR t3.B <> t1.B)
WHERE t3.ID IS NULL

Related

How can I get a record which has two corresponding columns which are multiple in SQL?

For example, I have two tables:
ID | Name
------------
1 | test 1
2 | test 2
ID2| ID | Age
--------------
1 | 1 | 18
2 | 1 | 18
3 | 1 | 19
4 | 2 | 18
5 | 2 | 19
I want to have all records that have columns which are multiple in name with age but I don't know how to do that.
I want an output like this:
Name | Age
--------------------
test 1 | 18
test 1 | 18
Can anyone help me?
Try following query:
Select t1.*, t2.*
from table1 t1
join table2 t2
on t1.id = t2.id
join (select id, age
from table2
group by id, age
having count(*) > 1
) t3
on t1.id = t2.id and t2.id = t3.id and t2.age = t3.age
Use exists:
select t.*
from t
where exists (select 1
from t t2
where t2.name = t.name and t2.age = t.age and
t2.id <> t.id
);
With an index on (name, age, id), this should be the fastest approach.
You can also use an IN on tupples.
And a GROUP BY can be combined with a HAVING to only get those that have duplicate (name, age).
SELECT t1.Name, t2.Age
FROM YourTable2 t2
LEFT JOIN YourTable1 t1 ON t1.ID = t2.ID
WHERE (t2.ID, t2.Age) IN (
SELECT ID, Age
FROM YourTable2
GROUP BY ID, Age
HAVING COUNT(*) > 1
);

mysql max function in group by

I try to get items in mysql, I need to remove from result t2 items, that have status_id = 17 and status_change_date less then max date_created for this t1
t1:
-----
|id |
-----
| 1 |
-----
t2:
---------------------------------------------------------------
| id | t1_id | status_change_date | status_id | date_created |
---------------------------------------------------------------
| 1 | 1 | 2006-02-12 | 17 | 2004-02-12 |
| 2 | 1 | 2006-02-12 | 17 | 2006-02-12 |
| 3 | 1 | 2010-02-12 | 17 | 2007-02-12 |
| 4 | 1 | 2006-02-12 | 17 | 2008-02-12 |
---------------------------------------------------------------
so in result I should to see only item with id 3, cause only in this case status_change_date is after max date_created for items, that have reference to t1 item with id 1
SELECT t1.*, t2.*
FROM t1 JOIN
(
SELECT t2.*,
MAX(date_created) AS max_date FROM t2 WHERE t2.id NOT IN
(
SELECT t2.id FROM t2
WHERE
status_id = 17
and status_change_date < max_date
)
GROUP BY t1_id
) t2 ON t1.id = t2.t1_id
But i got error cause sql don't know what is max_date. How I should to get this value?
From what I understand of joins, your 'from' should come AFTER your 'select' statements.
select *,left(name,1) as L1 from artists order by name limit 5;
You can store value to temporary variable and use to compare:
SELECT t1.*, t2.*
FROM t1 JOIN
(
SELECT t2.*,
#max_date:=MAX(date_created) AS max_date FROM t2 WHERE t2.id NOT IN
(
SELECT t2.id FROM t2
WHERE
status_id = 17
and status_change_date < #max_date
)
GROUP BY t1_id
) t2 ON t1.id = t2.t1_id
You can go with anyone of the following queries:
SELECT t1.*,
t2.*,
Max(date_created) AS max_date
FROM t1
JOIN t2
ON t1.id = t2.t1_id
WHERE status_id = 17
GROUP BY status_change_date
HAVING status_change_date > max_date;
Or
SELECT t1.*,
t2.*
FROM t1
JOIN t2
ON t1.id = t2.t1_id
WHERE status_id = 17
AND status_change_date > (SELECT Max(date_created)
FROM t2);

SQL to group a similar table by name

I have two similar tables
Table 1
| id | name | amount|
| 2 | Mike | 1000 |
| 3 | Dave | 2500 |
Table 2
| id | name | amount|
| 2 | Mike | 1200 |
| 4 | James| 2500 |
I want to query the tables to get a result like this:
| id | name | amount_table1| amount_table2|
| 2 | Mike | 1000 | 1200 |
| 3 | Dave | 2500 | |
| 4 | james| | 2500 |
UNION ALL the tables. Do GROUP BY to get one row per id/name combo.
select id, name, sum(amount1), sum(amount2)
from
(
select id, name, amount as amount1, null as amount2 from table1
union all
select id, name, null, amount from table2
) dt
group by id, name
You need to do union with left and right join
select a.id , a.name , a.amount amount_table1,b.amount amount_table2 from table1 a left join table2 b on (a.id=b.id)
union
select b.id , b.name ,a.amount,b.amount from table1 a right join table2 b on (a.id=b.id)
MySql doesn't support FULL OUTER JOIN.
But it supports LEFT & RIGHT joins and UNION.
select
t1.id, t1.name, t1.amount as amount_table1, t2.amount as amount_table2
from Table1 t1
left join Table2 t2 on t1.id = t2.id
union all
select t2.id, t2.name, t1.amount, t2.amount
from Table2 t2
left join Table1 t1 on t2.id = t1.id
where t1.id is null
The first select will get those only in Table1 and those in both.
The second select will get those only in Table2.
And the UNION glues those resultsets together.
If this were for a database that supports FULL JOIN then it would be simplified to:
select
coalesce(t1.id, t2.id) as id,
coalesce(t1.name, t2.name) as name,
t1.amount as amount_table1,
t2.amount as amount_table2
from Table1 t1
full join Table2 t2 on t1.id = t2.id

Combine two selects into one

I have a couple of tables. I'll try to make it simple: Table_1 ID is unique. Table_2 ID is not unique. The table_2 stores the ID from a row on table_1 and a value, resulting in, for instance, this:
table_1
ID | A
------
1 | a
2 | b
3 | c
table_2
ID | B
------
1 | x
1 | y
3 | z
I want to count how many of each ID is there on table_2, so I do
select t1.id, count(*)
from table_1 t1
group by t1.id
id | count
----------
1 | 2
3 | 1
And I want to list every row on table_2 and its corresponding value on table_1.A, so I do
select t1.id, t1.A, t2.B
from table_2 t2
left join table_1 t1
on t1.id = t2.id
ID | A | B
----------
1 | a | x
1 | a | y
3 | c | z
Is there a way to combine those 2 selections into one, to get a result like this?
ID | A | B | count
------------------
1 | a | x | 2
1 | a | y | 2
3 | c | z | 1
You can combine the results by joining the count result.
Fiddle with sample data
select t1.id, t1.A, t2.B, x.cnt as count
from t2
left join t1
on t1.id = t2.id
join (select t2.id, count(*) as cnt
from t2
group by t2.id
) x
on x.id = t1.id
Besides the generic Derived Table solution posted by #vkp you might also utilize a Scalar Subquery to return a single value:
select t1.id, t1.A, t2.B,
(select count(*)
from t2 as x
where x.id = t2.id
) cnt
from t2
http://www.sqlfiddle.com/#!9/07a3a/6

Re-mapping rows in MYSQL

I have table like this:
uid | fid | value
1 | 1 | nameOf1
1 | 2 | surnameOf1
1 | 3 | countryOf1
2 | 1 | nameOf2
2 | 2 | surnameOf2
And need to transform/select it like this:
uid | name | surname | country
1 | nameOf1 | surnameOf1 | countryOf1
2 | nameOf2 | surnameOf2 | ..
Try something like:
SELECT t1.uid, t2.value AS name, t3.value AS surname, t4.value AS country FROM table t1
LEFT JOIN table t2 on t2.uid = t1.uid AND t2.fid = 1
LEFT JOIN table t3 on t3.uid = t1.uid AND t3.fid = 2
LEFT JOIN table t4 on t4.uid = t1.uid AND t4.fid = 3
GROUP BY t1.uid;
or:
SELECT DISTINCT t1.uid, t2.value AS name, t3.value AS surname, t4.value AS country FROM table t1
LEFT JOIN table t2 on t2.uid = t1.uid AND t2.fid = 1
LEFT JOIN table t3 on t3.uid = t1.uid AND t3.fid = 2
LEFT JOIN table t4 on t4.uid = t1.uid AND t4.fid = 3;
It's a little complex, but you could do something like:
select distinct uid as uid,
(select value from dataTable as table1
where table1.uid=dataTable.uid and fid = 1) as name,
(select value from dataTable as table2
where table2.uid=dataTable.uid and fid = 2) as surname,
(select value from dataTable as table3
where table3.uid=dataTable.uid and fid = 3) as country
from dataTable group by uid
SQLFiddle