Left Join to get previous record sql - php

So I am working on a system to manage best before checks in stores and when you're on a overview page of a check you see the following table
Productgroup -> Productname -> Barcode -> Checked -> Checked Time -> New best before -> Old best before
I managed to gather all the data but Old best before using this query:
SELECT `thtchecksproducts`.*, productName, productGroup, productBarcode FROM `thtchecksproducts`
LEFT JOIN `products` ON thtchecksproducts.productID = products.productID
WHERE `checkID`=:checkID ORDER BY checked DESC, productGroup ASC, productName ASC
now I need it to do another left join with the FROM table, thtchecksproducts. It needs to join on productID just like the last one but only with the last record that matches this query:
SELECT productID, max(id) FROM thtchecksproducts WHERE checkID IN (SELECT checkID FROM thtchecks WHERE userID=:userID AND closed=1 AND checkID IS NOT :checkID)
I came up with this:
SELECT s1.*, productName, productGroup, productBarcode FROM `thtchecksproducts` s1
LEFT JOIN `products` ON thtchecksproducts.productID = products.productID
LEFT JOIN thtchecksproducts s2 ON s1.productID = s2.productID
AND s1.id = (SELECT productID, max(id) FROM thtchecksproducts
WHERE checkID IN (SELECT checkID FROM thtchecks WHERE userID=:userID AND closed=1 AND checkID IS NOT :checkID) AND productID = s1.productID)
WHERE `checkID`=:checkID ORDER BY checked DESC, productGroup ASC, productName ASC
Anyone who could help me getting this to work?

Steffan:
You need to provide columns of all tables in your problem and business logic.
What rule describes "Old best before date"? Is the "New Best Before date" always greater than sysdate? In addition, is the query to determine the "Old best before date" always firing before a "New best before date" has been committed?
CK.

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)

Searching multiple databases to output lowest value

