MySQL multiple right joins - mysql

mysql> select * from product;
+------------+---------------+
| product_id | name |
+------------+---------------+
| 1 | Car |
| 2 | House |
| 3 | Cat |
| 4 | Blank Product |
+------------+---------------+
4 rows in set (0.00 sec)
mysql> select * from tag;
+--------+-----------+
| tag_id | name |
+--------+-----------+
| 1 | Expensive |
| 2 | Fast |
| 3 | Mean |
| 4 | Large |
| 5 | Small |
| 6 | Alive |
| 7 | Blank Tag |
+--------+-----------+
7 rows in set (0.00 sec)
mysql> select * from product_tag;
+------------+--------+
| product_id | tag_id |
+------------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 4 |
| 3 | 2 |
| 3 | 3 |
| 3 | 5 |
| 3 | 6 |
+------------+--------+
10 rows in set (0.00 sec)
Why does the following query return my blank tag but not my blank product?
mysql> select * from product_tag right join product using (product_id)
right join tag using (tag_id);
+--------+-----------+------------+-------+
| tag_id | name | product_id | name |
+--------+-----------+------------+-------+
| 1 | Expensive | 1 | Car |
| 1 | Expensive | 2 | House |
| 2 | Fast | 1 | Car |
| 2 | Fast | 3 | Cat |
| 3 | Mean | 1 | Car |
| 3 | Mean | 3 | Cat |
| 4 | Large | 1 | Car |
| 4 | Large | 2 | House |
| 5 | Small | 3 | Cat |
| 6 | Alive | 3 | Cat |
| 7 | Blank Tag | NULL | NULL |
+--------+-----------+------------+-------+
11 rows in set (0.00 sec)

You are using right join. In your query tags ids are the base where MySQL will start matching. The right join is evaluated from right to left. If you break your query into two parts. The first one will be:
select * from product_tag right join tag using (tag_id);
+--------+-----------+------------+
| tag_id | name | product_id |
+--------+-----------+------------+
| 1 | expensive | 1 |
| 1 | expensive | 2 |
| 2 | fast | 1 |
| 2 | fast | 3 |
| 3 | mean | 1 |
| 3 | mean | 3 |
| 4 | larg | 1 |
| 4 | larg | 2 |
| 5 | small | 3 |
| 6 | alive | 3 |
| 7 | blank tag | NULL |
+--------+-----------+------------+
As you see there is not product_id that matches with the blank tag. The explains why joining this result with the product table will give you the result you saw.
If you use left join instead you'll get this result:
select * from product_tag left join product using (product_id) left join tag using (tag_id);
+--------+------------+-------+-----------+
| tag_id | product_id | name | name |
+--------+------------+-------+-----------+
| 1 | 1 | car | expensive |
| 2 | 1 | car | fast |
| 3 | 1 | car | mean |
| 4 | 1 | car | larg |
| 1 | 2 | house | expensive |
| 4 | 2 | house | larg |
| 2 | 3 | cat | fast |
| 3 | 3 | cat | mean |
| 5 | 3 | cat | small |
| 6 | 3 | cat | alive |
+--------+------------+-------+-----------+

There is no row associating product id 4 with a tag. You need to add a row to the product_tag table like the following:
+------------+--------+
| product_id | tag_id |
+------------+--------+
| 4 | 7 |
+------------+--------+

You are using a RIGHT JOIN so all the rows in the right table i.e "tag" will be returned even if there is no match in the joined tables.

Related

Query equivalent to two group by's without a subquery?

I'm trying to run a report on User ACL's. We use MYSQL and my we're prohibited from using subqueries for performance reasons. The goal is to turn this:
--------------------------------
| userName | folderID | roleID |
--------------------------------
| gronk | 1 | 1 |
| gronk | 2 | 2 |
| gronk | 4 | 2 |
| tbrady | 1 | 2 |
| jedelman | 1 | 1 |
| jedelman | 2 | 1 |
| mbutler | 1 | 2 |
| mbutler | 2 | 2 |
| bill | 1 | 3 |
| bill | 2 | 3 |
| bill | 3 | 3 |
| bill | 4 | 3 |
--------------------------------
Into this:
------------------------
| Lowest Role | Number |
------------------------
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
------------------------
I can see how to do it with a subquery. The inner query would do a group by on userName with a min(roleID). Then the outer query would do a group by on the lowest role and count(*). But I can't see how to do it without a subquery.
Also, if it helps I created a SQL Fiddle that has the data above.
I found a solution using a left join:
select UFM.roleID, count(distinct UFM.userName)
from UserFolderMembership UFM
left join UserFolderMembership UFM2 on
UFM.userName = UFM2.userName and
UFM.roleID > UFM2.roleID
where
UFM2.userName is null
group by
UFM.roleID

MYSQL: count if value from one table exists in multiple columns in another table

