get rows from two tables using join and sub query - mysql

I have a table of Persons
Person_ID , Person_Name
another table of Person_Vehicle_Relation
PV_ID , Person_ID , Vehicle_ID, Role
I want to build a query in which I can get the list of
PV_ID , Person_Name
where Vehicle_ID= 3 and Role = 'Driver'.
I have tried join in following way but it is not working. How can I get my desired data?
Select Persons.Person_Name , Person_Vehicle_Relation.PV_ID
from Persons
inner join Person_Vehicle_relations on Persons.Person_ID = (select Person_ID from Person_Vehicle_relations where Vehicle_ID = 3 and Role= 'driver')
and error was
Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

Why do you need a subquery/inline view? a simple where should work.
SELECT P.Person_Name , PVR.PV_ID
FROM Persons P
INNER join Person_Vehicle_relations PVR
on P.Person_ID = PRV.Person_ID
WHERE PVR.Vehicle_ID = 3
and PVR.Role= 'driver'
The reason why you had an error is because the subquery returned multiple persons and a single person_ID from person's can't match multiple persons from PVR.
You could switch it to "IN" instead of "=" and it should work as well; but a join and a where clause seemed the simplest to maintain and run.
One normally joins on the PK/FK relationship then applies limits in the where or in a having. I generally only use subquery/inline views instead of a straight join when I need aggregation done and a M-M relationship would artificially inflate the aggregation. I may use subqueries also in cross applies or in exists when I don't need data from the second table. In this case you needed data from both tables so a join seemed the best.

Related

MySQL: Optimizing Sub-queries

