How to count using LEFT JOIN? - mysql

I need to make a list with the company that scans the most things... I've got two tables, Exhibitors and Leads
Leads contain each "action" that every exhibitors does. Sometimes, in the Exhibitors table, there's two accounts for the same company. For example, PepsiCO might have two accounts.
The key/col that link both is called ExhibitorID.
I need to retrieve the top 20 with the most leads (so COUNT(*) in leads) but I need to group by Company in Exhibitors table.
Curerntly I am using this :
SELECT t2.ExhibitorID, t2.Company, t2.Username, t1.Count
FROM exhibitors AS t2
, ( SELECT ExhibitorID, COUNT( * ) AS Count
FROM leads AS l
WHERE l.ContractorID = 100
AND l.ShowID = "20120228AB"
GROUP BY l.ExhibitorID
ORDER BY COUNT(*) DESC
LIMIT 20 ) AS t1
WHERE t2.ExhibitorID = t1.ExhibitorID
ORDER BY t1.Count DESC
But it doesn't group by company. Thanks for all your help.

select e.exhibitorid, count(l.leadid)
from leads l
inner join exhibitors on e.exhibitorid = l.exhibitorid
and l.showid = "20120228AB" and l.contractorid = 100
group by e.exhibitorid
order by count(l.contractorid) DESC
limit 20;
If there are ties, this will still only return the top 20. So if you want ties then there appears to be a MySql workaround here MySQL's alternative to T-SQL's WITH TIES

SELECT
e.Company,
COUNT(l.LeadId) AS Actions
FROM
Exhibitors AS e
LEFT JOIN Leads AS l
ON l.ExhibitorID = e.ExhibitorID
AND l.ContractorID = 100
AND l.ShowID = "20120228AB"
GROUP BY e.Company
ORDER BY Actions DESC
LIMIT 20;

You could group on Company and join on ExhibitorID:
select e.Company
, count(distinct l.LeadID) as LeadCount
from Exhibitors e
join Leads l
on l.ExhibitorID = e.ExhibitorID
and l.ShowID = "20120228AB"
and l.ContractorID = 100
group by
e.Company
order by
LeadCount DESC
limit 20

Related

Searching and Sorting Using MySQL Inner Join

I guess I can't explain my problem properly. I want to explain this to you with a picture.
Picture 1
In the first picture you can see the hashtags in the trend section. These hashtags are searched for the highest total and it is checked whether the date has passed. If valid data is available, the first 5 hashtags are taken.
Picture 2
In the second picture, it is checked whether the posts in the hashtag are in the post, if any, the oldest date value is taken, LIMIT is set to 1 and the id value from the oyuncular table is matched with sid. Thus, the name of the person sharing can be accessed.
Picture 3
My English is a little bad, I hope I could explain it properly.
SELECT
social_trend.hashtag,
social_trend.total,
social_trend.tarih,
social_post.sid,
social_post.tarih,
social_post.post,
oyuncular.id,
oyuncular.isim
FROM
social_trend
INNER JOIN
social_post
ON
social_post.post LIKE '%social_trend.hashtag%' ORDER BY social_post.tarih LIMIT 1
INNER JOIN
oyuncular
ON
oyuncular.id = social_post.sid
WHERE
social_trend.tarih > UNIX_TIMESTAMP() ORDER BY social_trend.total DESC LIMIT 5
YOu should use a sibquery
and add a proper join between subqiery and social_trend
(i assumed sing both sid)
SELECT
social_trend.hashtag,
social_trend.total,
social_trend.tarih,
t.sid,
t.tarih,
t.post,
oyuncular.id,
oyuncular.isim
FROM (
select social_post.*
from social_post
INNER JOIN social_trend ON social_post.post LIKE concat('%',social_trend.hashtag,'%' )
ORDER BY social_post.tarih LIMIT 1
) t
INNER JOIN social_trend ON social_trend.hashtag= t.post
INNER JOIN oyuncular ON oyuncular.id = t.sid
WHERE
social_trend.tarih > UNIX_TIMESTAMP() ORDER BY social_trend.total DESC LIMIT 5
but looking to your new explanation and img seems you need
SELECT
t.hashtag,
t.total,
t.tarih_trend,
t.sid,
t.tarih,
t.post,
oyuncular.id,
oyuncular.isim
FROM (
select social_post.sid
, social_post.tarih
, social_post.post
, st.hashtag
, st.total
, st.tarih tarih_trend
from social_post
INNER JOIN (
select * from social_trend
WHERE social_trend.tarih > UNIX_TIMESTAMP()
order by total DESC LIMIT 5
) st ON social_post.post LIKE concat('%',st.hashtag,'%' )
ORDER BY social_post.tarih LIMIT 5
) t
INNER JOIN oyuncular ON oyuncular.id = t.sid

