My SQL sum returning double values - php

The following query
SELECT
invoices.id,
SUM(payments.amount)
FROM adass.invoices
INNER JOIN adass.payments ON invoices.id=payments.id_invoice
GROUP BY invoices.id ORDER BY 1 desc LIMIT 0, 25
returns the following result
However, when I join another table to the query like so:
SELECT
invoices.id,
SUM(payments.amount)
FROM adass.invoices
INNER JOIN adass.invoice_items ON invoices.id=invoice_items.id_invoice
INNER JOIN adass.payments ON invoices.id=payments.id_invoice
GROUP BY invoices.id ORDER BY 1 desc LIMIT 0, 25
It duplicates the payment amount for invoice id 13919, effectively doubling the payment of 100 in the table
What is causing this?
I've added the table contents below
INVOICE ITEMS TABLE
PAYMENTS TABLE
UPDATE: Larger query as follows
SELECT SQL_CALC_FOUND_ROWS invoices.id,
COALESCE(SUM(invoice_items.gross),0) AS gross,
COALESCE(SUM(invoice_items.net) - SUM(invoice_items.gross),0) AS vat,
COALESCE(SUM(invoice_items.net),0) AS net,
COALESCE(SUM(payments.amount),0),
COALESCE(SUM(payments.amount) - SUM(invoice_items.net),0) AS outstanding
FROM adass.invoices
LEFT JOIN adass.invoice_items ON invoices.id=invoice_items.id_invoice
LEFT JOIN adass.payments ON invoices.id=payments.id_invoice
GROUP BY invoices.id ORDER BY 1 desc LIMIT 0, 25
RESULT:

It's clear what's going on. Your INVOICE #13919 has two INVOICE ITEMS, hence you get double the amount of invoice payment because your query is generating two rows. If you had three INVOICE ITEMS, then the amount would have been tripled.
You need to remove this clause from your query INNER JOIN adass.invoice_items ON invoices.id=invoice_items.id_invoice since you are not using columns from adass.invoice_items anyway.
Your query then will be:
SELECT invoices.id,
SUM(payments.amount)
FROM adass.invoices
INNER JOIN adass.payments ON invoices.id=payments.id_invoice
GROUP BY invoices.id ORDER BY 1 desc LIMIT 0, 25

Related

How to increase the speed of MySQL query with extra condition?

I'm trying to speed up the following query as it takes quite long to run: now it's 'only' about 1.5 seconds, but it will certainly get slower with more rows (which will 10x over the next period).
Basically, I want to show all the rows from the orders table for the user, and per row show the total order amount (which is the SUM of the orders_products table).
SELECT
orders.order_number,
orders.order_date,
companies.company_name,
COALESCE(SUM(orders_products.product_price * orders_products.product_quantity),0) AS order_value
FROM orders
LEFT JOIN companies ON companies.id = orders.company_id
LEFT JOIN orders_products ON orders_products.order_id = orders.id
LEFT JOIN users ON users.id = orders.user_id
WHERE orders.user_id = '$user_id'
AND companies.user_id = '$user_id'
GROUP BY orders.id ORDER BY orders.order_date DESC, orders.order_number DESC
I've tried adding another condition AND orders_products.user_id = '$user_id'. Speed wise the query was about 12x faster (yeah!) but the problem is that not all orders have products in them. In this case, the orders without products in them are not returned.
How do I change my query so that despite of an order not having products in them, it still is returned (with total order value 0), whilst also speeding up the query?
Thank you in advance for your help!
You might find it faster to use a correlated subquery:
SELECT o.order_number, o.order_date, c.company_name,
(SELECT COALESCE(SUM(op.product_price * op.product_quantity), 0)
FROM orders_products op
WHERE op.order_id = o.id
) AS order_value
FROM orders o LEFT JOIN
companies c
ON c.id = o.company_id AND c.user_id = o.user_id
WHERE o.user_id = '$user_id'
ORDER BY o.order_date DESC, o.order_number DESC
This gets rid of the outer aggregation which is often a performance win.
Then for performance you want the following indexes:
orders(user_id, order_date desc, order_number_desc, company_id)
companies(id, company_id, company_name)
orders_products(order_id, product_price, product_quantity)

limit sub-query result set to last record