I have this query I need to optimize further since it requires too much cpu time and I can't seem to find any other way to write it more efficiently. Is there another way to write this without altering the tables?
SELECT category, b.fruit_name, u.name
, r.count_vote, r.text_c
FROM Fruits b, Customers u
, Categories c
, (SELECT * FROM
(SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r
WHERE b.fruit_id = r.fruit_id
AND u.customer_id = r.customer_id
AND category = "Fruits";
This is your query re-written with explicit joins:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN
(
SELECT * FROM
(
SELECT *
FROM Reviews
ORDER BY fruit_id, count_vote DESC, r_id
) a
GROUP BY fruit_id
) r on r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
CROSS JOIN Categories c
WHERE c.category = 'Fruits';
(I am guessing here that the category column belongs to the categories table.)
There are some parts that look suspicious:
Why do you cross join the Categories table, when you don't even display a column of the table?
What is ORDER BY fruit_id, count_vote DESC, r_id supposed to do? Sub query results are considered unordered sets, so an ORDER BY is superfluous and can be ignored by the DBMS. What do you want to achieve here?
SELECT * FROM [ revues ] GROUP BY fruit_id is invalid. If you group by fruit_id, what count_vote and what r.text_c do you expect to get for the ID? You don't tell the DBMS (which would be something like MAX(count_vote) and MIN(r.text_c)for instance. MySQL should through an error, but silently replacescount_vote, r.text_cbyANY_VALUE(count_vote), ANY_VALUE(r.text_c)` instead. This means you get arbitrarily picked values for a fruit.
The answer hence to your question is: Don't try to speed it up, but fix it instead. (Maybe you want to place a new request showing the query and explaining what it is supposed to do, so people can help you with that.)
Your Categories table seems not joined/related to the others this produce a catesia product between all the rows
If you want distinct resut don't use group by but distint so you can avoid an unnecessary subquery
and you dont' need an order by on a subquery
SELECT category
, b.fruit_name
, u.name
, r.count_vote
, r.text_c
FROM Fruits b
INNER JOIN Customers u ON u.customer_id = r.customer_id
INNER JOIN Categories c ON ?????? /Your Categories table seems not joined/related to the others /
INNER JOIN (
SELECT distinct fruit_id, count_vote, text_c, customer_id
FROM Reviews
) r ON b.fruit_id = r.fruit_id
WHERE category = "Fruits";
for better reading you should use explicit join syntax and avoid old join syntax based on comma separated tables name and where condition
The next time you want help optimizing a query, please include the table/index structure, an indication of the cardinality of the indexes and the EXPLAIN plan for the query.
There appears to be absolutely no reason for a single sub-query here, let alone 2. Using sub-queries mostly prevents the DBMS optimizer from doing its job. So your biggest win will come from eliminating these sub-queries.
The CROSS JOIN creates a deliberate cartesian join - its also unclear if any attributes from this table are actually required for the result, if it is there to produce multiples of the same row in the output, or just an error.
The attribute category in the last line of your query is not attributed to any of the tables (but I suspect it comes from the categories table).
Further, your code uses a GROUP BY clause with no aggregation function. This will produce non-deterministic results and is a bug. Assuming that you are not exploiting a side-effect of that, the query can be re-written as:
SELECT
category, b.fruit_name, u.name, r.count_vote, r.text_c
FROM Fruits b
JOIN Reviews r
ON r.fruit_id = b.fruit_id
JOIN Customers u ON u.customer_id = r.customer_id
ORDER BY r.fruit_id, count_vote DESC, r_id;
Since there are no predicates other than joins in your query, there is no scope for further optimization beyond ensuring there are indexes on the join predicates.
As all too frequently, the biggest benefit may come from simply asking the question of why you need to retrieve every single row in the tables in a single query.

SQL select users that belong to two groups

I have a list of persons in a table. I then have another table where I correlate each person to one or more groups. Some persons have only one entry in the groups table but some have multiple.
I am now trying to SELECT list of persons that are in two specific groups. Person must be in BOTH groups in order to qualify.
My table with the basic information on the persons is base and the table with the group correlation is groups_registration. In fact I also have a third table where the groups names and further information are stored but it is not required for this query.
The groups I am trying to gather in this example are 4 and 11.
What I tried initially was:
SELECT base.*, groups_registration.person_id, groups_registration.group_id
FROM base
INNER JOIN groups_registration
ON base.id = groups_registration.person_id
WHERE (groups_registration.group_id = '4' AND groups_registration.group_id = '11')
ORDER BY base.name
This did not get my any response, I assume because no single row contains both group_id = 4 and group_id 11.
I have been searching through stackoverflow with no joy. Do you guys have any ideas?
Obviously, no row has both values. Use group by:
SELECT gr.person_id, groups_registration.group_id
FROM groups_registration gr
WHERE gr.group_id IN (4, 11)
GROUP BY gr.person_id
HAVING COUNT(DISTINCT gr.group_id) = 2;
I'll let you figure out how to join in the additional information from base.
Notes:
Use table aliases to make it easier to write and read queries.
Presumably, the ids are numbers. Compare numbers to numbers. Only use single quotes for date and string constants.
IN is better than long chains of OR/=.
You can use joins as shown below:
SELECT A.*, B.person_id, B.group_id
FROM base A
INNER JOIN
(SELECT gr.person_id, groups_registration.group_id
FROM groups_registration gr
WHERE gr.group_id IN (4, 11)
GROUP BY gr.person_id
HAVING COUNT(DISTINCT gr.group_id) = 2) B
ON A.id = B.person_id;
This will give you all the desired fields.

MySQL COUNT on multiple relations

I am trying to figure out how to return the counts of multiple different items in seperate tables related to the table i am joining too.
Im quite new to joins so im not sure if im using correct join. hopefully you can help me!
the tables would be like this:
staff_type table
id type
1 doctor
2 nurse
3 surgeon
staff table
id type_id name
1 1 bob
2 1 jane
3 2 phil
4 2 esther
5 3 michael jackson
im tring to construct a statement that will return me the COUNT off the various different staff types, as in how many dactors, how many nurses etc. I also want the query to bring the data from the staff_type table.
I haven't much ideas on how to construct this query, but it may look something like this:
SELECT staff_type.*, COUNT(Staff.type_id = staff_type.id)
INNER JOIN staff AS Staff ON (staff_type.id = Staff.type_id)
i know this is nothing like what its supposed to be, bit hopefully some of you can point me in the right direction. Other posts on this topic are hard for me to understand and look like they are trying to do something slightly different.
thanks for any help!
You can use something like this as an example:
SELECT t.id
, t.name
, COUNT(s.id) AS count_staff
FROM staff_type t
LEFT
JOIN staff s
ON s.type_id = t.id
GROUP
BY t.id
, t.name
To understand what that's doing, you can remove the GROUP BY and the aggregate expression (COUNT) function in the SELECT list, and see the rows returned by the JOIN operation.
For example:
SELECT t.id AS `t.id`
, t.name AS `t.name`
, s.id AS `s.id`
, s.name AS `s.name`
, s.type_id AS `s.type_id`
FROM staff_type t
LEFT
JOIN staff s
ON s.type_id = t.id
ORDER
BY t.id
, s.id
Note that the LEFT keyword indicates an "outer join". This is going to return all the rows from the table on the left side, even if there aren't any matching rows on the right side. (This will let us get a "zero" count for a staff_type that doesn't have any related staff.)
When we add the GROUP BY clause, that says to "collapse" all the rows that have the same values for the expressions or columns in the GROUP BY list.
We can use an aggregate function, such as COUNT(), SUM(), MAX(), MIN() to perform an operation on all of the rows that are collapsed into a group.
The COUNT() aggregate starts at zero, and increments by one for every non-NULL value. So, we use an expression, COUNT(s.id) that we are guaranteed will be non-NULL if there is a matching row from staff, and will be NULL if there isn't a matching row.
(I hope this helps clear up some of your confusion.)
The query will be,
select staff_type.*, count(staff.id) as count from staff_type left join staff on (staff.type_id = staff_type.id) group by staff_type.id

MySQL Union always returns one row with NULL's

Given the following query…
SELECT DISTINCT *
FROM PAS_Post
WHERE post_user_id = 21
GROUP BY post_post_id
UNION
SELECT DISTINCT PAS_Post.*
FROM PAS_Follow LEFT JOIN PAS_Post ON (
PAS_Follow.folw_followed_user_id = PAS_Post.post_user_id
)
WHERE PAS_Follow.folw_follower_user_id = 21
GROUP BY post_post_id
ORDER BY post_posted_date DESC
I always get a row in the results that is just NULL's, unfortunately I need to preserve some NULL values in the data as the Post's table (PAS_Post) holds different types of information.
Can anyone steer me in the right direction to get rid of this null row.
I do not want or need the last row here
You're using a (left) outer join in the second part of the UNION, so any cases that do not satisfy the join criteria will result in data from the table on the left of the join (PAS_Follow), but NULL in every column of the table on the right of the join (PAS_Post); the subsequent selection only of columns from the latter table results in the NULL rows that you observe. Therefore, the simplest solution is to use an inner join (that completely excludes records where the join criteria is not met).
However, in your case, it appears that your query can be greatly simplified by simply using two possible conditions in a filter on the joined tables rather than a UNION:
SELECT p.*
FROM PAS_Post AS p
JOIN PAS_Follow AS f ON f.folw_followed_user_id = p.post_user_id
WHERE p.post_user_id = 21
OR f.folw_follower_user_id = 21
ORDER BY p.post_posted_date DESC
I have excluded the GROUP BY clause on the assumption that post_post_id is the primary key (or at very least is UNIQUE) in your PAS_Post table. If that assumption is incorrect, you may want to reintroduce it—but beware that MySQL will indeterminately select the values that will be returned from each group.

mysql query on database using composite design pattern

I'm not an SQL expert and therefore am having trouble wrapping my head around designing a mysql query to query database tables designed using the "composite design pattern."
The tables are:
composites:
id, name, type [type is either "Condition" or "ConditionGroup"]
composites_properties:
id, composite_id, property_id
groupings:
id, parent_id, child_id
properties: id, key, value
What I want to do is generate a query that will return the unique properties of the group's ("ConditionGroup") member conditions ("Condition") such that I end up with a Group Name and a list of Property Keys (inherited from the member conditions).
The best I've come up with is:
SELECT DISTINCT properties.`key`, composites.name
FROM composites, composites_properties, properties
WHERE composites.id=composites_properties.composite_id
AND properties.id=composites_properties.property_id
AND composites.id IN (
SELECT child_id FROM groupings WHERE parent_id IN
(SELECT id FROM composites WHERE type='ConditionGroup')
)
This yields each member condition along with its list of properties where the properties are repeated if more than one member condition has that property.
In the end I'd like:
Group Name
property_1
property_2
property_3
But I'm getting the following type list (with no indication to which group the conditions belong)
Condition Name 1 property_1
Condition Name 1 property_2
Condition Name 1 property_3
Condition Name 2 property_1
Condition Name 2 property_2
Condition Name 3 property_1
Condition Name 3 property_2
Any suggestions?
To be honest, I'm a little unclear on exactly what you are trying to accomplish, but I'll give it a shot. If you can clarify how this query does not do what you want, I can probably help further.
SELECT c.name, p.key
FROM composites c
INNER JOIN groupings g ON c.id = g.parent_id
INNER JOIN composites_properties cp ON cp.composite_id = g.child_id
INNER JOIN properties p ON p.id = cp.property_id
WHERE c.type = 'ConditionGroup'
This doesn't seem like the best query in the world to me because it ignores what the children in each grouping actually are, so I'm not sure this is what you want.