HAVING clause not working after server update - mysql

We have just made a major upgrade in mysql version from 5.0.51 to 5.6.22 and I have just noticed that one of my queries no longer works properly.
SELECT
p.id AS product_id,
p.code,
p.description,
p.unitofmeasure,
p.costprice,
p.packsize,
vc.rateinpercent,
CASE
WHEN Sum(sales.qty) IS NULL THEN 0
ELSE Sum(sales.qty)
END AS sold,
CASE
WHEN stock.stocklevel IS NULL THEN 0
ELSE stock.stocklevel
END AS stocklevel,
sum(sales.qty) - stock.stocklevel AS diff,
CEIL((sum(sales.qty) - stock.stocklevel) / p.packsize) AS amt
FROM products p
LEFT JOIN
( SELECT
col.product_id,
col.quantity AS qty
FROM customerorderlines col
LEFT JOIN customerorders co
ON co.id = col.customerorder_id
WHERE co.orderdate >= '2014-12-01 00:00:00'
AND co.orderdate <= '2015-02-09 23:59:59'
AND co.location_id IN (1,2,3,7)
) sales
ON sales.product_id = p.id
LEFT JOIN
( SELECT
product_id,
location_id,
Sum(stocklevel) AS stocklevel
FROM stock
WHERE location_id IN (1,2,3,7)
GROUP BY product_id
) stock
ON stock.product_id = p.id
LEFT JOIN vatcodes vc
ON vc.id = p.purchasevatcode_id
WHERE p.supplier_id IN (137)
AND p.currentstatus_v = 1
GROUP BY p.id
HAVING sold > stocklevel
ORDER BY sold DESC
On the old server, the HAVING clause filtered out all results with minuses in, giving a result as follows:
Instead, I am getting the following result on the new server:
Basically, it's filtering out some of the negative results but not all of them. (The datasets are a few days old, which is why the 'Freeze Gel Spray' qty and sold and stock numbers are slightly different)
Hindsight is a wonderful thing but I didn't expect there to by any major changes for queries between server updates so I didn't care to test or check anything. Luckily this one of only two or three queries that use HAVING, so if I have to re-write a couple of queries so be it. Any ideas as to why this is though? If it wasn't working at all, fair enough, but to only be working partially?
Thanks in advance for any insight,
R

I take it you've tried EXPLAIN on the query to find out what it's doing?
Try making the calculated field names unique from the underlying field names so you can be sure what you're filtering on. I've seen some screwy results when the calculated fields have the same names as underlying physical fields.
Having your subqueries return the same format of results (i.e. both summed/grouped) helps to see what's going on.
I haven't tested this query but it may help. If you post the table structures and perhaps some fake data that shows the error, that would help diagnose
SELECT
p.id AS product_id,
p.code,
p.description,
p.unitofmeasure,
p.costprice,
p.packsize,
vc.rateinpercent,
sales.totalSold,
stock.totalStock,
sales.totalSold - stock.totalStock AS diff,
CEIL((sales.totalSold - stock.totalStock) / p.packsize) AS amt
FROM products p
LEFT JOIN (
SELECT
col.product_id,
IFNULL( SUM(col.quantity), 0) AS totalSold
FROM customerorderlines col
LEFT JOIN customerorders co
ON co.id = col.customerorder_id
WHERE co.orderdate >= '2014-12-01 00:00:00'
AND co.orderdate <= '2015-02-09 23:59:59'
AND co.location_id IN (1,2,3,7)
GROUP BY product_id
) sales
ON sales.product_id = p.id
LEFT JOIN (
SELECT
product_id,
IFNULL( SUM(stocklevel), 0) AS totalStock
FROM stock
WHERE location_id IN (1,2,3,7)
GROUP BY product_id
) stock
ON stock.product_id = p.id
LEFT JOIN vatcodes vc
ON vc.id = p.purchasevatcode_id
WHERE p.supplier_id IN (137)
AND p.currentstatus_v = 1
GROUP BY p.id
HAVING totalSold > totalStock
ORDER BY totalSold DESC

Related

How to get different status count by product id

