mysql query on database using composite design pattern - mysql

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.

Related

get rows from two tables using join and sub query

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.

how to get the sum of a child column from another table

i have a system that can get member information. i want my system to automatically get the name of the awardee. The conditions are if the member have recruited 6 people, he/she sold 15 soaps and his/her downlines sold 15 soaps each. Im using mysql and VB.NET so far i have this query.
SELECT *
FROM members m
LEFT JOIN
geneology g
ON SUM(m.status) >=90 AND
m.upline = g.parent_id;
but i get
1111 error - invalid use of group function
Table 1 has columns
id, name, status 'This is where the soap data are stored', downlines, upline
Table 2 has columns
id, parent_id, child_id
Please help im new to sql queries
First of all, the ON clause defines the criteria used to match the rows of the two tables, so that SUM() >= 90 is not supposed to stay there (in fact, that's why you're getting that error).
I will assume that for a person on table members, upline is the ID of the member that recruited him/her. If that's correct, you can build your query like this:
select t1.id, t1.name
from members t1
join geneology t2
on t1.id = t2.parent_id
join members t3
on t2.child_id = t3.upline
where t1.status = 15
group by t1.id, t1.name
having count(distinct t3.id) = 6 and
sum(t3.status) = 90
This will use the members table twice, once for the parent and once for the children, and the two tables are correlated by the geneology table.
The condition in the where clause is for the parent, then you group the child data for each parent, and only return the parents whose children satisfiy the conditions in the having clause.

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

How can I select data from one table depending on the data from another table

I have 2 tables: contracts_main_list and contracts_detail.
In contracts_main_list I have columns:
user_id
contract_id
and in contracts_detail:
contract_id
other columns with data
I need to select all the rows from the table contracts_main_list WHERE user_id = some number.
From these rows I need to get the list of contract numbers (from column contract_id) and according to them select rows corresponding to each of the contract number from the list. So something like:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
The contract_ids are probably gonna be unique, but in case there is some kind of error and there will be more rows with the same contract_id in either of the tables, I need to select only one row (so probably using DISTINCT) and select the latest record (both tables have a column id as a primary key)
Also if there is no row in contracts_detail matching with the contract_id to the contract_id of the first table contracts_main_list it should skip the row. But I guess the condition:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
already covers it.
I hope I made it clear enough. What I am trying to do in real life is show list of contracts with all the relevant data belonging to the user.
To sum this up, I need to find all the contracts belonging to the user and select the rows with details about each contract and finally get the data from the contracts_detail table as a result.
Here is the result you're looking for:
SELECT CD.*
FROM (SELECT C2.contract_id
,MAX(C2.id) AS last_main_list_id
,MAX(CD2.id) AS last_contracts_detail_id
FROM contracts_main_list C2
INNER JOIN contracts_detail CD2 ON CD2.contract_id = C2.contract_id
GROUP BY C2.contract_id) L
INNER JOIN contracts_main_list C ON C.id = L.last_main_list_id
AND C.user_id = ?
INNER JOIN contracts_detail CD ON CD.id= L.last_contracts_detail_id
This query use a subquery for the FROM because of the following indication you provided:
The contract_ids are probably gonna be unique, but in case there is
some kind of error and there will be more rows with the same
contract_id in either of the tables, I need to select only one row
If you're sure that the contract_id are unique, here is the same query without this check on contract_id:
SELECT CD.*
FROM contracts_main_list C
INNER JOIN contracts_detail CD ON CD.contract_id = C.contract_id
WHERE C.user_id = ?
Hope this will help you.

MySQL joins and COUNT(*) from another table

I have two tables: groups and group_members.
The groups table contains all the information for each group, such as its ID, title, description, etc.
In the group_members table, it lists all the members who are apart of each group like this:
group_id | user_id
1 | 100
2 | 23
2 | 100
9 | 601
Basically, I want to list THREE groups on a page, and I only want to list groups which have MORE than four members. Inside the <?php while ?> loop, I then want to four members who are apart of that group. I'm having no trouble listing the groups, and listing the members in another internal loop, I just cannot refine the groups so that ONLY those with more than 4 members show.
Does anybody know how to do this? I'm sure it's with MySQL joins.
MySQL use HAVING statement for this tasks.
Your query would look like this:
SELECT g.group_id, COUNT(m.member_id) AS members
FROM groups AS g
LEFT JOIN group_members AS m USING(group_id)
GROUP BY g.group_id
HAVING members > 4
example when references have different names
SELECT g.id, COUNT(m.member_id) AS members
FROM groups AS g
LEFT JOIN group_members AS m ON g.id = m.group_id
GROUP BY g.id
HAVING members > 4
Also, make sure that you set indexes inside your database schema for keys you are using in JOINS as it can affect your site performance.
SELECT DISTINCT groups.id,
(SELECT COUNT(*) FROM group_members
WHERE member_id = groups.id) AS memberCount
FROM groups
Your groups_main table has a key column named id. I believe you can only use the USING syntax for the join if the groups_fans table has a key column with the same name, which it probably does not. So instead, try this:
LEFT JOIN groups_fans AS m ON m.group_id = g.id
Or replace group_id with whatever the appropriate column name is in the groups_fans table.
Maybe I am off the mark here and not understanding the OP but why are you joining tables?
If you have a table with members and this table has a column named "group_id", you can just run a query on the members table to get a count of the members grouped by the group_id.
SELECT group_id, COUNT(*) as membercount
FROM members
GROUP BY group_id
HAVING membercount > 4
This should have the least overhead simply because you are avoiding a join but should still give you what you wanted.
If you want the group details and description etc, then add a join from the members table back to the groups table to retrieve the name would give you the quickest result.