I'm building a report that counts how many materials like my search marked as % below are in locations (Stor.bin) and then joining it to my bins table which has each Stor.bin total capacity. If I remove the inner join my count for Stor.bin is correct but as soon as I add in my join it makes my Totals count completely wrong.
I can't work out why the join is changing my Total count.
SELECT *
FROM (
SELECT COUNT(
CASE
WHEN `SDON`.`Material` LIKE '%' THEN 1
END
) AS `MaterialCount`,
COUNT(`SDON`.`Material`) AS `Total`,
COUNT(DISTINCT `SDON`.`Material`) AS `Materials`,
`SDON`.`Stor.bin`,
`bins`.`Capacity` AS `Bin Capacity`,
(`bins`.`Capacity` - COUNT(`SDON`.`Stor.bin`)) AS `Free Space`
FROM `jjezuk_StockFiles`.`SDON`
INNER JOIN `jjezuk_StockFiles`.`bins` ON `SDON`.`Stor.bin` = `bins`.`Stor.bin`
WHERE `SDON`.`import_date` = "2022-10-05"
AND `C` != 'Q'
AND `C` != 'T'
AND `C` != 'A'
AND `C` != 'S'
GROUP BY `SDON`.`Stor.bin`
) XYZ
WHERE `MaterialCount` > 0
ORDER BY `XYZ`.`Total` DESC
I have following Mysql query
SELECT c.`id`
,c.`category_name`
,c.`category_type`
,c.bookmark_count
,f.category_id cat_id
,f.unfollow_at
,(
CASE WHEN c.id = f.follower_category_id
THEN (
SELECT count(`user_bookmarks`.`id`)
FROM `user_bookmarks`
WHERE (`user_bookmarks`.`category_id` = cat_id)
AND ((`f`.`unfollow_at` > `user_bookmarks`.`created_at`) || (`f`.`unfollow_at` = '0000-00-00 00:00:00'))
)
ELSE 0 END
) counter
,c.id
,f.follower_category_id follow_id
,c.user_id
FROM categories c
LEFT JOIN following_follower_categories f ON f.follower_category_id = c.id
WHERE c.user_id = 26
ORDER BY `category_name` ASC
and here is output what i am getting after execuation
now i just want to count . here i have field id having value 172 against it i have counter 30,3, 2 and Bookmark_count is 4( i need to include only once)
and i am accepting output for id 172 is 30+3+2+4(bookmark_count only once).
I am not sure how to do this.
Can anybody help me out
Thanks a lot
The following may be the most inefficient query for that purpose, but I added a cover to your query in order to hint at grouping the results.
(I removed the second c.id, and my example may have errors since I couldn't try it.)
SELECT `id`,
`category_name`,
`category_type`,
max(`bookmark_count`),
`cat_id`,
`unfollow_at`,
sum(`counter`)+max(`bookmark_count`) counter,
follow_id`, `user_id`
FROM
(SELECT c.`id`
,c.`category_name`
,c.`category_type`
,c.bookmark_count
,f.category_id cat_id
,f.unfollow_at
,(
CASE WHEN c.id = f.follower_category_id
THEN (
SELECT count(`user_bookmarks`.`id`)
FROM `user_bookmarks`
WHERE (`user_bookmarks`.`category_id` = cat_id)
AND ((`f`.`unfollow_at` > `user_bookmarks`.`created_at`) || (`f`.`unfollow_at` = '0000-00-00 00:00:00'))
)
ELSE 0 END
) counter
,f.follower_category_id follow_id
,c.user_id
FROM categories c
LEFT JOIN following_follower_categories f ON f.follower_category_id = c.id
WHERE c.user_id = 26)
GROUP BY `id`, `category_name`, `category_type`, `cat_id`, `unfollow_at`, `follow_id`, `user_id`
ORDER BY `category_name` ASC
I almost spent a day to optimize this query:
SELECT
prod. *,
cat.slug category_slug,
sup.bname bname,
sup.slug bname_slug
FROM bb_admin.bb_directory_products AS prod
LEFT JOIN bb_admin.bb_categories_products AS cat
ON prod.primary_category_id = cat.category_id
LEFT JOIN bb_admin.bb_directory_suppliers AS sup
ON prod.supplier_id = sup.supplier_id
LEFT JOIN bb_admin.bb_directory_suppliers AS credit_sup
ON prod.credit_supplier_id = credit_sup.supplier_id
LEFT JOIN bb_admin.bb_directory_suppliers AS photo_sup
ON prod.photo_supplier_id = photo_sup.supplier_id
WHERE (
prod.status = '1'
OR prod.status = '3'
OR prod.status = '5'
)
AND (
sup.active_package_id != '1'
OR sup.active_package_id != '5'
OR sup.active_package_id != '6'
OR prod.supplier_id = '0'
)
AND (
sup.supplier_id = '1989'
OR credit_sup.supplier_id = '1989'
OR photo_sup.supplier_id = '1989'
)
GROUP BY prod.product_id
ORDER BY prod.priority_index ASC
Can you help me to optimized this query?
Update your column data types to be INT or one of its variants, since the ones you are checking against are all integer IDs (assumption).
Create indexes on following columns(if possible in all tables):
prod.status
supplier_id
active_package_id
Use IN clause instead of concatenating OR segments.
I'll be putting the updated WHERE clause here:
WHERE prod.status IN(1, 3, 5)
AND ( sup.active_package_id NOT IN(1, 5, 6)
OR prod.supplier_id = 0
)
AND 1989 IN (prod.supplier_id, prod.credit_supplier_id, prod.photo_supplier_id)
I am trying to bring back a string based on an IF statement but it is extremely slow.
It has something to do with the first subquery but I am unsure of how to rearrange this as to bring back the same results but faster.
Here is my SQL:
SELECT IF
(
(
SELECT COUNT(*)
FROM
(
SELECT DISTINCT enquiryId, type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id
) AS parts
WHERE parts.enquiryId = enquiries.id
) > 1, 'Mixed',
(
SELECT DISTINCT type
FROM parts_enquiries, parts_service_types AS pst
WHERE parts_enquiries.serviceTypeId = pst.id AND enquiryId = enquiries.id
)
) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
How can I make it faster?
I have modified my original query below, but I am getting the error that subquery returns more than one row:
SELECT
(SELECT
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId) AS partTypes
FROM enquiries,
entities
WHERE enquiries.entityId = entities.id
Please have a look if this query yields the same results:
SELECT
enquiryId,
CASE WHEN COUNT(DISTINCT type) > 1 THEN 'Mixed' ELSE `type` END AS type
FROM parts_enquiries
INNER JOIN parts_service_types AS pst ON parts_enquiries.serviceTypeId = pst.id
INNER JOIN enquiries ON parts_enquiries.enquiryId = enquiries.id
INNER JOIN entities ON enquiries.entityId = entities.id
GROUP BY enquiryId
But N.B.'s comment is still valid. To see if and index is used and other information we need to see the EXPLAIN and the table definitions.
This should get you what you want.
I would first pre-query your parts enquiries and parts service types looking for both the count and MINIMUM of the part 'type', grouped by the enquiry ID.
then, run your IF() against that result. If the distinct count is > 0, then 'Mixed'. If only one, since I did the MIN(), it would only have the description of that one value that you desire anyhow.
SELECT
E.ID
IF ( PreQuery.DistTypes > 1, 'Mixed', PreQuery.FirstType ) as PartType
from
Enquiries E
JOIN ( SELECT
PE.EnquiryID,
COUNT( DISTINCT PE.ServiceTypeID ) as DistTypes,
MIN( PST.Type ) as FirstType
from
Parts_Enquiries PE
JOIN Parts_Service_Types PST
ON PE.ServiceTypeID = PST.ID
group by
PE.EnquiryID ) as PreQuery
ON E.ID = PreQuery.EnquiryID
The result I receive:
id sku name GROUP_CONCAT(quantity_received) GROUP_CONCAT(item_cost)
4 00004 Antibacterial Wipes 50,14,25,309,50,14,25,309,50,14,25,309,50,14,25,309,50,14,25,309,50,14,25,309,50,14,25,309,50,14,25,309,50,14,25,309 3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49,3.29,3.29,3.29,3.49
The result I want:
id sku name GROUP_CONCAT(DISTINCT quantity_received) GROUP_CONCAT(DISTINCT item_cost)
4 00004 Antibacterial Wipes 50,14,25,309 3.29,3.49
The way I resolved this issue was placing DISTINCT in the quantity_recieved select. The problem is that if the quantity has two values that are the same such as 50, 50, 14, 25. The result will be 50, 14, 25. I just want to get rid of the repeating numbers and only get the values once.
Here is the query:
SELECT `product`.`id`,`product`.`sku`,`product`.`name`,
case when coalesce(stock1.`quantity`, '') = ''
then '0'
else stock1.`quantity`
end as qty_warehouse,
case when coalesce(sum(distinct stock2.`quantity`), '') = ''
then '0'
else sum(distinct stock2.`quantity`)
end as qty_events,
case when coalesce(stock1.`quantity`, '') = ''
then '0'
else stock1.`quantity`
end +
case when coalesce(sum(distinct stock2.`quantity`), '') = ''
then '0'
else sum(distinct stock2.`quantity`)
end as qty_total
GROUP_CONCAT(DISTINCT quantity_received) ,
GROUP_CONCAT(DISTINCT item_cost)
FROM (`product`)
LEFT JOIN`shipping_event`
ON `shipping_event`.`product_id` = `product`.`id`
LEFT JOIN `product_stock` as stock1
ON `product`.`id` = `stock1`.`product_id` and `stock1`.`location_id` = 112
LEFT JOIN `product_stock` as stock2
ON `product`.`id` = `stock2`.`product_id` and `stock2`.`location_id` != 112
LEFT JOIN `shipping_list`
ON `shipping_event`.`shipping_list_id` = `shipping_list`.`id`
WHERE `shipping_list`.`type` = 'incoming'
AND `shipping_event`.`end_date` > '2004-01-01 01:01:01'
GROUP BY `product`.`id`
ORDER BY `sku` asc LIMIT 20
I am using group concat just to display the result in this case. I actually sum the quantity_received and then multiple them by the item cost.
You could make a subquery with a GROUP BY and then JOIN to the product table.
The same logic can be applied to the other joined tables as well. If you do that, you can skip the GROUP BY product.id:
SELECT p.id
, p.sku
, p.name
, COALESCE(stock1.quantity, 0) --- minor improvements on
AS qty_warehouse --- the long CASE clauses
, COALESCE(SUM(DISTINCT stock2.quantity), 0)
AS qty_events
, COALESCE(stock1.quantity, 0) + COALESCE(SUM(DISTINCT stock2.quantity), 0)
AS qty_total
, grp.all_quantities_received
, grp.all_item_costs
FROM product AS p
LEFT JOIN
( SELECT product_id
, GROUP_CONCAT(se.quantity_received) AS all_quantities_received
, GROUP_CONCAT(se.item_cost) AS all_item_costs
FROM shipping_event AS se
LEFT JOIN shipping_list AS sl
ON se.shipping_list_id = sl.id
WHERE sl.type = 'incoming'
AND se.end_date > '2004-01-01 01:01:01'
GROUP BY se.product_id
) AS grp
ON grp.product_id = p.id
LEFT JOIN `product_stock` AS stock1
ON p.id = stock1.product_id
AND stock1.location_id = 112
LEFT JOIN product_stock AS stock2
ON p.id = stock2.product_id
AND stock2.location_id <> 112
GROUP BY p.id
ORDER BY sku ASC
LIMIT 20