wondering if anyone one can assist,
I have a fixed reference table
tblConfig_qC
----------------
| id | value |
----------------
| 1 | optionA |
| 2 | optionB |
| 3 | optionC |
| 4 | optionD |
| 5 | optionE |
| 6 | optionF |
| 7 | optionG |
----------------
And a tblSubmission table where I have 3 columns which stores which of the option users selected (from the tblConfig_qC table)
User can select between 1 to 3 options
So typically, rows can be like:
tblSubmissions
------------------------------------
| id | qC1 | qC2 | qC3 |
------------------------------------
| 1 | optionB | | |
| 2 | optionA | optionD | optionE |
| 3 | optionC | optionD | optionF |
| 4 | optionD | optionF | |
------------------------------------
What i need to do is create a summary of counts:
-------------------------
| id | Options | Counts |
-------------------------
| 1 | optionA | 0 |
| 2 | optionB | 1 |
| 3 | optionC | 1 |
| 4 | optionD | 3 |
| 5 | optionE | 1 |
| 6 | optionF | 2 |
| 7 | optionG | 0 |
-------------------------
I can do this for one column but how do i add the counts of the other 2?
SELECT q.value AS 'Option',
COUNT(s.qC1) AS 'qC1',
FROM tblConfig_qC q
LEFT JOIN tblSubmission s ON q.value = s.qC1
group by q.value ORDER BY q.value;

Selected Child Categories based on parent categories from another table

I have two tables
rules
With three step hierarchy
|id | name | parent |
|---|-------|--------|
| 1 | A | 0 |
| 2 | B | 0 |
| 3 | A(1) | 1 |
| 4 | A(2) | 1 |
| 5 | B(1) | 2 |
| 6 | A(1.1)| 3 |
| 7 | A(1.2)| 3 |
| 8 | A(2.1)| 4 |
| 9 | B(1.1)| 5 |
| 10| A(3) | 1 |
Subject
|id | date | rules | group |
|---|---------------------|-------|-------|
| 1 | 2016-05-20 18:24:20 | 2 | AQR48 |
| 2 | 2016-05-20 19:31:17 | 5 | AQR52 |
| 3 | 2016-05-21 18:11:37 | 6,7,4 | AQR48 |
I need to get second step rules based on group and ruleid(first step) of subject table data
When group = 'AQR48' and rules.parent=1 result should be
|id | name | parent |
|---|-------|--------|
| 3 | A(1) | 1 |
| 4 | A(2) | 1 |
I tried it like this but with out success.
select rules.id,rules.name,rules.parent from rules left join subject on find_in_set(rules.id,subject.rules) where rules.parent=1 AND subject.group='AQR48'
With this I get output as
|id | name | parent |
|---|-------|--------|
| 4 | A(2) | 1 |
Anyone could help me with this

How to match comma delimited value with other column in mySql

I have following two tables:
user:
+---------+--------------+
| user_id | skills |
+---------+--------------+
| 1 | 1,2,3,5,4,14 |
| 2 | 1,2,3 |
| 3 | 3,4,5 |
| 4 | 1,2 |
+---------+--------------+
pskills:
+-----+--------+------+----------+
| PID | SKILLS | SPLI | status |
+-----+--------+------+----------+
| 1 | 2,4 | 1 | |
| 1 | 1 | 1 | required |
+-----+--------+------+----------+
I want to match values of SKILLS columns of table pskills. Such as if query is done with first row of pskills and join with user table then it will return User ID 1 because SKILLS 2,4 match with user id 1 only. How can i do this easily?
Never store multiple values in one column!
You should normalize your tables like this
**user**
+---------+--------------+
| user_id | skills |
+---------+--------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | ... |
| 2 | 1 |
| 2 | 2 |
| | ... |
+---------+--------------+
**pskills**
+-----+--------+------+----------+
| PID | SKILLS | SPLI | status |
+-----+--------+------+----------+
| 1 | 2 | 1 | |
| 1 | 4 | 1 | |
| 1 | 1 | 1 | required |
+-----+--------+------+----------+

Updating multiple rows with first instance of record in same table

Assume following table:
+----+-----------+
| id | session |
+----+-----------+
| 1 | abcd1234 |
| 2 | abcd1234 |
| 3 | abcd1234 |
| 4 | qwert5678 |
| 5 | qwert5678 |
| 6 | abcd1234 |
| 7 | abcd1234 |
| 8 | qwert5678 |
| 9 | abcd1234 |
| 10 | qwert5678 |
| 11 | qwert5678 |
| 12 | qwert5678 |
+----+-----------+
Suppose we want to get the first id of a given session, then set every instance of that session to the id for all sessions, such that the table becomes:
+----+-----------+
| id | session |
+----+-----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 4 |
| 5 | 4 |
| 6 | 1 |
| 7 | 1 |
| 8 | 4 |
| 9 | 1 |
| 10 | 4 |
| 11 | 4 |
| 12 | 4 |
+----+-----------+
We have a table with approximately 45M records, and are essentially changing every instance of column b to the value of min(column a) when grouped by column b.
Is there a way to do this in a single query? We have attempted several.
update example e
set session =
(select id from
(select id,min(session)
from example as first_id
group by session
) as this_id
);
...which errors out: "Subquery returns more than 1 row".
update example e
join
(select id
from
(select id,min(session)
from example as first_id
group by session
) as this_id
) as etable
set session = first_id;
...which errors out: "Unknown column 'first_id' in 'field list'". Also used 'this_id' to the same effect.
And other queries. Is this possible in a single query? Are we thinking about this incorrectly?
Query:
SQLFIDDLEExample
UPDATE example
SET session =(SELECT MIN(e2.ID)
FROM (SELECT *
FROM example) e2
WHERE e2.session = example.session)
Result:
| ID | SESSION |
----------------
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 4 |
| 5 | 4 |
| 6 | 1 |
| 7 | 1 |
| 8 | 4 |
| 9 | 1 |
| 10 | 4 |
| 11 | 4 |
| 12 | 4 |