$query = mysqli_query($mysqli, "SELECT product_name ,MIN(product_price) product_price,link FROM(
select jumia.product_name,jumia.product_price,jumia.product_image,jumia.link from jumia INNER JOIN
jumia ON jumia.id = olx.id
where product.name like '%{$search}%'
UNION
select olx.product_name,olx.product_price,olx.product_image, olx.link from olx INNER JOIN
olx ON olx.id = jumia.id
where product.name like '%{$search}%')Minim
GROUP BY product_name,product_image
");
I am trying to create a query from two tables with similar column names as displayed above that will allow me to display the rows between the two tables that have the lowest price.
For example, if product_name called mattresses is searched the matching item in my database whose price is lower between the two table names should be displayed. Some help would be appreciated
I think this is the general idea of what you're trying to do:
SELECT id, price
FROM (SELECT id, price FROM T1 WHERE price = (SELECT min(price) FROM T1)
UNION
SELECT id, price FROM T2 WHERE price = (SELECT min(price) FROM T2)
) AS M
ORDER BY price ASC
LIMIT 1
Ended up changing the code and removing the ordering at the end and this finally worked. I hadn't properly linked my database using foreign keys, and changed my code to reflect this.
$query = mysqli_query($mysqli, "
SELECT product_name
, MIN(product_price) product_price
, link
FROM
( select j.product_name
, j.product_price
, j.link
from jumia j
JOIN olx
ON j.categoryID = olx.categoryID
where j.product_name like '%{$search}%'
UNION select olx.product_name
, olx.product_price
, olx.link
from olx
JOIN jumia
ON jumia.categoryID = olx.categoryID
where olx.product_name like '%{$search}%'
) x
");

MYSQL: distinct values in join - price is double counted

I have a report that joining 5 tables:
1 - Coupon Uses: id - coupon_id - user_id - order_id
2 - Coupon: id - code - discount_type - discount_amount
3 - Order: id - user_id - orderread_ts - order_status
4 - Payments: id - user_id - order_id - sub - tax - discount - total - payment_collected
5 - User: id - email - fname - lname
So I'm running a report that takes in several criteria: Promo name, user email, start date, end date.
Here is my query:
SELECT
`Coupon`.`code` AS Promotion,
CONCAT('',o.`id`,'') AS Order_ID,
o.`orderread_ts` AS Order_Date,
u.`email` AS Customer_Email,
CONCAT(u.`fname`, " " ,u.`lname`) AS Name,
p.`subtotal` AS Subtotal,
p.`discount` AS Discount,
p.`total` AS Total
FROM `Coupon`
LEFT JOIN `Coupon_Uses` AS cu ON cu.`coupon_id` = `Coupon`.`id`
LEFT JOIN `Order` AS o ON o.`id` = cu.`order_id`
LEFT JOIN `User` AS u ON u.`id` = o.`user_id`
LEFT JOIN `Payments` AS p ON p.`id` = o.`id`
WHERE `Coupon`.`code` = 'email10'
AND o.`orderread_ts` > 0
AND o.`order_status` = ''
AND p.`discount` > 0
AND p.`payment_collected` = '1'
GROUP BY o.`id`
I'm getting the correct results, however, if during checkout, the sale wasn't complete (bad payment, wrong cc entry, etc.) the table Coupon Uses will have two entries for the same Order_id.
Now, the order, when counted will be show incorrect information.
What I can't figure out is how to get distinct order_id from the coupon_uses table.
UPDATED FINAL QUERY
SELECT
c.`code` AS Promotion,
o.`id` AS Order_ID,
o.`orderread_ts` AS Order_Date,
u.`email` AS Customer_Email,
CONCAT(u.`fname`, " " ,u.`lname`) AS Name,
p.`subtotal` AS Subtotal,
p.`discount` AS Discount,
p.`total` AS Total
FROM `Coupon` c
INNER JOIN (SELECT DISTINCT coupon_id, user_id, order_id
FROM `Coupon_Uses`) AS cu ON cu.coupon_id = c.id
INNER JOIN `Order` AS o
ON o.`id` = cu.`order_id`
INNER JOIN `User` AS u
ON u.`id` = cu.`user_id`
INNER JOIN `Payments` AS p
ON p.`order_id` = cu.`order_id`
WHERE c.`code` = 'email10'
AND o.`orderread_ts` > 0
AND o.`order_status` = ''
AND p.`discount` > 0
AND p.`payment_collected` = '1'
First change what the I used SELECT DISTINCT in the first join to make sure i'm getting only unique values. I joined each other table on the fields from coupon_uses, and I had an error in syntax on the last join, which is now fixed.
For performance: 683 rows affected, taking 30.1ms
If I understand correctly you can have the situation where you have two records in the Coupon_Uses table which only differ in their id value. coupon_id, user_id, order_id could be the same...
If that is so, I would really suggest to look into that, as it doesn't seem right. Could you not write such a duplicate record in another table during the transaction, so you can ensure there are no such duplicates? Or could you not add a status column so the temporary record gets a Draft status, while all others are Final?
Anyway, if this is the situation, you can solve it as follows in the query:
Replace:
LEFT JOIN `Coupon_Uses` AS cu ON cu.`coupon_id` = `Coupon`.`id`
with:
LEFT JOIN (SELECT DISTINCT coupon_id, user_id, order_id
FROM `Coupon_Uses`) AS cu ON cu.`coupon_id` = `Coupon`.`id`
But this could have a performance impact, as the join can probably not benefit from using an index.

mysql query to fetch all products with no orders for a user

I have a Products table and an Orders table defined in such a way that I can do JOIN query as the following to return Products with zero orders for a specific user.
This query works but its very slow.
select * from products where id not in (select product_id from orders where user_id = 1)
The question is, how to write same query better way and faster?
No need of a subquery for that:
SELECT p.product_id
FROM
Products p
LEFT JOIN Order o ON p.product_id = o.product_id AND o.user_id = #UserId
WHERE
o.order_id IS NULL -- or any other field that cannot be null on Order
EDIT: for increased performance you may want to check as well that you have indexes in place on the Order user_id column and on your ids (more likely you have them there and probably clustered indexes, both worth to check)
You should be able to do simple LEFT JOIN
SELET * FROM products
LEFT JOIN orders ON (orders.product_id=products.id and orders.user_id=1)
WHERE orders.id IS NULL;
SELECT P.*
FROM products P
LEFT JOIN (SELECT product_id from orders where user_id = 1) O
ON P.id = O.product_id
WHERE O.product_id IS NULL

Unable to return results for MySQL query

My goal is to only show purchase orders that haven't received all their product yet.
I have a master table called test_po and two other tables test_po_bom, test_rog_bom. The test_po_bom and test_rog_bom tables are where I'm storing the list of products. test_po_bom is the list of products I've ordered, test_rog_bom is the list of products I've received.
Basically: loop purchase_orders WHERE products_received < products_ordered
Table Structure:
table `test_po`: `ID`, `vendor_ID`
table `test_po_bom`: `ID`, `po_ID`, `product_ID`, `quantity`
table `test_rog_bom`: `ID`, `po_ID`, `product_ID`, `quantity`
Code:
$SQL = "SELECT
*,
test_po.ID AS test_po_ID
FROM
test_po
LEFT JOIN test_po_bom ON test_po_bom.po_ID=test_po.ID
LEFT JOIN test_rog_bom ON test_rog_bom.po_ID=test_po.ID
WHERE
(SELECT SUM(quantity) FROM test_rog_bom WHERE po_ID=test_po.ID) < (SELECT SUM(quantity) FROM test_po_bom WHERE po_ID=test_po.ID)";
$result = mysql_query($SQL) or die(mysql_error());
while($row = mysql_fetch_array($result))
{
echo $row['test_po_ID'].'<br>';
}
It doesn't spit out anything, and I've tried many different variations but I just can't figure it out.
The problem appears to be with your query. Don't use * and instead specify the desired columns. The following solution uses aliases to help make your code more readable, especially with similar names. You will also notice HAVING instead of WHERE.
SELECT
p.ID as PO_ID
,p.VENDOR_ID
,pb.product_ID as PRODUCT_ID
,SUM(pb.quantity) as QUANTITY_ORDERED
,SUM(rb.quantity) as QUANTITY_RECEIVED
FROM test_po as p
LEFT JOIN test_po_bom as pb ON pb.po_ID = p.ID
LEFT JOIN test_rog_bom as rb ON rb.po_ID = p.ID
GROUP BY
p.ID
,p.VENDOR_ID
,pb.product_ID
HAVING SUM(rb.quantity) < SUM(pb.quantity)
THIS IS UNTESTED CODE. I apologize for posting untested code but I can't test it right now, and I think it demonstrates some things for you to try differently even if it's not exactly correct.
Try this:
select po.ID, po_bom.quant n_ordered, rog_bom.quant n_received
from test_po po
left join (select po_ID, sum(quantity) as quant from test_po_bom group by po_ID) po_bom
on po.ID = po_bom.po_ID
left join (select po_ID, sum(quantity) as quant from test_rog_bom group by po_ID) rog_bom
on po.ID = rog_bom.po_ID
where coalesce(rog_bom.quant, 0) < coalesce(po_bom.quant, 0);
This changes a few things from how you were doing them:
Uses table aliases to clearly specify which references refer to the same table row.
Uses group by to aggregate the sums by ID.
Uses coalesce to deal with situation where at least one of your tables (probably test_rog_bom) has no rows for an ID. I suspect this was actually the source of your problem in the first place.

Categories