How to display only the multiple maximal values of my result table?

Using this MySQL queries on my database:
SELECT movie.name, SUM(heroes.likes) AS 'success'
FROM heroebymovie JOIN
heroes
ON heroes.ID = heroebymovie.heroID JOIN
movie
ON movie.ID = heroebymovie.movieID
GROUP BY movie.ID
ORDER BY SUM(heroes.likes) DESC
I obtain this result:
|name |success |
|Avengers 2 |72317559 |
|Avengers |72317559 |
|Captain America : Civil War|67066832 |
I would like to display only the movies with the highest number of “success” (in this case “Avengers 2” and “Avengers”).
Can someone explain the way of doing it?
A simple way is using an having clause that filter for the max value ( in this case the ordered list of sum desc limit 1)
SELECT movie.name, SUM(heroes.likes) AS success
FROM heroebymovie JOIN heroes ON heroes.ID = heroebymovie.heroID
JOIN movie ON movie.ID = heroebymovie.movieID
GROUP BY movie.ID
HAVING success = (
SELECT SUM(heroes.likes)
FROM heroebymovie JOIN heroes ON heroes.ID = heroebymovie.heroID
JOIN movie ON movie.ID = heroebymovie.movieID
GROUP BY movie.ID
ORDER BY SUM(heroes.likes) DESC
LIMIT 1
)
ORDER BY SUM(heroes.likes) DESC
You are looking for a limit, but want to consider ties. MySQL supports the LIMIT clause, but unfortunately no accompanying ties expression.
In standard SQL you would simply add
FETCH 1 ROW WITH TIES;
and be done with it. (SQL Server does the same with TOP(1) WITH TIES.)
Another way would be to use standard SQL's MAX OVER: MAX(SUM(heroes.likes)) OVER() and only keep rows where the sum matches the maximum. Or use RANK OVER. But again, MySQL doesn't support either of these.
So your main option is to execute the query twice, like in this pseudo code:
select sum ... having sum = (select max(sum) ...)
An easy way to get the max of the sums in MySQL is to order by sums descending and limit the results to one row.
SELECT m.name, SUM(h.likes) AS "success"
FROM heroebymovie hm
JOIN heroes h ON h.ID = hm.heroID
JOIN movie m ON m.ID = hm.movieID
GROUP BY m.ID
HAVING SUM(h.likes) =
(
SELECT SUM(h2.likes)
FROM heroebymovie hm2
JOIN heroes h2 ON h2.ID = hm2.heroID
GROUP BY hm2.movieID
ORDER BY SUM(h2.likes) DESC
LIMIT 1
);

SQL query that combines 2 into 1, specifically it counts the number of people in each group for each group

