Find the minimum discerning subset in MySQL - mysql

I have a t table, with let's say field1 and field2. field1 is an identifier of sets and field2 contains the member of the sets. Now, the question is, how to find out the smallest amount of field2 values that will uniquely find a given field1?
Sample data
+------+------+
|field1|field2|
| 1 | A |
| 1 | B |
| 1 | C |
| 2 | A |
| 2 | C |
| 2 | D |
| 3 | B |
| 3 | D |
+------+------+
I am looking for a query which given 1 as an argument returns (A,B), for 2 returns (A,D) or (C,D) both are good and for 3 returns (B,D). If the generic case is too hard then let's ask: is there such a pair that does this.
Once I have such a pair I can plug it into this query:
SELECT DISTINCT field1
FROM t t1
INNER JOIN t t2 USING(field1)
WHERE t1.field2 = 'A' AND t2.field2 = 'B'
and get only a single row.
I have tried something like SELECT field2 FROM t WHERE field2 NOT IN (SELECT field2 FROM t WHERE field1 != 1) and field1 = 1 but this obviously doesn't work.

You write a subquery that returns all pairs of field2 values for each field1. Then use a left join of this with itself, finding pairs that have the same field2 values, but different field1. These then get excluded with the NULL check, and you're left with the unique pairs for each field1.
SELECT subq1.field1, subq1.a, subq1.b
FROM (
SELECT t1.field1, t1.field2 AS a, t2.field2 AS b
FROM t AS t1
JOIN t AS t2 ON t1.field1 = t2.field1 AND t1.field2 < t2.field2
) as subq1
LEFT JOIN (
SELECT t1.field1, t1.field2 AS a, t2.field2 AS b
FROM t AS t1
JOIN t AS t2 ON t1.field1 = t2.field1 AND t1.field2 < t2.field2
) AS subq2 ON subq1.field1 != subq2.field1 AND subq1.a = subq2.a AND subq1.b = subq2.b
WHERE subq2.field1 IS NULL
DEMO

I have written below query
SELECT temp. field1,temp.firstValue,temp.secondValue
FROM (SELECT table1.field1 , table1.field2 firstValue ,table2.field2 secondValue FROM T table2 ,T table1
WHERE table1.field1 = table2.field1 AND table2.field2<> table1.field2 AND table1.field2 < table2.field2
) temp
GROUP BY temp. field1,temp.firstValue
It will give you below pairs
field1 firstValue secondValue
------ ---------- -----------
1 A B
1 B C
2 A C
2 C D
3 B D
Please let me know if for input 2 pair (A,C) and (A,D) would be sufficient

Related

How to get distinct values from a Table not present in multiple table

I am trying to get a unique list of values from a table which is not present in the corresponding columns in other 2 tables( multiple tables)
Here is how my Table looks :
----------- ----------- -----------
Table1 Table2 Table3
---|------- ---|------- ---|-------
id | Value id | Value id | Value
---|------- ---|------- ---|-------
1 | A 1 | A 1 | A
2 | B 2 | C 2 | D
3 | C 3 | D 3 | E
4 | D 4 | G 4 | F
Now, the unique value of Table1 is "B" ( This value is not present in Table2 and Tabl3).
Similarly unique value of Table2 is "G". Similarly unique value of Table3 is "E, F".
I am using the following query :
select Value from Table1 where Table1.Value NOT IN (select Value from Table2);
Any idea how to extend into 2 tables ( or more)?
Thanks
Use and:
select Value
from Table1
where Table1.Value not in (select Value from Table2) and
Table1.Value not in (select Value from Table3) ;
I discourage the using NOT IN with subqueries. It doesn't behave as you would expect with NULL values. If any value in the subquery is NULL, then all rows are filtered out.
Instead, use NOT EXISTS:
select t1.Value
from Table1 t1
where not exists (select 1 from table2 t2 where t2.value = t1.value) and
not exists (select 1 from table3 t3 where t3.value = t1.value);
You may also use left joins here:
SELECT t1.Value
FROM Table1 t1
LEFT JOIN Table2 t2
ON t1.Value = t2.Value
LEFT JOIN Table3 t3
ON t1.Value = t3.Value
WHERE
t2.Value IS NULL AND t2.Value IS NULL;

What does this sql mean with not exists?