I have four tables that are linked. The itemStatus table can have multiple records per Cart item to show its history (e.g. purchased, processed, shipped, delivered). The Shop table pulls in the Cart items name. The Order table defines all the Cart items in the order.
To show the latest status (not entire history) of each Cart item in the Order, I need to only obtain the last record for itemStatus. I am using a sub-query to obtain the records of the itemStatus table, but can not figure out how to get the last record. Playing with the below code, I end up always getting the first record:
SELECT
tblOrders.id,tblOrders.status as orderStatus,tblOrders.created,tblCart.id AS cartID,tblCart.status AS cartStatus,tblCart.qty,tblCart.price,tblShop.title,tblItem.itemStatus
FROM
".PREFIX."Orders tblOrders
LEFT JOIN
".PREFIX."Cart tblCart ON tblOrders.id=tblCart.OID
LEFT JOIN
".PREFIX."Shop tblShop ON tblCart.PID=tblShop.id
LEFT JOIN
(SELECT
CID,status as itemStatus
FROM
".PREFIX."ItemStatus
WHERE
status IN ('pending','refunded','cancelled','purchased','backordered','shipping','delivered')
GROUP BY CID
ORDER BY
created DESC
) tblItem ON tblCart.id=tblItem.CID
WHERE
tblOrders.status<>'disabled' AND tblCart.status='purchased' AND tblCart.BID='".$gbl_user['id']."'
ORDER BY
tblOrders.updated DESC
As shown above, I have tried using the 'GROUP BY' clause to limit the number of records returned by the sub-query, but that does not produce desired results. I have also tried placing a 'LIMIT 1' clause in the sub-query too, but that only limits the number of records returned to 1 (so any other cart items do not end up with a status). Any help would be appreciated!
UPDATE:
Per the duplication marking, I attempted to update the code using the MAX() statement and removal of 'ORDER BY' as follows:
SELECT
tblOrders.id,tblOrders.status as orderStatus,tblOrders.created,tblCart.id AS cartID,tblCart.status AS cartStatus,tblCart.qty,tblCart.price,tblShop.title,tblItem.itemStatus
FROM
".PREFIX."Orders tblOrders
LEFT JOIN
".PREFIX."Cart tblCart ON tblOrders.id=tblCart.OID
LEFT JOIN
".PREFIX."Shop tblShop ON tblCart.PID=tblShop.id
LEFT JOIN
(SELECT
CID,status as itemStatus,MAX(created) as itemCreated
FROM
".PREFIX."ItemStatus
WHERE
status IN ('pending','refunded','cancelled','purchased','backordered','shipping','delivered')
GROUP BY CID
) tblItem ON tblCart.id=tblItem.CID
WHERE
tblOrders.status<>'disabled' AND tblCart.status='purchased' AND tblCart.BID='".$gbl_user['id']."'
ORDER BY
tblOrders.updated DESC
UPDATE 2:
I had provided the actual code that works to solve the problem, but it wasn't approved for some reason. I am posting the correct code below:
SELECT
tblOrders.id, tblOrders.status as orderStatus, tblOrders.created,
tblCart.id AS cartID, tblCart.status AS cartStatus,tblCart.qty,
tblCart.price, tblShop.title, tblItem.status as itemStatus
FROM
".PREFIX."Orders tblOrders
LEFT JOIN
".PREFIX."Cart tblCart ON tblOrders.id=tblCart.OID
LEFT JOIN
".PREFIX."Shop tblShop ON tblCart.PID=tblShop.id
LEFT JOIN
".PREFIX."ItemStatus tblItem ON tblCart.id=tblItem.CID
JOIN (
SELECT
CID, MAX(created) AS maxCreated
FROM
".PREFIX."ItemStatus
WHERE
status IN ('pending','refunded','cancelled','purchased','backordered','shipping','delivered')
GROUP BY
CID
) tblMaxItem ON tblItem.CID=tblMaxItem.CID AND tblItem.created=tblMaxItem.maxCreated
WHERE
tblOrders.status<>'disabled' AND tblCart.status='purchased' AND tblCart.BID='".$gbl_user['id']."'
ORDER BY
tblOrders.updated DESC
You need to join with both ItemStatus and a subquery that gets the latest date for each item, so you can get other columns columns from that row.
SELECT
tblOrders.id, tblOrders.status as orderStatus, tblOrders.created,
tblCart.id AS cartID, tblCart.status AS cartStatus,tblCart.qty,
tblCart.price, tblShop.title, tblItem.status AS itemStatus
FROM
".PREFIX."Orders tblOrders
LEFT JOIN
".PREFIX."Cart tblCart ON tblOrders.id=tblCart.OID
LEFT JOIN
".PREFIX."Shop tblShop ON tblCart.PID=tblShop.id
LEFT JOIN
".PREFIX."ItemStatus tblItem ON tblCart.id=tblItem.CID
LEFT JOIN (
SELECT
CID, MAX(created) AS maxCreated
FROM
".PREFIX."ItemStatus
WHERE
status IN ('pending','refunded','cancelled','purchased','backordered','shipping','delivered')
GROUP BY
CID
) tblMaxItem ON tblItem.CID=tblMaxItem.CID AND tblItem.created=tblMaxItem.maxCreated
WHERE
tblOrders.status<>'disabled' AND tblCart.status='purchased' AND tblCart.BID='".$gbl_user['id']."'
ORDER BY
tblOrders.updated DESC