I've found a few posts in here that are similar, but doesn't work with what i'd like to do...
similar post: Trying to write a query that counts multiple things with different where cases
similar post: Query that Counts records with a WHERE clause
what I want to do is I have some... 200 groups, and within those groups are people with specific application dates. I want a count of how many people are in those groups that have a application date that falls within a specific range.
So this is the first method i've been using, but it only works for 1 group at a time
SELECT count(*) as count
FROM membersapplication ma
INNER JOIN members mb on mb.mbr_id = ma.mbr_id
WHERE (GPL_ID = 20179) and (ma.mpl_effectivedate >= '2/01/2015' and ma.mpl_effectivedate <= '4/30/2015') and (ma.mpl_cancellationdate is null)
This code takes the count of anyone that falls under GPL_ID 20179 (group placement id), i have 200 GPL_ID's that I would like this to run for, there is never a duplicate GPL_ID.
SELECT Gr.GPL_ID, Gr.GPL_Effectivedate, G.GRP_Enrolltype, G.GRP_Name, G.GRP_ID, G.GRP_Executive
FROM groupsreview gr
INNER JOIN groups g on gr.grp_ID = g.grp_ID
WHERE (GRP_ENROLLTYPE = 1) and (gp.gpl_effectivedate >= '4/30/2014' and gp.gpl_effectivedate <= '4/30/2015')
order by grp_name asc
This code gives me a list of every GPL_ID that I want (based off GRP_Enrolltype = 1) that falls within my desired date range
I basically would like to combine the two codes so that the 2nd set of code adds another column that has a count based off the fist code
Seems you really just need add GROUP BY to your query:
SELECT ma.GPL_ID, count(*) as count
FROM membersapplication ma
INNER JOIN members mb
ON mb.mbr_id = ma.mbr_id
where (ma.mpl_effectivedate >= '2/01/2015' and ma.mpl_effectivedate <= '4/30/2015')
AND (ma.mpl_cancellationdate is null)
GROUP BY ma.GPL_ID
This should do it. But I would double check the dates, I just used the ones you supplied; they don't match, and I am not sure if they should:
SELECT ma.GPL_ID, count(*) as count
FROM groups g
INNER JOIN groupsreview AS gr ON g.grp_ID = gr.grp_ID
INNER JOIN membersapplication AS ma ON gr.GPL_ID = ma.GPL_ID
INNER JOIN members AS mb ON mb.mbr_id = ma.mbr_id
WHERE g.GRP_ENROLLTYPE = 1
AND gr.gpl_effectivedate BETWEEN 20140430 AND 20150430
AND ma.mpl_effectivedate BETWEEN 20150201 and 20150430
AND ma.mpl_cancellationdate IS NULL
GROUP BY ma.GPL_ID
;
Judging from your question's wording, it feels a little odd to group by GPL_ID instead of grp_ID.
Not sure if this will work, but I can give it a try:
SELECT
*
FROM
(SELECT
count(*) as count, GPL_ID
FROM
membersapplication ma
inner join members mb ON mb.mbr_id = ma.mbr_id
where
(ma.mpl_effectivedate >= '2/01/2015'
and ma.mpl_effectivedate <= '4/30/2015')
and (ma.mpl_cancellationdate is null)
GROUP BY GPL_ID) T1
INNER JOIN
(SELECT
Gr.GPL_ID,
Gr.GPL_Effectivedate,
G.GRP_Enrolltype,
G.GRP_Name,
G.GRP_ID,
G.GRP_Executive
FROM
groupsreview gr
inner join groups g ON gr.grp_ID = g.grp_ID
WHERE
(GRP_ENROLLTYPE = 1)
and (gp.gpl_effectivedate >= '4/30/2014'
and gp.gpl_effectivedate <= '4/30/2015')) T2
ON T1.GPL_ID = T2.GPL_ID
Basically you should approach this by combining joins and then grouping on GPL_ID along with a having clause. Here's what came up with.
SELECT Gr.GPL_ID, Gr.GPL_Effectivedate, G.GRP_Enrolltype, G.GRP_Name, G.GRP_ID, G.GRP_Executive
count(*) as grp_count
FROM membersapplication ma
INNER JOIN members mb on mb.mbr_id = ma.mbr_id
INNER JOIN groupsreview gr on mb.GPL_ID = gr.GPL_ID
INNER JOIN groups g on gr.grp_ID = g.grp_ID
WHERE (GRP_ENROLLTYPE = 1) and (gp.gpl_effectivedate >= '4/30/2014' and gp.gpl_effectivedate <= '4/30/2015')
GROUP BY Gr.GPL_ID, Gr.GPL_Effectivedate, G.GRP_Enrolltype, G.GRP_Name, G.GRP_ID, G.GRP_Executive
HAVING (ma.mpl_effectivedate >= '2/01/2015' and ma.mpl_effectivedate <= '4/30/2015') and (ma.mpl_cancellationdate is null)
order by grp_name asc
Hopefully this helps

How to expand MySQL subquery/join to include all rows

