select self join if only one resulting row - mysql

Is it possible/economical to perform a SELF JOIN of a table (for this example, my table called myTable has two columns pk and fk), and return a record if there is only one resulting record? I am thinking of something like the following, however, only_one_row() is a fictional function that would need to be replaced with something real:
SELECT fk
FROM myTable as t1
INNER JOIN myTable AS t2 ON t2.fk=t1.fk
WHERE t1.pk=1
AND only_one_row();
For instance, if myTable(id,fk) had the following records, only one record is produced, and I which to select the record:
1 1
2 1
3 2
However, if myTable(id,fk) had the following records, two '1' records are produced, and the select should not return any rows:
1 1
2 1
3 2
4 1
I could use PHP to do so, but would rather just use SQL if feasible.

Use a HAVING clause that counts the results.
SELECT fk
FROM myTable as t1
INNER JOIN myTable AS t2 ON t2.fk=t1.fk
WHERE t1.pk=1
HAVING COUNT(*) = 1

How about this:
SELECT fk
FROM myTable as t1
INNER JOIN myTable AS t2 ON t2.fk=t1.fk
WHERE t1.pk=1
GROUP BY fk
HAVING COUNT(fk) = 1

Related

How do I search relations on tables SQL

I have 3 tables like this
With the tables filled like this:
How do I search the cases In where on the table 3 the idtable 1 has all the id from table 2 related?
For example idtable1 = 1 would be an output of that query cuz is related with every id from idtable2
Presumably, you intend:
select t1.*
from table1 t1
where (select count(*) from table3 t3 where t3.idtable1 = t1.idtable1) =
(select count(*) from table2);
This shows all records from table1 where table3 contains all values of idtable2 -- assuming no duplicates in table3 (and that the ids are unique).

Advanced Mysql Query to get master record if two conditions matches on different rows of child records