I have got the result form a complex query below
SELECT o_items.sku,
o_items.name AS 'title',
o_items.qty_ordered AS 'quantity',
s_orders.base_amount_paid AS 'paid/unpaid'
FROM sales_order_payment s_orders
INNER JOIN (SELECT s.sku, s.name, s.qty_ordered, s.order_id
FROM sales_order_item s
INNER JOIN (SELECT p.entity_id
FROM catalog_product_entity AS p
INNER JOIN catalog_product_entity_int AS a
ON p.row_id = a.row_id
WHERE VALUE >= 0
AND
a.attribute_id =
(SELECT attribute_id
FROM eav_attribute
WHERE attribute_code = 'is_darkhorse')) as q
ON s.product_id = q.entity_id
WHERE s.created_at BETWEEN '2019-01-14' AND '2019-01-16') o_items
ON
s_orders.parent_id = o_items.order_id
this is the order data those have been paid or not paid yet. Amount is representing paid and Null representing unpaid status
I am trying to generate below result but couldn't succeed and need help. Actually this result is showing how may quantity of a product has been paid and how many not paid yet. This would be result of above fetched data.
Please guide me how can i proceed to achieve these result.
Use this. ... represent existing code.
select .... , sum(case when s_orders.base_amount_paid is not null
then o_items.qty_ordered
else 0
end) as paid,
sum(case when s_orders.base_amount_paid is null
then o_items.qty_ordered
else 0
end) as unpaid
From .......
You can use if and ifnull functions together(presuming you're using mysql as DBMS)
and GROUP BY expression
SELECT c.sku, c.name,
sum(if(ifnull(base_amount_paid,0)=0,0,1)) as paid,
sum(if(ifnull(qty_ordered,0)=0,0,1)) as unpaid
FROM catalog_prod_ent_derived c
GROUP BY c.sku, c.name
where catalog_prod_ent_derived represents your whole query as a subquery.

How to convert sub query to joins?

I am using this query in my opencart site
SELECT MIN(tmp.date_added) AS date_start,
MAX(tmp.date_added) AS date_end,
COUNT(tmp.order_id) AS `orders`,
SUM(tmp.products) AS products,
SUM(tmp.tax) AS tax,
SUM(tmp.total) AS total
FROM
( SELECT o.order_id,
( SELECT SUM(op.quantity)
FROM `oc_order_product` op
WHERE op.order_id = o.order_id
GROUP BY op.order_id
) AS products,
( SELECT SUM(ot.value)
FROM `oc_order_total` ot
WHERE ot.order_id = o.order_id
AND ot.code = 'tax'
GROUP BY ot.order_id
) AS tax,
o.total,
o.date_added
FROM `oc_order` o
WHERE o.order_status_id > '0'
AND DATE(o.date_added) >= '2015-03-01'
AND DATE(o.date_added) <= '2016-04-19'
GROUP BY o.order_id
) tmp
GROUP BY WEEK(tmp.date_added)
ORDER BY tmp.date_added DESC
LIMIT 0,60
Queries like this make my site very slow. Is there any easy way to convert this query from sub query to joins.
Here is the output of above query
WEEK will have a hiccup around the first of the year -- there will be two partial weeks.
We are now in "week" 16 of 2016. That corresponds to slightly different days of 2015; did you want them combined?
Because of those hiccups with WEEK, you had better change the final ORDER BY to WEEK(tmp.date_added) DESC
The FROM ( SELECT ... ) is probably fine. Is that what you are asking about?
The two ( SELECT SUM ... ) AS ... are probably optimal, or nearly so. Is that what you are asking about?
However, you probably do need some indexes:
oc_order_total: INDEX(code, order_id) -- in that order
oc_order_product: INDEX(order_id)
Change DATE(o.date_added) >= '2015-03-01' to o.date_added >= '2015-03-01' (etc) so that INDEX(date_added) can be used.
If this can be only '1': o.order_status_id > '0', then change it to o.order_status_id = 1 so that INDEX(order_status_id, date_added) can be used.

Getting SUM() from multiple tables with conditions

I am trying to get the due_amount from two tables that is invoices and offline_invoice.
The condition is; status not like 'paid%'. I am working with this query
select
(sum(i.total_amount) + sum(oi.invoice_amount)) - (sum(i.paid_amount) + sum(oi.paid_amount)) due_amount
from
{CI}invoices i
left join
{CI}offline_invoice oi ON oi.customer_id = i.customer_id
where
i.customer_id = ?
and i.status not like 'paid%'
group by i.customer_id
But i don't know how do i use condition on joined table({CI}offline_invoice)? I have to use the same condition(status not like 'paid%') on it.
Just add the and to the ON clause too
left join
{CI}offline_invoice oi ON oi.customer_id = i.customer_id
AND oi.status not like 'paid%'
where
i.customer_id = ?
and i.status not like 'paid%'
However, I'm not sure how this is going to work for you without possible Cartesian impact. Say you have 10 current invoice and 10 and 6 offline invoices. I would do two separate pre-aggregates joined by their customer ID... unless the off-line invoice has the same invoice ID as current (such as archive purposes)
select
CurInvoices.Customer_ID,
CurInvoices.InvBalance + COALESCE( OIInvoices.OIBalance, 0 ) as AllBalanceDue
from
( select i.customer_id,
sum( i.total_amount - i.paid_amount ) as invBalance
from
{CI}invoices i
where
i.customer_id = ?
and i.status not like 'paid%'
group by
i.customer_ID ) as CurInvoices
LEFT JOIN
( select oi.customer_id,
sum( oi.total_amount - oi.paid_amount ) as OIBalance
from
{CI}offline_invoice oi
where
oi.customer_id = ?
and oi.status not like 'paid%'
group by
i.customer_ID ) as OIInvoces
on CurInvoices.Customer_ID = OIInvoices.customer_ID

MySQL DISTINCT not Filtering out

I have the folowing sql query:
SELECT DISTINCT(tbl_products.product_id), tbl_products.product_title,
tbl_brands.brand_name, tbl_reviews.review_date_added,
NOW() AS time_now
FROM tbl_products, tbl_reviews, tbl_brands
WHERE tbl_products.product_id = tbl_reviews.product_id AND
tbl_products.brand_id = tbl_brands.brand_id
ORDER BY tbl_reviews.review_date_added DESC
That needs to filter out any duplicate product_id's unfortunatly selecting tbl_reviews.review_date_added makes each record unique which means DISTINCT will not work anymore.
Is there any otherway of doing this query so that product_id is still unique?
I did do the GROUP BY and the problem is I display the tbl_reviews.review_date_added on a website and it selects the oldest date. I need the newest date.
Regards
With the description given, it's a bit hard to be certain, but if review_date_added is the only problem, it seems like you want the MAX() of that date?
If the following doesn't help, please could you give example data, example output, and a description of how you want the output to be created?
SELECT
tbl_products.product_id,
tbl_products.product_title,
tbl_brands.brand_name,
MAX(tbl_reviews.review_date_added) AS review_date_added,
NOW() AS time_now
FROM
tbl_products
INNER JOIN
tbl_reviews
ON tbl_products.product_id = tbl_reviews.product_id
INNER JOIN
tbl_brands
ON tbl_products.brand_id = tbl_brands.brand_id
GROUP BY
tbl_products.product_id,
tbl_products.product_title,
tbl_brands.brand_name
ORDER BY
MAX(tbl_reviews.review_date_added) DESC
Distinct works for the entire row. The parenthesis are just around the field:
distinct (a), b, c === distinct a, b, c
A straightforward solution is group by. You can use min to select the oldest date.
select tbl_products.product_id
, min(tbl_products.product_title)
, min(tbl_brands.brand_name)
, min(tbl_reviews.review_date_added)
, NOW() AS time_now
FROM tbl_products, tbl_reviews, tbl_brands
WHERE tbl_products.product_id = tbl_reviews.product_id AND
tbl_products.brand_id = tbl_brands.brand_id
GROUP BY
tbl_products.product_id
ORDER BY
min(tbl_reviews.review_date_added) DESC
Note that if a product can have multiple brands, this will pick the lowest one.
Try this:
SELECT pr.product_id, pr.product_title,
bd.brand_name,
(SELECT MAX(rev.review_date_added) FROM tbl_reviews rev
WHERE pr.product_id = rev.product_id) AS maxdate,
NOW() AS time_now
FROM tbl_products pr INNER JOIN tbl_reviews re
ON pr.product_id = re.product_id
INNER JOIN tbl_brands bd
ON pr.brand_id = bd.brand_id
GROUP BY pr.product_id
ORDER BY re.review_date_added DESC
or (as suggested by #Hogan)
SELECT pr.product_id, pr.product_title,
bd.brand_name, md.maxdate
NOW() AS time_now
FROM tbl_products pr INNER JOIN tbl_reviews re
ON pr.product_id = re.product_id
INNER JOIN tbl_brands bd
ON pr.brand_id = bd.brand_id
INNER JOIN (SELECT product_id, MAX(review_date_added) AS maxdate
FROM tbl_reviews rev GROUP BY product_id) md
ON pr.product_id = md.product_id
GROUP BY pr.product_id
ORDER BY re.review_date_added DESC
I combined the answer of Andomar with some changes you will find here.
SELECT tbl_products.product_id, tbl_products.product_title,
tbl_products.product_date_added, tbl_brands.brand_name,
MAX(tbl_reviews.review_date_added) AS review_date_added, NOW() AS time_now
FROM tbl_products, tbl_reviews, tbl_brands
WHERE tbl_products.product_id = tbl_reviews.product_id AND
tbl_products.brand_id = tbl_brands.brand_id
GROUP BY tbl_products.product_id
ORDER BY MAX(tbl_reviews.review_date_added) DESC
Works beautifully and shows the newest date at tbl_reviews.review_date_added.
Regards

MySQL sum of sub queries

I have quite a long query that is causing me some problems. For the first sub-query I keep getting the error: "MySQL server version for the right syntax to use near 'SELECT project.project_total_num_hours_quoted FROM project inner join time_recor' at line 5".
The subquery in question is:
sum(SELECT
project.project_total_num_hours_quoted
FROM
project inner join time_recording using(project_id)
WHERE
project.company_id = company.company_id
AND project_is_retainer != 1
AND time_recording.time_recording_event_start_datetime >= '2011-01-01' AND time_recording.time_recording_event_stop_datetime <= '2011-03-01'
group by project_id
) AS hours_quoted,
This returns a set of results. In the larger query I simply want to have the sum.
SELECT
SUM((unix_timestamp(time_recording.time_recording_event_stop_datetime)-unix_timestamp(time_recording.time_recording_event_start_datetime))/3600) AS total_time,
company.company_label,
sum(SELECT
project.project_total_num_hours_quoted
FROM
project inner join time_recording using(project_id)
WHERE
project.company_id = company.company_id
AND project_is_retainer != 1
AND time_recording.time_recording_event_start_datetime >= '2011-01-01' AND time_recording.time_recording_event_stop_datetime <= '2011-03-01'
group by project_id
) AS hours_quoted,
(SELECT SUM(project.project_total_num_hours_quoted)
FROM project
INNER JOIN time_recording ON project.project_id = time_recording.project_id
WHERE time_recording.time_recording_event_start_datetime>='2011-01-01'
AND project_is_retainer!=1
AND time_recording.time_recording_event_stop_datetime<='2011-03-01'
AND project.company_id!=1
) AS total_hours_quoted,
(
SELECT
SUM((unix_timestamp(time_recording.time_recording_event_stop_datetime)-unix_timestamp(time_recording.time_recording_event_start_datetime))/3600)
FROM time_recording
INNER JOIN project ON time_recording.project_id = project.project_id
WHERE project.company_id!=1
AND project_is_retainer!=1
AND time_recording.time_recording_event_start_datetime>='2011-01-01'
AND time_recording.time_recording_event_stop_datetime<='2011-03-01'
)
AS total_hours
FROM time_recording
INNER JOIN project ON time_recording.project_id = project.project_id
INNER JOIN company ON project.company_id = company.company_id
WHERE company.company_id!=1
AND project_is_retainer!=1
AND time_recording.time_recording_event_start_datetime>='2011-01-01'
AND time_recording.time_recording_event_stop_datetime<='2011-03-01'
GROUP BY company.company_id
ORDER BY total_time desc
LIMIT 7
In your first subquery, you don't need the group by if you sum it in the outer query. And you are missing the ON clause.
SELECT project.project_total_num_hours_quoted
FROM project inner join time_recording
ON project.id=time_recording.project_id
WHERE
project.company_id = company.company_id
AND project_is_retainer != 1
AND time_recording.time_recording_event_start_datetime >= '2011-01-01'
AND time_recording.time_recording_event_stop_datetime <= '2011-03-01'
I would strongly recommend scrapping this and starting again.
Several, if not all, the subselects could be merged into a single SELECT statement. The outer SELECT is an aggregate operation which selects non-aggregated values not included in the GROUP BY clause. MySQL does not optimize push-predicates. And you've got redundant joins in the query.