This query below is perfect in producing the result for horse_id = 1 ... but I want to do this for all horses in the database. Can anyone share with me how to tweak this query so I can do that?
SELECT figures.entry_id,
max(figures.beyer)
FROM
( SELECT hrdb_lines.horse_id,
hrdb_entries.entry_id,
hrdb_lines.beyer
FROM hrdb_entries
INNER JOIN hrdb_lines
ON hrdb_lines.horse_id = hrdb_entries.horse_id
WHERE hrdb_lines.horse_id = 1
ORDER BY hrdb_lines.line_date DESC
LIMIT 2
) as figures
Perhaps I'm doing it all wrong too.
I think the following would generate the desired results:
SELECT `entry_id`, `beyer`
FROM (SELECT hrdb_entries.entry_id,
MAX( hrdb_lines.beyer )
FROM hrdb_entries
INNER JOIN hrdb_lines
ON hrdb_lines.horse_id = hrdb_entries.horse_id
GROUP BY hrdb_lines.horse_id
ORDER BY hrdb_lines.line_date DESC
) AS figures
If I'm understanding your question, something like this should be close:
SELECT
figures.horse_id,
figures.entry_id,
max(figures.beyer)
FROM
(SELECT
hrdb_lines.horse_id,
hrdb_entries.entry_id,
hrdb_lines.beyer
FROM hrdb_entries
INNER JOIN hrdb_lines ON hrdb_lines.horse_id = hrdb_entries.horse_id
ORDER BY hrdb_lines.line_date DESC
) as figures
GROUP BY figures.horse_id
One option to limit the MAX to just the most recent 2 beyer fields is to add a row number to the results and only include rows 1 and 2.
SELECT
figures.horse_id,
figures.entry_id,
max(figures.beyer)
FROM
(SELECT
#rn:=if(#prev_horse_id=horse_id,#rn+1,1) rn,
hrdb_lines.horse_id,
hrdb_entries.entry_id,
hrdb_lines.beyer,
#prev_horse_id:=hrdb_lines.horse_id
FROM hrdb_entries
INNER JOIN hrdb_lines ON hrdb_lines.horse_id = hrdb_entries.horse_id
INNER JOIN (SELECT #rn:=0) r
ORDER BY hrdb_lines.horse_id, hrdb_lines.line_date DESC
) as figures
WHERE rn <= 2
GROUP BY figures.horse_id

sql query very slow when another table gets fuller

I have the following query, but after some time when users start putting in more and more items in the "ci_falsepositives" table, it gets really slow.
The ci_falsepositives table contains a reference field from ci_address_book and another reference field from ci_matched_sanctions.
How can I create a new query but still being able to sort on each field.
For example I can still sort on "hits" or "matches"
SELECT *, matches - falsepositives AS hits
FROM (SELECT c.*, IFNULL(p.total, 0) AS matches,
(SELECT COUNT(*)
FROM ci_falsepositives n
WHERE n.addressbook_id = c.reference
AND n.sanction_key IN
(SELECT sanction_key FROM ci_matched_sanctions)
) AS falsepositives
FROM ci_address_book c
LEFT JOIN
(SELECT addressbook_id, COUNT(match_id) AS total
FROM ci_matched_sanctions
GROUP BY addressbook_id) AS p
ON c.id = p.addressbook_id
) S
ORDER BY folder asc, wholename ASC
LIMIT 0,15
The problem has to be the SELECT COUNT(*) FROM ci_falsepositives sub-query. That sub-query can be written using an inner join between ci_falsepositives and ci_matched_sanctions, but the optimizer might do that for you anyway. What I think you need to do, though, is make that sub-query into a separate query in the FROM clause of the 'next query out' (that is, SELECT c.*, ...). Probably, that query is being evaluated multiple times - and that's what's hurting you when people add records to ci_falsepositives. You should study the query plan carefully.
Maybe this query will be better:
SELECT *, matches - falsepositives AS hits
FROM (SELECT c.*, IFNULL(p.total, 0) AS matches, f.falsepositives
FROM ci_address_book AS c
JOIN (SELECT n.addressbook_id, COUNT(*) AS falsepositives
FROM ci_falsepositives AS n
JOIN ci_matched_sanctions AS m
ON n.sanction_key = m.sanction_key
GROUP BY n.addressbook_id
) AS f
ON c.reference = f.addressbook_id
LEFT JOIN
(SELECT addressbook_id, COUNT(match_id) AS total
FROM ci_matched_sanctions
GROUP BY addressbook_id) AS p
ON c.id = p.addressbook_id
) AS s
ORDER BY folder asc, wholename ASC
LIMIT 0, 15