I was writing a mysql filter query which has a primary table and another table which holds multiple records against each record of primary table (I will call this table child).
Am trying to write a query which fetches record of primary table based on its values on child table. If the child table condition is one then I will be able to do it simply by joining, but I have 2 conditions which falls on same field.
For ex.
table 1:
id name url
1 XXX http://www.yahoo.com
2 YYY http://www.google.com
3 ZZZ http://www.bing.com
table 2:
id masterid optionvalue
1 1 2
2 1 7
3 2 7
4 2 2
5 3 2
6 3 6
My query has to return unique master records when the optionvalue matches only both 2 different conditions match on second table.
I wrote query with IN...
select * from table1
left join table2 on table1.id=table2.masterid
where table2.optionvalue IN(2,7) group by table1.id;
This gets me all 3 records because IN is basically checking 'OR', but in my case I should not get 3rd master record because it has values 2,6 (there is no 7). If I write query with 'AND' then am not getting any records...
select * from table1
left join table2 on table1.id=table2.masterid
where table2.optionvalue = 2 and table2.optionvalue = 7;
This will not return records as the and will fail as am checking different values on same column. I wanted to write a query which fetches master records which has child records with field optionvalues holds both 2 and 7 on different records.
Any help would be much appreciated.
Indeed, as AsConfused hinted, you need to two joins to TABLE2 using aliases
-- both of these are tested:
-- find t1 where it has 2 and 7 in t2
select t1.*
from table1 t1
join table2 ov2 on t1.id=ov2.masterid and ov2.optionValue=2
join table2 ov7 on t1.id=ov7.masterid and ov7.optionValue=7
-- find t1 where it has 2 and 7 in t2, and no others in t2
select t1.*, ovx.id
from table1 t1
join table2 ov2 on t1.id=ov2.masterid and ov2.optionValue=2
join table2 ov7 on t1.id=ov7.masterid and ov7.optionValue=7
LEFT OUTER JOIN table2 ovx on t1.id=ovx.masterid and ovx.optionValue not in (2,7)
WHERE ovx.id is null
You can try something like this (no performance guarantees, and assumes you only want exact matches):
select table1.* from table1 join
(select masterid, group_concat(optionvalue order by optionvalue) as opt from table2
group by masterid) table2_group on table1.id=table2_group.masterid
where table2_group.opt='2,7';
http://sqlfiddle.com/#!9/673094/9
select * from t1 where id in
(select masterid from t2 where
(t2.masterid in (select masterid from t2 where optionvalue=2))
and (t2.masterid in (select masterid from t2 where optionvalue=7)))
Old school :-) Query took 0.0009 sec.
This can also be done without the joins using correlated exists subqueries. That may be more efficient.
select *
from table1
WHERE EXISTS (SELECT 1 FROM table2 WHERE table1.id=table2.masterid and optionvalue = 2)
AND EXISTS (SELECT 1 FROM table2 WHERE table1.id=table2.masterid and optionvalue = 7)
If this is to be an exclusive match as suggested by, "when the optionvalue matches only both 2 different conditions match on second table" then you could ad yet a third exists condition. Performance-wise this may start to break down.
AND NOT EXISTS (SELECT 1 FROM table2 WHERE table1.id=table2.masterid AND optionvalue NOT IN (2,7)
Edit: A note on correlated subqueries from Which one is faster: correlated subqueries or join?.

MySQL, summing certain rows from a hash table, using results from a subquery

I'm trying to sum certain rows from a hash table using two elements: a select group of IDs and a particular key.
Here's the setup:
Table 1:
ID KEY VALUE
1 name John Doe
1 amount 10
2 name Jane Doe
2 amount 15
3 name Mike Lowry
3 amount 5
Table 2:
ORDERID TYPE TRANSACTIONID
1001 Purchase 1
1002 Donation 2
1003 Purchase 3
I'm trying to get a sum of all the amounts where the type is "Purchase." Here's the query I'm using:
SELECT SUM(Table1.value) as balance
FROM Table1
LEFT JOIN (SELECT Table2.TRANSACTIONID as TID FROM Table2 WHERE Table2.TYPE = "Purchase" ) as ids
ON Table1.ID = ids.TID
WHERE Table1.key = "amount"
Tweaking that, I've managed to get 0 and the total of all the rows, but not just the one result. Ideas?
The problem is that your query makes an outer join between Table1 and Table2, such that all records of Table1 are preserved irrespective of whether a matching record is found from Table2. Learn about SQL joins.
You want to make an inner join instead:
SELECT SUM(VALUE)
FROM Table1 JOIN Table2 ON Table1.ID = Table2.TRANSACTIONID
WHERE Table1.KEY = 'amount' AND Table2.TYPE = 'Purchase'
See it on sqlfiddle.

Is it possible to select a value in the table on another row using that id?

I have a table which has the following structure:
id name id_relation
---------------------------
1 this NULL
2 that 1
I want a query to get, instead of id_relation, the name of the correlating id (in this case - 'this', so at the end, I'll get this result:
id name parent_name
-----------------------
2 that this
Is it possible to do this?
Yes. Join the table to itself:
select t1.id, t1.name, t2.name as parent_name
from mytable t1
left join mytable t2 on t2.id = t1.id_relation
where t1.id = 2; -- where clause is optional. leave off to get all rows
This query will return rows for every row in your table, even if there isn't a matching "relation" row.
If you want to restrict the result rows to only those that have a matching row (as your example suggests), remove the LEFT keyword.
You have to do a table join on itself. Something like; select a.name, b.name from foo a, foo b where a.id_relation = b.id;

Mysql union/join help with multiple columns

At first I thought this may work as a join but I'm not sure if this is really a union command or if even possible. Below is an example of the two tables and each has about 20 more columns of various different data.
Table 1
> id assembly user1 user2 containerID productID packageID
1 line2 Billy John 3794 4892 4589
2 line4 John Doug 7794 6201 7864
Table 2
> item_id name width height weight flag1 flag2
3794 Box 10 10 10 0 1
4892 Lamp 4 6 2 1 1
7864 BigBox 200 200 300 4 5
What I am trying to do is show all of Table 1 but replace the containerID, productID, and packageID with their name from Table 2 using the matching item_id. Tried doing this outside of mysql with foreach but with Table 2 having 30k rows it lags up "just a bit" when trying to display the hundreds of rows from Table 1 and replacing each id with its name equivalent.
To see all the table_1 records, use:
SELECT t1.id, t1.assembly, t1.user1, t1.user2,
t2c.name, t2pr.name, t2pk.name
FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2c ON t2c.item_id = t1.containerid
LEFT JOIN TABLE_2 t2pr ON t2pr.item_id = t1.productid
LEFT JOIN TABLE_2 t2pk ON t2pk.item_id = t1.packageid
You can change those to INNER JOINs, but any row that doesn't match all three will not be in the resultset.
My query uses table aliases, because you have to join to the appropriate TABLE_2 column for each name you want to look up.
try to use something like this:
SELECT id, assembly, user1, user2,
(SELECT name from table2 where item_id = table1.containerID),
(SELECT name from table2 where item_id = table1.productID),
(SELECT name from table2 where item_id = table1.packageID)
FROM table1
ORDER BY id;
You'll need to join each row three times for each item_id
SELECT t1.*, t21.name,t22.name,t23.name FROM table_1 t1
INNER JOIN table_2 t21 ON t1.containerID = t21.itemid
INNER JOIN table_2 t22 ON t1.productId = t22.itemid
INNER JOIN table_2 t23 ON t1.packageID = t23.itemid
Make sure there's an index on table_2's itemid