So I'd like to join two tables using an association table because table 'a' connects to multiple rows in table 'b', and I'd like to get all the results from each table that matches my a.value but the values from table 'b' should be concatenated somehow.
Right now my query looks like this:
SELECT *,
GROUP_CONCAT(DISTINCT g.group_name) group_name,
GROUP_CONCAT(DISTINCT g.group_profile_id) group_profile_id,
GROUP_CONCAT(DISTINCT g.group_publications) group_publications,
GROUP_CONCAT(DISTINCT g.group_followers) group_followers,
GROUP_CONCAT(DISTINCT g.group_ongoing_projects) group_ongoing_projects,
GROUP_CONCAT(DISTINCT g.group_finished_projects) group_finished_projects,
GROUP_CONCAT(DISTINCT g.group_members) group_members
FROM projects_groups pg
LEFT JOIN projects p on p.projectid = pg.projectid
RIGHT JOIN groups g on g.groupid = pg.groupid
WHERE p.projectid IN ($projectIds)
GROUP BY p.projectid
The problem is that I'm not even selecting the half of the columns of the groups table but I'd like to get all of them. I could write down all the columns like above, but it looks really ugly and also i'd have to modify this every time I alter my table.
To further explain the issue a project can connect to multiple groups, and I'd like to get the project data with all the data of its groups. I could query the groups separately but that doesn't seem logical, cause i'd have to do it for each project (e.g. for 100 projects). So i get all the projects in one query and then a query for each project to get its groups. Or I could get the projects in one query joined with the association table and then get the ids from the association table and make a second query using these ids to get all the groups through the association, but I'm looking for a simpler solution.
Pity there is no sample data nor expected result included with this question, however there are a few syntax issues worth mentioning. You start your FROM clause with this:
FROM projects_groups pg
LEFT JOIN projects p on p.projectid = pg.projectid
This allows all rows from projects_groups even if there is no associated project. If there is no associated project then all projects.* columns would be NULL. However in the where clause you require this: WHERE p.projectid IN ($projectIds) which means that any NULLs from the projects table would be ignored and hence that LEFT JOIN is useless.
So as the projects seems to be the primary concern, use it with FROM and then join to the other tables appropriate to your needs. Now my guess is you can have a project without a project group (even if only temporarily) so you might use a left join here and so the query may look like this
FROM projects p
LEFT JOIN projects_groups pg on p.projectid = pg.projectid
WHERE p.projectid IN ($projectIds)
but note you would only do that if you wanted to list projects even if they have no associated project groups. So making the assumption you do need that then continue this left join to groups as well:
FROM projects p
LEFT JOIN projects_groups pg on p.projectid = pg.projectid
LEFT JOIN groups g on g.groupid = pg.groupid
WHERE p.projectid IN ($projectIds)
If the assumption is wrong, then it will be more efficient to use inner joins:
FROM projects p
INNER JOIN projects_groups pg on p.projectid = pg.projectid
INNER JOIN groups g on g.groupid = pg.groupid
WHERE p.projectid IN ($projectIds)
Now we need to look at the SELECT & GROUP BY clauses. MySQL has historically allowed a very non-standard GROUP BY syntax and the default settings allowed a query like yours where only one column is listed to GROUP BY.
e.g. in the past this was allowed:
select p.* from projects p group by p.id
This is very bad practice and now current versions of MySQL now default to standard SQL syntax. Under standard SQL syntax rules you MUST SPECIFY every "non-aggregating column" in the group by clause. and so the example immediately above would produce a syntax error
e.g. you should always be precise about the group by columns
select p.id, p.name, count(*) from projects p group by p.id, p.name
So to help avoid syntax errors you might prefer to use a "derived table" like this:
SELECT
p.*
, d.*
FROM projects p
INNER JOIN (
SELECT
p.projectid
, GROUP_CONCAT(DISTINCT g.group_name) group_name
, GROUP_CONCAT(DISTINCT g.group_profile_id) group_profile_id
, GROUP_CONCAT(DISTINCT g.group_publications) group_publications
, GROUP_CONCAT(DISTINCT g.group_followers) group_followers
, GROUP_CONCAT(DISTINCT g.group_ongoing_projects) group_ongoing_projects
, GROUP_CONCAT(DISTINCT g.group_finished_projects) group_finished_projects
, GROUP_CONCAT(DISTINCT g.group_members) group_members
FROM projects p
LEFT JOIN projects_groups pg on p.projectid = pg.projectid
LEFT JOIN groups g on g.groupid = pg.groupid
WHERE p.projectid IN ($projectIds)
GROUP BY
p.projectid
) d on p.projectid = d.projectid
Related
I'm trying to find the averages and sums of different tables and group by the project. I also want to condennse the returned table into a single row.
So for my subquery I get this result
And the using the outer query I expect this:
This is my sql code so far. It works but it's performance is very slow and I"m not sure why or how to optimize it.
select sum(sub.count) as count, avg(sub.opened) as opened,
avg(sub.clicked) as clicked, avg(sub.started_watching) as started_watching,
sum(sub.views) as views
from (
select p.id, count(e.id) as count,
avg(e.opened) as opened, avg(e.read_email) as clicked,
avg(e.started_video) as started_watching, sum(e.views) as views
from projects p
inner join guests g
on g.project_id = p.id
inner join videos v
on v.guest_id = g.id
inner join emails e
on e.video_id=v.id
group by p.id) sub;
looking to your query the main problem is related to the creation of subquery result
so is important improve this query eg adding proper index on columns involved in join for each table
adn eventually add composite index for column used in select ( after the column used in join )
Select sum(sub.count) as count
, avg(sub.opened) as opened
, avg(sub.clicked) as clicked
, avg(sub.started_watching) as started_watching
, sum(sub.views) as views
from (
select p.id
, count(e.id) as count
, avg(e.opened) as opened
, avg(e.read_email) as clicked
, avg(e.started_video) as started_watching
, sum(e.views) as views
from projects p
inner join guests g on g.project_id = p.id
inner join videos v on v.guest_id = g.id
inner join emails e on e.video_id=v.id
group by p.id
) sub;
so be sure you have proper index on
guests (project_id)
videos (guest_id)
emails (video_id, id, opened, read_email,started_video, views)
and obvious on projects (id)
I have come up with two queries, both use an inner join on two different tables.
Query 1
SELECT PRODUCTS.CODE, PRODUCTS.REFERENCE, PRODUCTS.TAXCAT, PRODUCTS.DISPLAY,PRODUCTS.NAME, PRODUCTS.PRICEBUY, PRODUCTS.PRICESELL, CATEGORIES.NAME AS CATEGORY
FROM PRODUCTS INNER JOIN CATEGORIES ON PRODUCTS.CATEGORY = CATEGORIES.ID;
Query 2
SELECT PRODUCTS.CODE, PRODUCTS.REFERENCE, PRODUCTS.TAXCAT, PRODUCTS.DISPLAY,PRODUCTS.NAME, PRODUCTS.PRICEBUY, PRODUCTS.PRICESELL,STOCKCURRENT.UNITS AS UNIT FROM PRODUCTS INNER JOIN STOCKCURRENT ON STOCKCURRENT.PRODUCT = PRODUCTS.ID;
Both queries run fine on their own, when I try to use both inner joins together I get errors. This is what I came up with on my own. I'm having trouble understanding the syntax to achieve this.
SELECT PRODUCTS.CODE, PRODUCTS.REFERENCE, PRODUCTS.TAXCAT,
PRODUCTS.DISPLAY,PRODUCTS.NAME, PRODUCTS.PRICEBUY,
PRODUCTS.PRICESELL,STOCKCURRENT.UNITS AS UNIT FROM PRODUCTS INNER JOIN
STOCKCURRENT ON STOCKCURRENT.PRODUCT = PRODUCTS.ID, CATEGORIES.NAME AS
CATEGORY FROM PRODUCTS INNER JOIN CATEGORIES ON PRODUCTS.CATEGORY =
CATEGORIES.ID;
Thank you.
Your attempted query has several syntax problems. Assuming you just want to join together the three tables, you may try the following query:
SELECT
p.CODE,
p.REFERENCE,
p.TAXCAT,
p.DISPLAY,
p.NAME,
p.PRICEBUY,
p.PRICESELL,
s.UNITS AS UNIT,
c.NAME AS CATEGORY
FROM PRODUCTS p
INNER JOIN STOCKCURRENT s
ON s.PRODUCT = p.ID
INNER JOIN CATEGORIES c
ON p.CATEGORY = c.ID;
Note that I introduced table aliases here. These aliases can be used elsewhere in the query to avoid having to repeat the entire table name.
By the way, I can also see taking a union of your two original queries. But without expected output, it was not entirely clear what you want.
So I have the below database structure
TABLES ------- Columns
person: id, name, salary, address
group: id, name
person_group: person_id, groud_id
So here is my query which is used to get all persons along with the groups they are associated with
SELECT p.id, p.name,
group_concat(g.name) as groups
FROM person_group pg, group g, person p
WHERE pg.group_id = g.id AND pg.novel_id = n.id
GROUP BY ng.person_id
So this query gives me data like
id name groups
2345 John Admin, SuperAdmin, RedHat
But the problem is: if that person doesn't belong to any group, the query doesn't return that person!
Any would be appreciated!
Never use commas in the FROM clause. Always use proper, explicit JOIN syntax.
That is exactly your problem here. When you are writing more than one table in the FROM clause, you should be thinking "what type of JOIN do I need". If you had that thought, you would immediate realize that you need an outer join to do what you want:
SELECT p.id, p.name, group_concat(g.name) as groups
FROM person p LEFT JOIN
person_group pg
ON pg.person_id = p.id LEFT JOIN
group g
ON pg.group_id = g.id
GROUP BY p.id, p.name;
If you don't know what an outer join is, then that is all the more reason to use the proper, explicit syntax, so you can learn.
Im trying to use the results of an inner join in another query and having trouble getting my head around how.
This is the first query i am running:
SELECT projects.Project_ID, projects.Name
FROM projects
INNER JOIN assigned_users
ON assigned_users.Project_ID=projects.Project_ID AND assigned_users.User_ID=4;
This is getting all of the assigned projects for a particular user. The Project_ID's this query returns i want to use to find all the related requirements for those projects.
SELECT *
FROM requirements
WHERE requirements.Project_ID=1;
So instead of finding the requirements for project '1' i want to get the requirements for all projects assigned to a particular user. Any help would be appreciated.
Cheers
If I understand correctly, you would just add another JOIN:
SELECT . . .
FROM assigned_users au JOIN
projects p
ON au.Project_ID = p.Project_ID JOIN
requirements r
ON r.Project_ID = p.Project_ID
WHERE au.User_ID = 4;
To the existing query, we can just add:
JOIN requirements
ON requirements.Project_ID = projects.Project_ID
We add expressions to the SELECT list to retrieve values of columns from requirements.
SELECT projects.Project_ID, projects.Name
, requirements.some_column
, requirements.some_other_column
It's likely that we also want to add an ORDER BY clause so that the rows are returned in a predictable sequence.
Note that if there are no matching rows in requirements for a given Project_ID, then that project will not be returned. To return those rows, we can specify an outer join.
Current query:
SELECT projects.Project_ID
, projects.Name
FROM projects
JOIN assigned_users
ON assigned_users.Project_ID=projects.Project_ID
AND assigned_users.User_ID=4
Proposed query with outer join to requirements table:
SELECT projects.Project_ID
, projects.Name
, requirements.Requirement_ID
, requirements.Requirement_Name
FROM projects
JOIN assigned_users
ON assigned_users.Project_ID=projects.Project_ID
AND assigned_users.User_ID=4
LEFT
JOIN requirements
ON requirements.Project_ID = projects.Project_ID
ORDER
BY projects.Project_ID
, requirements.Requirement_ID
Equivalently, we could relocate the condition assigned_users.User_ID=4 from the ON clause of the inner join to a WHERE clause, before the ORDER BY.
You can use either:
SELECT *
FROM requirements req
INNER JOIN projects pro
ON req.Project_ID = pro.Project_ID
INNER JOIN assigned_users u
ON u.Project_ID=pro.Project_ID
WHERE u.User_ID=4;
or
SELECT *
FROM requirements
WHERE requirements.Project_ID=(
SELECT Project_ID
FROM projects
INNER JOIN assigned_users
ON assigned_users.Project_ID=projects.Project_ID AND assigned_users.User_ID=4;
);
I have three tables: stories, story_types, and comments
The following query retrieves all of the records in the stories table, gets their story_types, and the number of comments associated with each story:
SELECT s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM stories AS s
LEFT OUTER JOIN story_types AS t ON s.story_type_id = t.id
LEFT OUTER JOIN comments AS c ON s.id = c.story_id
GROUP BY s.id;
Now what I want to do is only retrieve a record from stories WHERE s.id = 1 (that's the primary key). I have tried the following, but it still returns all of the records:
SELECT s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM stories AS s
LEFT OUTER JOIN story_types AS t ON s.story_type_id = t.id
AND s.id = 1
LEFT OUTER JOIN comments AS c ON s.id = c.story_id
GROUP BY s.id;
I have also tried a WHERE clause at the end, which throws an error.
Can someone point out the correct syntax for a condition like this in this situation?
I'm using MySQL 5.1.47. Thanks.
I'm guessing you put the WHERE after the GROUP BY, which is illegal. See this reference on the SELECT syntax in MySQL.
Try this:
SELECT
s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM
stories AS s
LEFT JOIN story_types AS t ON s.story_type_id = t.id
LEFT JOIN comments AS c ON s.id = c.story_id
WHERE
s.id = 1
GROUP BY
s.id;
editor's note: I reformatted the code to highlight the query structure
Following up this comment on the accepted answer:
It is not intuitive to me that this WHERE would go in the second JOIN
This is just to outline how proper code formatting enhances understanding. Here is how I usually format SQL:
SELECT
s.id AS id,
s.story_date AS datetime,
s.story_content AS content,
t.story_type_label AS type_label,
t.story_type_slug AS type_slug,
COUNT(c.id) AS comment_count
FROM
stories AS s
LEFT JOIN story_types AS t ON t.id = s.story_type_id
LEFT OUTER JOIN comments AS c ON s.id = c.story_id
WHERE
s.id = 1
GROUP BY
s.id;
The WHERE is not on the second join. There is only one WHERE clause allowed in a SELECT statement, and it always is top level.
PS: Also note that in many database engines (apart from MySQL) it is illegal to use a GROUP BY clause and then selecting columns without aggregating them via functions like MIN(), MAX(), or COUNT(). IMHO this is bad style and a bad habit to get into.