group by count from left joined table - mysql

Here's my query:
$select d.*,
count(a.id) as delivered
from `dealerships` as d
left join `assignments` as a on (a.id_dealership = d.id)
group by d.id
order by d.name asc
now this works, but it is counting duplicate leads. when I add a.id_lead to the group by, it messes up everything. There is a column in the assignments table called id_lead and I want the count() (delivered) to count the total of assignments grouped by id_lead, so that it ignores more than 1 row with the same id_lead.

Is this what you mean? :
select d.*,
count(distinct a.id_lead) as delivered
from `dealerships` as d
left join `assignments` as a on (a.id_dealership = d.id)
group by d.id
order by d.name asc
It's the same as your query, except that instead of counting the total number of records in a, it will only count the number of distinct non-null values in a.id_lead.
(If that's not what you mean, then please clarify.)

Related

Select highest value per group and associated row from another table, ordered by highest value

I'm trying to get these values from two tables, joining on member id. In the table with the points values, there are running total rows for each sale by each member. I need the member's point total associated with their row in the members table, sorted by value descending. This is my current query, it returns unique values unless a member has two identical entries.
SELECT m.id
, m.fname
, m.lname
, p.points_total
FROM
( SELECT s.member_id
, MAX(s.points_total) points_total
FROM sale_sale s
GROUP
BY s.member_id
) p
JOIN sale_sale x
ON x.member_id = p.member_id
AND x.points_total = p.points_total
JOIN member_member m
ON m.id = p.member_id
WHERE x.site_id = 1
AND m.fname != "Sales"
ORDER
BY p.points_total DESC;
A simple JOIN and GROUP BY would likely do what you're asking for:
SELECT
m.id,
m.fname,
m.lname,
COALESCE(MAX(s.points_total), 0) AS points_total
FROM member_member AS m
LEFT JOIN sale_sale AS s
ON m.id = s.member_id
AND s.site_id = 1
WHERE m.fname != "Sales"
GROUP BY m.id
ORDER BY points_total DESC;
EDIT: Made it a LEFT JOIN with COALESCE(points_total, 0) to allow for members who have no sales totals to show in the results. If you don't want this, you could change LEFT JOIN to INNER JOIN and eliminate the COALESCE function.

Order by and Limit in a multiple table join using Group By to do a Limit by and Max

Select group_concat(ID SEPARATOR '|')
from TableJ J
Left Join TableL L on L.J_ID=J.ID
Left Join TableB B on B.LJ_ID=L.F_ID
Left Join TableLJ LJ on LJ.ID=L.F_ID
Group
by J.ID
The output returns records like
103237|43775|84462|19153|54618|108646|50142|96946|37251|75984|54524
29728|46758|65987|20772|34513|61323|2778|32630|53616|103450|27152|37278|65950|13837|33500|59490
etc
The issue is I need to limit the # of records/pipes in the results to 5 records.
In TableLJ there is a field Population which I can use to sort and limit but no amount of fiddling allows me to specify I want to Select the top 5 from that table PER group.
Update is that I can do the following to limit the group_concat (still does not solve getting them sorted first by LJ.Population)
substring_index(group_concat(ID SEPARATOR '|'),'|',5)
Group_Concat() function has an optional Order By clause, using which we can sort the column value(s) to concat, based on another/same column(s).
You can use Substring_Index() function to restrict the concatenated string to 5 per group.
Try:
SELECT Substring_Index(Group_concat(ID
ORDER BY LJ.Population ASC
SEPARATOR '|'),
'|',
5)
FROM TableJ AS J
LEFT JOIN TableL AS L
ON L.J_ID = J.ID
LEFT JOIN TableB AS B
ON B.LJ_ID = L.F_ID
LEFT JOIN TableLJ AS LJ
ON LJ.ID = L.F_ID
GROUP BY J.ID
In MySQL 8.0.2 and onwards, we can utilize Window Functions, to determine Row_Number(), within a partition for J.ID. We can sort by LJ.Population to get the row number accordingly.
Now, use this result-set as a Derived Table and consider only the rows upto row number 5. You can then do Group By and Group_Concat() accordingly.
SELECT Group_Concat(dt.ID ORDER BY dt.rownumber ASC SEPARATOR '|')
FROM (
SELECT J.ID,
ROW_NUMBER() OVER (PARTITION BY J.ID
ORDER BY LJ.Population ASC) AS rownumber
FROM TableJ AS J
LEFT JOIN TableL AS L
ON L.J_ID = J.ID
LEFT JOIN TableB AS B
ON B.LJ_ID = L.F_ID
LEFT JOIN TableLJ AS LJ
ON LJ.ID = L.F_ID ) AS dt
WHERE dt.rownumber <= 5
GROUP BY dt.ID

emulated of full join return wrong values

I have two tables that doesn't have relationship and I do a left and right join to simulate a full join on them and select some data.
the manner of displaying data is right but values are wrong, its looks like they selected more than once.
my tables are something like this :
TABLE 1 (bargains)
trade_date ---- profit
TABLE 2 (general_cost)
date ----- cost
this is the query that i write :
select b.trade_date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0)
from bargains as b
left join general_cost as g on b.trade_date = g.date group by b.trade_date
union
select g.date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0) from
bargains as b
right join general_cost as g on b.trade_date = g.date group by g.date
this is the result of query :
for example in date 1395-9-28 the sum of profit column should be 440 and sum of cost column should be 800
if it's help you should know that there is three row with this date in bargains table and two row in general_cost table
Yes, your query duplicates the matching records because those are included in both left and the right join. You need to exclude the matching records from one of the queries. I usually exclude them from the 2nd query of the union:
select b.trade_date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0)
from bargains as b
left join general_cost as g on b.trade_date = g.date group by b.trade_date
union
select g.date, coalesce(sum(b.profit),0), coalesce(sum(g.cost),0) from
bargains as b
right join general_cost as g on b.trade_date = g.date
where b.date is null //include only the records from general_cost that are not matched
group by g.date
UPDATE
If you have multiple records in both tables with the same date, then you need to do the summing per table separately in subqueries, otherwise the matching records do get duplicated:
select b.trade_date, b.profit, coalesce(g.cost,0)
from (select trade_date, sum(profit) as profit from bargains group by trade_date) as b
left join (select date, sum(cost) as cost from general_cost group by date) as g on b.trade_date = g.date
union
select g.date, 0, sum(g.cost) from //all profits has been summed up in the above query, so here we can use 0 in place of profit
bargains as b
right join general_cost as g on b.trade_date = g.date
where b.trade_date is null //include only the records from general_cost that are not matched
group by g.date

