SELECT deals.id,
deals.partner_id
FROM deals
LEFT JOIN deals_partners
ON ( deals.id = deals_partners.deal_id )
WHERE 1
AND ( `deals`.`partner_id` = 222
OR CASE
WHEN deals.partner_count = 1 THEN
deals_partners.partner_id = 222
end )
ORDER BY deals.id ASC
I would like to grab the deals that are associated to the partner.
They can either be master of the deal, deals.partner_id = 222 or they can have a row in deals_partners where they get linked to the deal.
The above works out for me, but gives me multiple of the same deals, because of the count of deals_partners I have. I made it a left join, I dont understand why it still keeps grabbing rows from the deals_partners?
Update:
table: deals, columns: ID, title, name, partner_id
table deals_partners, columns: deal_id, partner_id
I would like to display the partner 222's deals. To find out which deals, that the partner has, his partner_id can either be in the deal row, in the column partner_id or he can have a row in deals_partners where his partner_id is linked to the deal_id.
When you join to a table and match on multiple records, you will return multiple records. You can use the IN clause to prevent this.
SELECT deals.id,
deals.partner_id
FROM deals
WHERE `deals`.`partner_id` = 22
OR id in
(SELECT deal_id
FROM deals_partners
WHERE partner_id = 222)
ORDER BY deals.id ASC
Try something like: (hope it works)
SELECT deals.id,
deals.partner_id
FROM deals
LEFT JOIN deals_partners
ON deals.id = deals_partners.deal_id AND
deals_partners.partner_id = 222
WHERE `deals`.`partner_id` = 222 OR deals_partners.partner_id = 222
ORDER BY deals.id ASC
This assumes there is only one match for deals.id = deals_partners.deal_id AND deals_partners.partner_id = 222. Otherwise you'll need to add GROUP BY deals.id, deals.partner_id, deals_partners.partner_id.
Related
"employee" Table
emp_id
empName
1
ABC
2
xyx
"client" Table:
id
emp_id
clientName
1
1
a
2
1
b
3
1
c
4
2
d
"collection" Table
id
emp_id
Amount
1
2
1000
2
1
2000
3
1
1000
4
1
1200
I want to aggregate values from the three tables input tables here reported as samples. For each employee I need to find
the total collection amount for that employee (as a sum)
the clients that are involved with the corresponding employee (as a comma-separated value)
Here follows my current query.
MyQuery:
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
SUM(collection.Amount)
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN collection
ON collection.emp_id = employee.emp_id
GROUP BY employee.emp_id;
The problem of this query is that I'm getting wrong values of sums and clients when an employee is associated to multiple of them.
Current Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a,b,c,c,b,a,a,b,c
8400
2
xyz
d,d
1000
Expected Output:
emp_id
empName
clientName
TotalCollection
1
ABC
a , b , c
4200
2
xyz
d
1000
How can I solve this problem?
There are some typos in your query:
the separator inside the GROUP_CONCAT function should be a comma instead of a space, given your current output, though comma is default value, so you can really omit that clause.
each alias in your select requires the table where it comes from, as long as those field names are used in more than one tables among the ones you're joining on
your GROUP BY clause should at least contain every field that is not aggregated inside the SELECT clause in order to have a potentially correct output.
The overall conceptual problem in your query is that the join combines every row of the "employee" table with every row of the "client" table (resulting in multiple rows and higher sum of amounts during the aggregation). One way for getting out of the rabbit hole is a first aggregation on the "client" table (to have one row for each "emp_id" value), then join back with the other tables.
SELECT emp.emp_id,
emp.empName,
cl.clientName,
SUM(coll.Amount)
FROM employee emp
LEFT JOIN (SELECT emp_id,
GROUP_CONCAT(client.clientName
ORDER BY client.id) AS clientName
FROM client
GROUP BY emp_id) cl
ON cl.emp_id = emp.emp_id
LEFT JOIN (SELECT emp_id, Amount FROM collection) coll
ON coll.emp_id = emp.emp_id
GROUP BY emp.emp_id,
emp.empName,
cl.clientName
Check the demo here.
Regardless of my comment, here is a query for your desired output:
SELECT
a.emp_id,
a.empName,
a.clientName,
SUM(col.Amount) AS totalCollection
FROM (SELECT e.emp_id,
e.`empName`,
GROUP_CONCAT(DISTINCT c.clientName ORDER BY c.id ) AS clientName
FROM employee e
LEFT JOIN `client` c
ON c.emp_id = e.emp_id
GROUP BY e.`emp_id`) a
LEFT JOIN collection col
ON col.emp_id = a.emp_id
GROUP BY col.emp_id;
When having multiple joins, you should be careful about the relations and the number of results(rows) that your query generates. You might as well have multiple records in output than your desired ones.
Hope this helps
SELECT emp_id,
empName,
GROUP_CONCAT(client.clientName ORDER BY client.id SEPARATOR '') AS clientName,
C .Amount
FROM employee
LEFT JOIN client
ON clent.emp_id = employee.emp_id
LEFT JOIN (select collection.emp_id , sum(collection.Amount ) as Amount from collection group by collection.emp_id) C
ON C.emp_id = employee.emp_id
GROUP BY employee.emp_id;
it works for me now
I have 3 tables where one table has 3 columns with foreign keys to the other two tables.
table album_posters_albums-
+---------+---------+---------+
| album_id|poster_id|albums_id|
+---------+---------+---------+
| 49 | 167 | NULL |
| 49 | NULL | 45 |
+---------+---------+---------+
album_id and albums_id references the album table and poster_id represents the poster table.
I need to
SELECT * FROM poster
WHERE poster_id IN (
SELECT poster_id
FROM album_poster_albums
WHERE album_id=49);
IF the poster_id IS NULL:
SELECT * FROM album
WHERE album_id IN (
SELECT poster_id
FROM album_poster_albums
WHERE album_id=49).
The problem is I need to keep the posters and albums in the same order as they occur in the album_posters_albums table.
I was sending a query to get the list of ids, then looping through each result and querying the db to get either the poster or album but that is obviously very inefficient when I should be able to do it in one query.
It sounds like you want to use INNER JOINS
SELECT album.*, poster.*
FROM album_poster_albums
INNER JOIN album ON album_poster_albums.albums_id = album.album_id
INNER JOIN poster ON album_poster_albums.poster_id = poster.poster_id
WHERE album_poster_albums.album_id = 49
Based on your comment about one row with a poster and one row with an album, UNION ALL might be what you're looking for. (We'd need to see more details about the tables and a few more rows to understand the ordering part.) This should give you an album row then a poster row for each album id.
Caveats: The number and the orders of columns in the album and poster tables must be the same. Also, the data types of those columns must be the same or compatible. (I haven't used a UNION, or UNION ALL, in a very long time.)
SELECT * FROM (
SELECT album.*
FROM album_poster_albums
INNER JOIN album ON album_poster_albums.albums_id = album.album_id
WHERE album_poster_albums.album_id = 49
UNION ALL
SELECT poster.*
FROM album_poster_albums
INNER JOIN poster ON album_poster_albums.poster_id = poster.poster_id
WHERE album_poster_albums.album_id = 49
)
ORDER BY album_id
DECLARE #rowId INT(11);
SET #rowId :=0;
SELECT * FROM(SELECT #rowId:=#rowId+1,t.album_id,album.*
FROM album_poster_albums t
INNER JOIN album ON album.albums_id = t.albums_id
WHERE t.album_id = 49
UNION
SELECT #rowId:=#rowId + 2,t.album_id,poster.*
FROM album_poster_albums s
INNER JOIN poster ON poster.poster_id = t.poster_id
WHERE t.album_id = 49) T
ORDER BY #rowId,t.album_id
I decided to create a new table with an auto increment field based on #beltouche comment. My Mysql is pretty rusty and I thought there may be a way using case or if null. I didn't need the unique id previously with how I wrote the queries.
In hindsight the solution is obvious.
SELECT * FROM (SELECT albums.*, 1 AS type, t.id
FROM album_poster_album t
INNER JOIN albums ON albums.album_id = t.albums_id
WHERE t.album_id = 49
UNION ALL
SELECT poster.*, 2 AS type, s.id
FROM album_poster_album s
INNER JOIN poster ON poster.posterID = s.poster_id
WHERE s.album_id = 49) T
ORDER BY t.id
I have a relation between users and groups. Users can be in a group or not.
EDIT : Added some stuff to the model to make it more convenient.
Let's say I have a rule to add users in a group considering it has a specific town, and a custom metadata like age 18).
Curently, I do that to know which users I have to add in the group of the people living in Paris who are 18:
SELECT user.id AS 'id'
FROM user
LEFT JOIN
(
SELECT user_id
FROM user_has_role_group
WHERE role_group_id = 1 -- Group for Paris
)
AS T1
ON user.id = T1.user_id
WHERE
(
user.town = 'Paris' AND JSON_EXTRACT('custom_metadata', '$.age') = 18
)
AND T1.user_id IS NULL
It works & gives me the IDs of the users to insert in group.
But when I have 50 groups to proceed, like for 50 town or various ages, it forces me to do 50 requests, it's very slow and not efficient for my Database.
How could I generate a result for each group ?
Something like :
role_group_id user_to_add
1 1
1 2
2 1
2 3
The only way I know to do that for now is to do an UNION on several sub queries like the one above, but of course it's very slow.
Note that the custom_metadata field is a user defined field. I can't create specific columns or tables.
Thanks a lot for your help.
if I good understood you:
select user.id, grp.id
from user, role_group grp
where (user.id, grp.id) not in (select user_id, role_group_id from user_has_role_group) and user.town in ('Paris', 'Warsav')
that code give list of users and group which they not belong from one of towns..
To add the missing entries to user_has_role_group, you might want to have some mapping between those town names and their group_id's.
The example below is just using a subquery with unions for that.
But you could replace that with a select from a table.
Maybe even from role_group, if those names correlate with the user town names.
insert into user_has_role_group (user_id, group_id)
select u.user_id, g.group_id
from user u
join (
select 'Paris' as name, 1 as group_id union all
select 'Rome', 2
-- add more towns here
) g on (u.town = g.name)
left join user_has_role_group ug
on (ug.user_id = u.user_id and ug.role_group_id = g.group_id)
where u.town in ('Paris','Rome') -- add more towns here
and json_extract(u.custom_metadata, '$.age') = 18
and ug.id is null;
I cannot find the answer to my problem here on stackoverflow. I have a query that spans 3 tables:
newsitem
+------+----------+----------+----------+--------+----------+
| Guid | Supplier | LastEdit | ShowDate | Title | Contents |
+------+----------+----------+----------+--------+----------+
newsrating
+----+----------+--------+--------+
| Id | NewsGuid | UserId | Rating |
+----+----------+--------+--------+
usernews
+----+----------+--------+----------+
| Id | NewsGuid | UserId | ReadDate |
+----+----------+--------+----------+
Newsitem obviously contains newsitems, newsrating contains ratings that users give to newsitems, and usernews contains the date when a user has read a newsitem.
In my query I want to get every newsitem, including the number of ratings for that newsitem and the average rating, and how many times that newsitem has been read by the current user.
What I have so far is:
select newsitem.guid, supplier, count(newsrating.id) as numberofratings,
avg(newsrating.rating) as rating,
count(case usernews.UserId when 3 then 1 else null end) as numberofreads from newsitem
left join newsrating on newsitem.guid = newsrating.newsguid
left join usernews on newsitem.guid = usernews.newsguid
group by newsitem.guid
I have created an sql fiddle here: http://sqlfiddle.com/#!9/c8add/8
Both count() calls don't return the numbers I want. numberofratings should return the total number of ratings for that newsitem (by all users). numberofreads should return the number of reads for the current user for that newsitem.
So, newsitem with guid d104c330-c319-40e8-8be3-a7c4f549d35c should have 2 ratings and 3 reads for the current user with userid = 3.
I have tried conditional counts and sums, but no success yet. How can this be accomplished?
The main problem that I see is that you're joining in both tables together, which means that you're going to effectively be multiplying out by both numbers, which is why your counts aren't going to be correct. For example, if the Newsitem has been read 3 times by the user and rated by 8 users then you're going to end up getting 24 rows, so it will look like it has been rated 24 times. You can add a DISTINCT to your COUNT of the ratings IDs and that should correct that issue. Average should be unaffected because the average of 1 and 2 is the same as the average of 1, 1, 2, & 2 (for example).
You can then handle the reads by adding the userid to the JOIN condition (since it's an OUTER JOIN it shouldn't cause any loss of results) instead of in a CASE statement for your COUNT, then you can do a COUNT on distinct id values from Usernews. The resulting query would be:
SELECT
I.guid,
I.supplier,
COUNT(DISTINCT R.id) AS number_of_ratings,
AVG(R.rating) AS avg_rating,
COUNT(DISTINCT UN.id) AS number_of_reads
FROM
NewsItem I
LEFT OUTER JOIN NewsRating R ON R.newsguid = I.guid
LEFT OUTER JOIN UserNews UN ON
UN.newsguid = I.guid AND
UN.userid = #userid
GROUP BY
I.guid,
I.supplier
While that should work, you might get better results from a subquery, as the above needs to explode out the results and then aggregate them, perhaps unnecessarily. Also, some people might find the below to be a little clearer.
SELECT
I.guid,
I.supplier,
R.number_of_ratings,
R.avg_rating,
COUNT(*) AS number_of_reads
FROM
NewsItem I
LEFT OUTER JOIN
(
SELECT
newsguid,
COUNT(*) AS number_of_ratings,
AVG(rating) AS avg_rating
FROM
NewsRating
GROUP BY
newsguid
) R ON R.newsguid = I.guid
LEFT OUTER JOIN UserNews UN ON UN.newsguid = I.guid AND UN.userid = #userid
GROUP BY
I.guid,
I.supplier,
R.number_of_ratings,
R.avg_rating
I'm with Tom you should use a subquery to calculate the user count.
SQL Fiddle Demo
SELECT NI.guid,
NI.supplier,
COUNT(NR.ID) as numberofratings,
AVG(NR.rating) as rating,
user_read as numberofreads
FROM newsitem NI
LEFT JOIN newsrating NR
ON NI.guid = NR.newsguid
LEFT JOIN (SELECT NewsGuid, COUNT(*) user_read
FROM usernews
WHERE UserId = 3 -- use a variable #user_id here
GROUP BY NewsGuid) UR
ON NI.guid = UR.NewsGuid
GROUP BY NI.guid,
NI.supplier,
numberofreads;
I am trying to optimise my php by doing as much work on the MySQL server as possible. I have this sql query which is pulling data out of a leads table, but at the same time joining two tags tables to combine the result. I am looking to add a company which is linked through a relations table.
So the table that holds the relationship between the two is relations_value which simply states (I add example data)
parenttable (companies) | parentrecordid (10) | childtable (leads) | childrecordid (1)
the companies table has quite a few columns but the only two relevant are;
id (10) | companyname (my company name)
So this query currently grabs everything I need but I want to bring the companyname into the query:
SELECT leads.id,
GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status,
leads.probability
FROM `gs_db_1002`.leads
LEFT JOIN ( SELECT *
FROM tags_module
WHERE tagid IN ( SELECT id
FROM tags
WHERE moduleid = 'leads' ) ) as b
ON leads.id = b.recordid
LEFT JOIN `gs_db_1002`.tags as c
ON b.tagid = c.id
GROUP BY leads.id,
leads.status,
leads.probability
I need to be able to go into the relations_values table and pull parenttable and parentrecordid by selecting childtable = leads and childrecordid = 1 and somehow join these so that I am able to get companyname as a column in the above query...
Is this possible?
I have created a sqlfiddle: sqlfiddle.com/#!2/023fa/2 So I am looking to add companies.companyname as column to the query.
I don't know what your primary keys and foreign keys are that link each table together.. if you could give a better understanding of what ID's are linked to eachother it would make this a lot easier... however i did something that does return the correct result... but since all of the ID's are = 1 then it could be incorrect.
SELECT
leads.id, GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status, leads.probability, companyname
FROM leads
LEFT JOIN (
SELECT * FROM tags_module WHERE tagid IN (
SELECT id FROM tags WHERE moduleid = 'leads' )
) as b ON leads.id = b.recordid
LEFT JOIN tags as c ON b.tagid = c.id
LEFT JOIN relations_values rv on rv.id = b.recordid
LEFT JOIN companies c1 on c1.createdby = rv.parentrecordid
GROUP BY leads.id,leads.status, leads.probability