MySql - how to calculate count(*) for query with group by?

I have the query :
select sellers.* from sellers
left join locations
on locations.seller_id = sellers.id
group by sellers.id
limit 0, 10;
Let assume that first query gives me 10 results with a group by, 15 results without a group by.
Now I want to calculate "all results count" for the pagination.
select count(*) from ...
I tried this :
select count(*) from sellers
left join locations
on locations.seller_id = sellers.id
//group by sellers.id;
1.WITH "group by sellers.id" I get 10 results with value 1 (it should be one result with value 10)
2.WITHOUT "group by sellers.id" I get one result with value 15 (it should be one result with value 10)
any ideas?
This should work for you:
select count(*) from (select sellers.id from sellers left join locations on locations.seller_id = sellers.id) as a;
You can put your SELECT query inside a SELECT COUNT(*) query, this way:
SELECT count(*) FROM (select sellers.* from sellers
left join locations
on locations.seller_id = sellers.id
group by sellers.id) sellers;
I don't approve of your query, but you simply want the count of sellers. The simplest method is:
select count(*)
from sellers;

How to 'GROUPT BY' after 'ORDER BY' in MySQL

I'm using the following query to get a list of users having maximum 'speed'.
With group by:
SELECT users.email, speed.speed
FROM users INNER JOIN speed ON users.email=speed.email
GROUP BY users.email
ORDER BY speed.speed DESC LIMIT 15
How ever on running the query, the 'ORDER BY' is not working. It always show the result in the top.
If I remove 'GROUP BY', I got the order list. But I only want 1 row with same email ids. How can i apply group by on this?
Without Group By:
SELECT users.email, speed.speed
FROM users INNER JOIN speed ON users.email=speed.email
ORDER BY speed.speed DESC LIMIT 15
Without an aggregate function, GROUP BY will show you the grouped column with whichever value is selected last from non-grouped columns. You'll want to use an aggregate function on your speed.speed column if you want it sorted.
If you want each user with their maximum speed you'll want to do something like
SELECT users.email, MAX(speed.speed) FROM users INNER JOIN speed ON users.email=speed.email ORDER BY speed.speed DESC LIMIT 15
Or if you wanted the minimum
SELECT users.email, MIN(speed.speed) FROM users INNER JOIN speed ON users.email=speed.email ORDER BY speed.speed DESC LIMIT 15
Or Both
SELECT users.email, MAX(speed.speed) as maxspeed, MIN(speed.speed) as minspeed FROM users INNER JOIN speed ON users.email=speed.email ORDER BY speed.speed DESC LIMIT 15
If you just want the 15 highest speeds:
SELECT s.email, s.speed
FROM speed s
ORDER BY s.speed DESC
LIMIT 15;
If you want the users with the highest speed:
SELECT s.email, s.speed
FROM speed s
WHERE s.speed = (SELECT MAX(s2.speed) FROM speed s2)
LIMIT 15;
Note that joins are not needed for these queries.

Getting SUM based on quantity in MySQL?

I have this tables
ORDERS
orders_id
order_article_id
order_invoice_id
order_customer_id
order_qty
ARTICLES
articles_id
article_name
article_qty
article_price
article_amount
CUSTOMERS
customers_id
customer_name
customer_position
customer_office
And SQL Join Table
$sql="SELECT customer_name, article_name, orders_id FROM orders
LEFT JOIN articles ON order_article_id = articles_id
LEFT JOIN customers on order_customer_id = customers_id";
From this query I need to get AMOUNT.
Amount is example
USER: MICHAEL
AMOUNT: ORDER ID = order_article_id + order_qty;
Is it possible to do that in MySQL or i need some addition PHP code?
You can do it in MySQL, using a GROUP BY query and a SUM() aggregate function:
SELECT
c.customer_name,
SUM(a.article_price*a.article_quantity) AS amount
FROM
orders o LEFT JOIN articles a
ON o.order_article_id = a.articles_id
LEFT JOIN customers c
ON o.order_customer_id = c.customers_id
GROUP BY
c.customers_id,
c.customer_name
you might want to obtain the AMOUNT per order, then you need to group by also for the order_id:
GROUP BY
o.orders_id,
c.customers_id,
c.customer_name

Categories