MySQL Help Cleaning Total Counts

Having an issue with a JOIN statement.
I'm trying to get a total per name, and not the current 1 with a ton of other same name records
SELECT a.`name`,
(SELECT COUNT(b.`id`)
FROM `host1_hosting` AS b
WHERE b.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`) AS HostingCount
FROM `host1_servers` AS a
LEFT JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
ORDER BY a.`name`
Example Returned
Name HostingCount
Name 1
Name 1
Name 1
Where it should be:
Name 3
I'm sure this is simple, but it's early monday, and I'm foggy
Query 2
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` AS a
JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
JOIN `host1_hosting` AS d ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
ORDER BY a.`name`
Gets me 1 name record, but a total of all COUNT
Your second query needs a group by:
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` AS a
JOIN `host1_servers_host1_hosting_1_c` AS c ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id`
JOIN `host1_hosting` AS d ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
GROUP BY a.name
ORDER BY a.`name`;
Without the GROUP BY, MySQL interprets the query as an aggregation query to produce one row. The count() is the overall count. The column name is chosen arbitrarily from one of the rows (using a MySQL extension that wouldn't work in any other database).
EDIT:
If you want to keep all names from the first table and do the count, use left outer join:
SELECT a.`name`, COUNT(d.`id`)
FROM `host1_servers` a LEFT OUTER JOIN
`host1_servers_host1_hosting_1_c` c
ON c.`host1_servers_host1_hosting_1host1_servers_ida` = a.`id` LEFT OUTER JOIN
`host1_hosting` d
ON d.`id` = c.`host1_servers_host1_hosting_1host1_hosting_idb`
GROUP BY a.name
ORDER BY a.`name`;

MySQL Query not displaying correctly

I am having to set up a query that retrieves the last comment made on a customer, if no one has commented on them for more than 4 weeks. I can make it work using the query below, but for some reason the comment column won't display the latest record. Instead it displays the oldest, however the date shows the newest. It may just be because I'm a noob at SQL, but what exactly am I doing wrong here?
SELECT DISTINCT
customerid, id, customername, user, MAX(date) AS 'maxdate', comment
FROM comments
WHERE customerid IN
(SELECT DISTINCT id FROM customers WHERE pastdue='1' AND hubarea='1')
AND customerid NOT IN
(SELECT DISTINCT customerid FROM comments WHERE DATEDIFF(NOW(), date) <= 27)
GROUP BY customerid
ORDER BY maxdate
The first "WHERE" clause is just ensuring that it shows only customers from a specific area, and that they are "past due enabled". The second makes sure that the customer has not been commented on within the last 27 days. It's grouped by customerid, because that is the number that is associated with each individual customer. When I get the results, everything is right except for the comment column...any ideas?
Join much better to nested query so you use the join instead of nested query
Join increase your speed
this query resolve your problem.
SELECT DISTINCT
customerid,id, customername, user, MAX(date) AS 'maxdate', comment
FROM comments inner join customers on comments.customerid = customers.id
WHERE comments.pastdue='1' AND comments.hubarea='1' AND DATEDIFF(NOW(), comments.date) <= 27
GROUP BY customerid
ORDER BY maxdate
I think this might probably do what you are trying to achieve. If you can execute it and maybe report back if it does or not, i can probably tweak it if needed. Logically, it ' should' work - IF i have understood ur problem correctly :)
SELECT X.customerid, X.maxdate, co.id, c.customername, co.user, co.comment
FROM
(SELECT customerid, MAX(date) AS 'maxdate'
FROM comments cm
INNER JOIN customers cu ON cu.id = cm.customerid
WHERE cu.pastdue='1'
AND cu.hubarea='1'
AND DATEDIFF(NOW(), cm.date) <= 27)
GROUP BY customerid) X
INNER JOIN comments co ON X.customerid = co.customerid and X.maxdate = co.date
INNER JOIN customer c ON X.customerid = c.id
ORDER BY X.maxdate
You need to have subquery for each case.
SELECT a.*
FROM comments a
INNER JOIN
(
SELECT customerID, max(`date`) maxDate
FROM comments
GROUP BY customerID
) b ON a.customerID = b.customerID AND
a.`date` = b.maxDate
INNER JOIN
(
SELECT DISTINCT ID
FROM customers
WHERE pastdue = 1 AND hubarea = 1
) c ON c.ID = a.customerID
LEFT JOIN
(
SELECT DISTINCT customerid
FROM comments
WHERE DATEDIFF(NOW(), date) <= 27
) d ON a.customerID = d.customerID
WHERE d.customerID IS NULL
The first join gets the latest record for each customer.
The second join shows only customers from a specific area, and that they are "past due enabled".
The third join, which uses LEFT JOIN, select all customers that has not been commented on within the last 27 days. In this case,only records without on the list are selected because of the condition d.customerID IS NULL.
But tomake your query shorter, if the customers table has already unique records for customer, then you don't need to have subquery on it.Directly join the table and put the condition on the WHERE clause.
SELECT a.*
FROM comments a
INNER JOIN
(
SELECT customerID, max(`date`) maxDate
FROM comments
GROUP BY customerID
) b ON a.customerID = b.customerID AND
a.`date` = b.maxDate
INNER JOIN customers c
ON c.ID = a.customerID
LEFT JOIN
(
SELECT DISTINCT customerid
FROM comments
WHERE DATEDIFF(NOW(), date) <= 27
) d ON a.customerID = d.customerID
WHERE d.customerID IS NULL AND
c.pastdue = 1 AND
c.hubarea = 1
Two of your table columns are not contained in either an aggregate function or the GROUP BY clause. for example suppose that you have two data rows with the same customer id and same date, but with different comment data. how SQL should aggregate these two rows? :( it will generate an error...
try this
select customerid, id, customername, user,date, comment from(
select customerid, id, customername, user,date, comment,
#rank := IF(#current_customer = id, #rank+ 1, 1),
#current_customer := id
from comments
where customerid IN
(SELECT DISTINCT id FROM customers WHERE pastdue='1' AND hubarea='1')
AND customerid NOT IN
(SELECT DISTINCT customerid FROM comments WHERE DATEDIFF(NOW(), date) <= 27)
order by customerid, maxdate desc
) where rank <= 1