What's happening with GROUP BY statement - mysql

Without going into the details of the tables, here is a query that outputs 34 rows.
SELECT `t`.`id_product`,
`product_lang`.`name`,
supplies_detail.id_supplies_detail,
`supplies_in_process`.id_supply
FROM `ps_product` `t`
LEFT OUTER JOIN `ps_product_lang` `product_lang` ON (`product_lang`.`id_product` = `t`.`id_product`)
LEFT OUTER JOIN `supplies_detail` `supplies_detail` ON (`supplies_detail`.`id_product` = `t`.`id_product`)
LEFT OUTER JOIN `supplies` `supplies_in_process`
ON (`supplies_detail`.`id_supply` = `supplies_in_process`.`id_supply`) AND
(supplies_in_process.state = 'added')
WHERE ((product_lang.name NOT LIKE '[del]%') AND (t.group_id = '14884'))
ORDER BY t.id_product DESC;
Query without GROUP BY
But it is important for the processor to understand that there will be no rows with duplicate t.id_product in the result, so we add GROUP BY t.id_product to this query.
The result changes to:
Query with GROUP BY
With exactly the same 34 rows we get additional unrelated data. How did it happen?
Thanks
MySQL 8.0.25-15

Related

how to count the records in a query

i want to include the rows also in the query instead of doing a different query to count its record, how can i do in mysql
my query looks like this
SELECT
tblItems.ItemsID,
tblItems.ItemsTitle,
tblItems.ItemsManager_ContactID,
tblItems.EndDate_Scheduled,
tblItems.ItemsViews_Target,
tblviews.viewsName,
tblcontact.FirstName,
tblcontact.LastName
FROM
tblItems
LEFT OUTER JOIN tblviews ON (tblItems.Items_viewsID = tblviews.viewsID)
LEFT OUTER JOIN tblcontact ON (tblItems.ItemsManager_ContactID = tblcontact.ContactID)
WHERE (tblItems.ItemsStatusID = 7)
ORDER BY tblItems.EndDate_Scheduled ASC, Items_viewsID ASC
i am not sure where i can add count, in SQL we can do but in mYsql i have no idea
If you want to determine the count of the returned rows, you will have to execute the same query again, but instead of selecting the rows, select the count:
SELECT count(*)
FROM
tblItems
LEFT OUTER JOIN tblviews ON (tblItems.Items_viewsID = tblviews.viewsID)
LEFT OUTER JOIN tblcontact ON (tblItems.ItemsManager_ContactID = tblcontact.ContactID)
WHERE (tblItems.ItemsStatusID = 7)
ORDER BY tblItems.EndDate_Scheduled ASC, Items_viewsID ASC

SQL query doesnt give desired output

I am trying to create an SQL query, but when I left join an additional table (c) with conditions it reduces the number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.NNaziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.id
ORDER BY a.idArtikla ASC
Query below works great. I would like to keep the records I get, but I would like to JOIN another table. Once I do that it reduces number of rows.
SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla,
norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal
WHERE c.status = 1
ORDER BY a.idArtikla ASC
How I can show all rows from first query and attach table radni_nalozi c with where condition that sums only quantity with status = 1?
try this
With Outer Join, if you put in where clause then it will be treated as inner join.
$stmt = "SELECT a.id, a.naziv as nazivOperacije, b.Naziv as nazivArtikla, b.sifra as sifraArtikla, norma, idArtikla, sum(c.kolicina) as kolRadnogNaloga
FROM artikli_rad a
LEFT JOIN artikli b ON a.idArtikla = b.Id
LEFTJOIN radni_nalozi c ON a.idArtikla = c.idArtikal and c.status = 1
ORDER BY a.idArtikla ASC";
You outer-join radni_nalozi. This means that in case there is no match, the original row is kept and all radni_nalozi columns are null. Then you limit your results by:
WHERE c.status = 1
This dismisses all outer-joined rows, because their status is null. You have turned your outer join into an inner join.
What you want instead is the condition to be part of the join clause:
LEFT JOIN radni_nalozi c ON a.idArtikla = c.idArtikal AND c.status = 1
But as P.Salmon just pointed out, your query is invalid, because of a malformed aggregation. MySQL should raise a syntax error, but obviously you have not SET sql_mode = 'ONLY_FULL_GROUP_BY', which you should in order to have the DBMS prevent you from writing such invalid queries. With sum(c.kolicina) and no GROUP BY clause, you tell the DBMS to limit your results to a single row containing the sum. But what values for a.id, a.naziv, etc. is the DBMS supposed to show then?
It seems you want to join the radni_nalozi sums:
SELECT
ar.id,
ar.naziv as nazivOperacije,
a.Naziv as nazivArtikla,
a.sifra as sifraArtikla,
a.norma,
ar.idArtikla,
rn.sum_kolicina AS kolRadnogNaloga
FROM artikli_rad ar
JOIN artikli a ON a.id = ar.idArtikla
LEFT JOIN
(
SELECT idArtikal, SUM(kolicina) AS sum_kolicina
FROM radni_nalozi
WHERE status = 1
GROUP BY idArtikal
) rn ON rn.idArtikla = ar.idArtikal
ORDER BY ar.idArtikla ASC;
I have also used mnemonic alias names. names like a, b, and c don't make the query more readable as alias names are supposed to do, but make it less readable.
I also turned the outer join on artikli into an inner join, because there must be no artikli_rad row without a match in artikli in a properly set up database.

