limit sub-query result set to last record - php

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

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)

Mysql query not giving exact records

I want the unique records against each user and product id. So my query is working fine but a small issue is, I am not getting the exact value of columns against the order id.
select max(o.id) as order_id, u.email,oi.product_id, DATE_FORMAT(FROM_UNIXTIME(o.paid_time), '%Y-%m-%d') as paid_times
from `order` o
join `users` u on u.id = o.user_id
join order_item oi on oi.order_id = o.id
where oi.product_id in (1212,1213)
group by u.email, oi.product_id
order by o.id desc;
Try - 2
select max(o.id) as order_id, u.email,oi.product_id, DATE_FORMAT(FROM_UNIXTIME(o.paid_time), '%Y-%m-%d') as paid_times
from `order` o
join `users` u on u.id = o.user_id
join order_item oi on oi.order_id = o.id
where oi.product_id in (1212,1213) and o.id IN (SELECT max(id) FROM `order`)
group by u.email, oi.product_id
order by o.id desc;
I get the max order id, which is fine. but the paid_time is not of this order id.
Current Results:
Desired Results:
This is very common type of error. for this first you need to get the max order id than use it in where condition as taking max with select and others columns mysql gives you max order id and any row data which comes first not the data related to max id.
Understand below example
SELECT name
FROM table
WHERE id=(
SELECT max(id) FROM table
)
above will give you data of max(id) from table
SELECT name , max(id)
FROM table
the above query will display Maximum id but not the matching name
http://www.plus2net.com/sql_tutorial/sql_max.php

My SQL sum returning double values

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

Mysql Ranking based on Category and Branch

I'm having a hard time figuring out and trying how to fix this.
Can you help me give a logic or idea how can get the ranking of each category for each branch based on sales?
For example:
Rank 1 for branch_code_id = 9 is Accicular since it has 300,000 sales
Rank 2 for branch_code_id = 9 is WLO since it has only 200,000
sales.
Same as with other branches. I only need the rank of category for each branch_code_id.
I can't figure out how to loop this one. Rank will be placed in the "r" column as you can see in the excel output.
By the way, here's the sql statement i used to get the result you see in the screenshot.
SELECT
a.id,
a.date,
a.branch_code_id,
SUM(b.amount),
c.category
FROM
sales_add_h AS a
INNER JOIN sales_add_i AS b ON a.id = b.sales_h_id
INNER JOIN control_panel_item_create AS c ON b.item_code_id = c.id
GROUP BY c.category, a.branch_code_id, b.amount
ORDER BY SUM(b.amount) DESC
Thanks Guys!
Try this query
SELECT
#rn:=if(#prv=branch_code_id, #rn+1, 1) as rId,
#prv:= branch_code_id as branch_code_id,
val,
id,
date,
category
FROM
(SELECT
a.id,
a.date,
a.branch_code_id,
SUM(b.amount) as val,
c.category
FROM
sales_add_h AS a
INNER JOIN
sales_add_i AS b ON a.id = b.sales_h_id
INNER JOIN
control_panel_item_create AS c ON b.item_code_id = c.id
GROUP BY
c.category, a.branch_code_id, b.amount
ORDER BY
a.branch_code_id, SUM(b.amount) DESC)tmp
JOIN
(SELECT #rn:=0, #prv:=0)t
SQLFIDDLE to understand how ranking works.
I have done ranking for each branch_id as you have mentioned, if you want to rank for each category in a particular branch than you need to add another variable which stores the category and compare it within the if clause and also need to sort data within inner query accordingly order by c.category, a.branch_code_id, SUM(b.amount) DESC

How to order the category that has most product

I'm not so clear in one of mysql.
i have two tables:
tblcategory
id, name
tblproduct
id, Name, Qty, Price, Category_id
and when i use sql to select all catogories to order by product count:
SELECT c.id,c.name,count(p.id) as product_count
FROM tblcategory as c inner join tblproduct as p on c.id=p.category_id
GROUP BY c.id,c.name
ORDER BY product_count;
The result was that some category which has no product was not appear in my result! how can i get all of them?
You need to use a left outer join:
SELECT c.id,c.name,count(p.id) as product_count
FROM tblcategory as c left outer join tblproduct as p on c.id=p.category_id
GROUP BY c.id,c.name
ORDER BY product_count;
The inner join only keeps records that match in both tables. You want all the product categories, even when there are no matches. The left outer join keeps all records in the first table (tblcategory) along with any matching records in the second table (tblproduct). If there are no products on a category, then you'll get a value of 0 instead of a missing row.

Categories