a table like this
int char int int
id name a_id b_id
SELECT count(*) FROM tbl t1 WHERE b_id = 12 AND NOT EXISTS(select * from tbl t2 where t2.a_id = t1.b_id AND t2.b_id = t1.a_id)
I think it at least equals to
SELECT count(*) FROM tbl t1 WHERE b_id = 12 AND NOT EXISTS(select * from tbl t2 where t2.a_id = 12 AND t2.b_id = t1.a_id)
then what does this mean?
For example, SELECT a_id FROM tbl t1 WHERE b_id = 12 gives 1,2,3,4
then do following:
select * from tbl t2 where t2.a_id = 12 AND t2.b_id = 1 # NULL
select * from tbl t2 where t2.a_id = 12 AND t2.b_id = 2 # exists
select * from tbl t2 where t2.a_id = 12 AND t2.b_id = 3 # exists
select * from tbl t2 where t2.a_id = 12 AND t2.b_id = 4 # NULL
so the count(*) will be 2?
This one is very interesting because it finds "symmetries" in a_id and b_id with the number 12,so basically it counts the number of rows in the table where b_id=12 except for the ones which have a symmetric pair, like this:
| a_id | b_id |
---------------
| 12 | 12 |
| 12 | 2 |
| 2 | 12 |
Or more generally speaking, the query skips counting the rows which have a_id=X b_id=12 and there exists another row where a_id=12 and b_12=X.
So the NOT EXISTS is simply there to skip counting rows which have a symmetric pair, I don't know what this can be useful for in most applications but it is interesting nonetheless.

MySQL select multiple row on table 2 with two differents Id from table 1 result in one row as two differents fields

Is it possible in only one MySQL query to get the desired result (below) returned into one row ?
Where id=1 in Table 1
Table 1
|------|----------|----------|
| id | refIdOne | refIdTwo |
|------|----------|-----------
| 1 | 1 | 2 |
|------|----------|-----------
Columns "refIdOne" & "refIdTwo" refer Table 2 "id" column
Table 2
|------|------------------|
| id | text |
|------|------------------|
| 1 | cheese |
| 2 | made with milk |
|------|------------------|
Desired result returned in ONE ROW with custom AS columns named "subject" and "description" :
|----------|-----------------|
| subject | description |
|----------|-----------------|
| cheese | made with milk |
? Many thanks for help
* EDIT : Answer is *
select t21.text as subject, t22.text as description
from Table1 as t1
join Table2 as t21 on t1.refidone = t21.id
join Table2 as t22 on t1.refidtwo = t22.id
where t1.id = 1
Table2 has to joined twice to Table1.
select t21.text as subject, t22.text as description
from table1 t1
join table2 t21 on t1.refidone = t21.id
join table2 t22 on t1.refidtwo = t22.id
You need to perform Self join. Join Table2.ID twice with Table1.refIdOne and Table1.refIdTwo
select t2.text as subject, t3.text as description
from Table1 t1
Left join Table2 t2 on t1.refIdOne = t2.id
Left join Table2 t3 on t1.refIdTwo = t2.id
without joins
SELECT
(SELECT text FROM Table2 WHERE id = (SELECT refIdOne FROM Table1 WHERE id = 1) ) AS name,
(SELECT text FROM Table2 WHERE id = (SELECT refIdTwo FROM Table1 WHERE id = 1) ) AS description

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

mysql selecting a union where values in one don't appear in the other

sorry for the poorly titled post.
Say I have the following table:
C1 | C2 | c3
1 | foo | x
2 | bar | y
2 | blaz | z
3 | something| y
3 | hello | z
3 | doctor | x
4 | name | y
5 | continue | x
5 | yesterday| z
6 | tomorrow | y
I'm trying to come up w/ a sql statement which performs the following union:
1st retrieval retrieves all records w/ c3 = 'y'
2nd retrieval retrieves the first instance of a record where c3 <> 'y' and the result is not in the previous union
So, for the result, I should see:
C1 | C2
1 | foo
2 | bar
3 | something
4 | name
5 | continue
6 | tomorrow
So two questions: 1: Am I totally smoking crack where I think I can do this, and 2: (assuming I can), how do I do this?
Try this one:
SELECT a.C1, a.C2
FROM MyTable a
WHERE a.C3 = 'y'
UNION
SELECT b.C1, b.C2
FROM MyTable b
WHERE b.C3 <> 'y' AND
b.C1 not in
(
SELECT c.C1
FROM MyTable c
WHERE c.C3 = 'y'
)
UPDATE 1
by the way, why is that there is only one record of 5 in your desired result? where, in fact, there could be two.
SEE FOR DEMO 1
OR
SELECT g.C1, MIN(g.C2) C2
FROM
(SELECT a.C1, a.C2
FROM MyTable a
WHERE a.C3 = 'y'
UNION
SELECT b.C1, b.C2
FROM MyTable b
WHERE b.C3 <> 'y' AND
b.C1 not in
(
SELECT c.C1
FROM MyTable c
WHERE c.C3 = 'y'
)
) g
GROUP BY g.C1
SEE FOR DEMO 2 (yields same result with your desired result)
DEMO # Sql Fiddle.
select *
from table1
where c3 = 'y'
union all
(select table1.*
from table1
left join table1 t1
on table1.c1 = t1.c1
and t1.c3 = 'y'
where table1.c3 <> 'y'
and t1.c1 is null
-- The meaning of first becomes clear here
order by table1.c3, table1.c2
limit 1)
Note: foo is not in a list because it is marked as x.
Try this:
SELECT C1, C2
FROM Table1
Where C3 = 'y'
UNION
(
SELECT C1, C2
FROM Table1
Where C3 <> 'y' ORDER BY C1 LIMIT 1
)
ORDER BY C1