SQL - order by is breaking my query when there is no reviews

I have the rather lengthy SQL query that I have included below. As you can see it orders by AvgRating and NumReviews, both of which rely on data from the reviews table. Unfortunately I need to see the rows in my results even when there are no reviews, currently if there are no reviews to order by then that row just doesnt show up in the results. All help greatly appreciated.
SELECT travisor_tradesperson.name, travisor_tradesperson.id, travisor_catagory.catname,
travisor_company.cname, travisor_company.description, travisor_company.city, travisor_company.address, travisor_company.postcode, travisor_company.phone,
ROUND(AVG(travisor_review.rating)) as RoundAvgRating, AVG(travisor_review.rating) as AvgRating, COUNT(travisor_review.rating) as NumReviews
FROM `travisor_tradesperson`
INNER JOIN travisor_company
ON travisor_tradesperson.company = travisor_company.id
INNER JOIN travisor_catagory
ON travisor_tradesperson.catagory = travisor_catagory.id
INNER JOIN travisor_review
ON travisor_review.tradesperson = travisor_tradesperson.id
WHERE travisor_catagory.catname = '$catagory'
AND travisor_company.city = '$city'
GROUP BY travisor_tradesperson.name, travisor_catagory.catname, travisor_company.cname,
travisor_company.description
ORDER BY AvgRating DESC, NumReviews DESC
Left join travisor_review instead of Inner Join. Inner join will only find records that are present in both tables. If you have no reviews for that tradesperson record, it will drop from the results set.
Left join will return a NULL if it cannot match on the join predicate. In this case, the tradesperson will return but with a NULL. Convert the NULL to a 0 if needed and that should fix your AVG.

SQL Using count and sum when joining 3 tables

When I try to join 2 tables and use SUM or COUNT it all works perfectly and as expected. However when I join 3 tables SUM and COUNT makes no sense because the JOIN statements create extra rows for each table to have a unique row so they over count and over sum the required values.
Here is the query:
SELECT PO_club.clubid, PO_club.name, PO_club.pic, PO_club.points, count(PO_club_user.userid), SUM(PO_club_point_log.points)
FROM PO_club
INNER JOIN PO_club_user ON PO_club.clubid = PO_club_user.clubid
LEFT JOIN PO_club_point_log ON PO_club.clubid = PO_club_point_log.clubid
WHERE PO_club.deleted = 0
GROUP BY PO_club.clubid
ORDER BY PO_club.points DESC;
If I run two separate scripts like first join only PO_club and PO_club_user to get COUNT() it works. And then run PO_club JOIN PO_club_point_log to get SUM() its all good. However, I need to run it one script so that i would not need to sort it at the front end. Is there a way to join 3 tables and somehow COUNT() just work on PO_club and PO_club_users while SUM only on PO_club and PO_club_point_log?
Thanks!
How's this? This will include all PO_clubs even if they don't have any users in case they have points. If that's not what you want, change the first LEFT JOIN to INNER JOIN.
SELECT PO_club.clubid, PO_club.name, PO_club.pic, PO_club.points, count(PO_club_user.userid), t.sum_points
FROM PO_club
LEFT JOIN PO_club_user ON PO_club.clubid = PO_club_user.clubid
LEFT JOIN
(SELECT PO_club_point_log.clubid, SUM(PO_club_point_log.points) AS sum_points
FROM PO_club_point_log INNER JOIN PO_club ON PO_club_point_log.clubid = PO_club.clubid
GROUP BY PO_club_point_log.clubid) AS t
ON PO_club.clubid = t.clubid
WHERE PO_club.deleted = 0
GROUP BY PO_club.clubid
ORDER BY PO_club.points DESC

MySQL group by twice and COUNT

Some sql query gives me the following result:
As you can see, it already has GROUP BY.
So what I need? I need to group it again (by treatment_name) and count rows for each group. See more details on screenshot.
Here is full query:
SELECT
treatment_summaries.*
FROM `treatment_summaries`
INNER JOIN
`treatments`
ON
`treatments`.`treatment_summary_id` = `treatment_summaries`.`id`
AND
(treatment <> '' and treatment is not null)
INNER JOIN
`treatment_reviews`
ON
`treatment_reviews`.`treatment_id` = `treatments`.`id`
INNER JOIN
`conditions_treatment_reviews`
ON
`conditions_treatment_reviews`.`treatment_review_id` = `treatment_reviews`.`id`
INNER JOIN
`conditions` ON `conditions`.`id` = `conditions_treatment_reviews`.`condition_id`
INNER JOIN `conditions_treatment_summaries` `conditions_treatment_summaries_join`
ON
`conditions_treatment_summaries_join`.`treatment_summary_id` = `treatment_summaries`.`id`
INNER JOIN `conditions` `conditions_treatment_summaries`
ON `conditions_treatment_summaries`.`id` = `conditions_treatment_summaries_join`.`condition_id`
WHERE
`conditions`.`id` = 9
AND `conditions`.`id` IN (9)
AND (latest_review_id is not null)
GROUP BY
treatment_reviews.id
ORDER BY
treatment_summaries.reviews_count desc
LIMIT 20 OFFSET 0
Maybe there is another issue, cause GROUP BY should not leave same lines (for given column), but anyway you can wrap it like this:
SELECT * FROM ( YOUR_SQL_SELECT_WITH_EVERYTHING ) GROUP BY id
So the result you get will behave as another table and you can do all operations like GROUP BY again.