How to get associated row data from min value - php

I am trying to get the minimum price (totalprice) from a grouped column (sku) and then get the corresponding supplier data (supplier) associated with the resulting lowest price.
Table example
sku supplier totalprice
505 Sup1 20
505 Sup2 30
505 Sup3 25
605 Sup1 100
605 Sup2 97
605 Sup3 111
I am trying to group the sku column and get the lowest price and related supplier,
$query = "SELECT sku, supplier, MIN(totalprice) FROM pricetable GROUP BY sku";
$result = mysqli_query($conn, $query);
while($row = mysqli_fetch_array($result)) {
echo "The cheapest ". $row['sku']. " is £" .$row['MIN(totalprice)']." from ".$row['supplier'];
For example,
group 505 has the lowest price of 20 from supplier Sup1
group 606 has the lowest price of 97 from supplier Sup2
The code above gives me the lowest price for each sku group, but I can't get the associated supplier name with it.

You can try below - using correlated subquery
SELECT sku, supplier, totalprice FROM pricetable t1
where totalprice in
(select MIN(totalprice) from pricetable t2 where t1.sku=t2.sku GROUP BY t2.sku)

In MySQL 8.x you can do:
select *
from pricetable
where rank() over(partition by sku order by totalprice) = 1

Another common way to do this is to create a derived table with the sku and the min totalprice per sku and join back to your base dataset.
SELECT PT.sku, PT.supplier, PT.totalprice
FROM pricetable PT
INNER JOIN (SELECT min(totalprice) MTP, SKU
FROM pricetable
GROUP BY sku) B
on PT.SKU = B.SKU
and PT.MTP = PT.totalprice

Related

Sum with inner join in PHP

I'm working with PHP and MySQL, and I need to SUM the total amount of products joining 3 tables:
order_products: (There are multiple order products with the same name but different amounts in the table)
order_id (int)
product_name (varchar)
product_amount (int)
orders:
order_id (int)
order_date (varchar)
order_status (varchar)
supplier:
product_name (varchar)
product_amount (int)
So, I want to show how many products I sold and status is shipped and how many I ordered from the supplier in one single row. Any of two examples below will help me to achieve my goal.
Like:
Product Name (sum order_products) (sum supplier) Order status
first product 300 2500 Shipped_Only
second product 50 400 Shipped_Only
third product 10 600 Shipped_Only
Product Name (sum order_products) (sum supplier) Order status
first product 2200 2500 Not_Shipped
second product 400 400 Not_Shipped
third product 590 600 Not_Shipped
Are there any examples or other help that I can get to do this?
Edit:
Sample Data goes like this
order_products:
order_id product_name product_amount
255 product 1 200
256 product 1 100
257 product 2 50
258 product 3 10
orders:
order_id order_date order_status
255 09.05.2018 Shipped
256 09.05.2018 Shipped
257 10.05.2018 Not_Shipped
258 10.05.2018 Not_Shipped
supplier:
product_name product_amount
product 1 2500
product 2 400
product 3 600
You should use a join on the aggregated subselect.
SELECT t1.product_name, t1.sum_order_products, t2.supplier_sum, t1.order_status
FROM (
SELECT op.product_name, SUM(op.product_amount) sum_order_products, o.order_status
FROM order_products op
INNER JOIN orders o ON op.order_id = o.order_id
WHERE o.order_status = 'Shipped'
GROUP BY op.product_name, o.order_status
) t1
LEFT JOIN (
SELECT s.product_name, SUM(s.product_amount) supplier_sum
FROM supplier s
GROUP BY s.product_name
) t2 ON t1.product_name = t2.product_name
ORDER BY t1.order_status, t1.product_name
From What I understand, I think this is what you want, please give more clarity if this is not what you are expecting.
You will need to use GROUP BY clause and them you will have to use count() function to count the number of rows for the results coming from Group By Clause. I am writing an example of how to use a group by clause, you will need to modify the query as per your need.
SELECT
order_products.product_name,
count(*) as Total_Orders,
MAX(supplier.product_amount) as Supplier_Amt,
orders.order_status
FROM supplier
INNER JOIN order_products ON supplier.product_name = order_products.product_name
INNER JOIN orders ON orders.order_id = order_products.order_id
WHERE orders.order_status = 'Not_Shipped'
GROUP BY order_products.product_name, orders.order_status;
You will need to queries, you can write the other one, just replace WHERE orders.order_status = 'Not_Shipped' with WHERE orders.order_status = 'Shipped' Also if you want all in a single query, simply remove the where clause.

Get sum of min values in each group from Mysql

I am trying to retrieve the minimum price of some models.
Each model belongs to a certain group which belongs to a product.
I have the following tables:
Product
model_id product_id price
1 1 100
2 1 120
3 1 100
4 1 200
5 1 250
10 1 20
11 1 50
12 1 50
Product Overview
model_id product_id group_id
1 1 A
2 1 A
3 1 A
4 1 A
5 1 A
10 1 B
11 1 B
12 1 B
Product Group Optional
group_id product_id
B 1
Some groups could be optional, which means price will be zero unless the member wants to choose otherwise.
So in the example above, I want to get the sum of minimum price from each group.
We have two groups, group A and group B.
Group A minimum price value is 100 (model_id 1 and 3)
Group B minimum price value is 20 (model_id 10) but because Group B is optional then that means minimum price value is 0.
Overall sum of min values: 100 (Group A) + 0 (Group B) = 100
My code so far:
SELECT po.group_id,
CASE WHEN
((SELECT COUNT(*) FROM product_group_optional pgo
WHERE po.group_id = group_id AND po.product_id = 1 AND po.product_id = product_id) >= 1)
THEN SUM(0)
ELSE SUM(p.price)
END AS sum_price
FROM product_overview po, product p
WHERE po.product_id = 1
AND po.model_id = p.model_id
AND p.price = (
SELECT MIN(p2.price)
FROM product p2, product_overview po2
WHERE po2.product_id = 1 AND po2.group_id = po.group_id
AND po2.model_id = p2.model_id
)
GROUP BY po.group_id
The output:
group_id sum_price
A 200
B 0
The problem is that I get 200 for Group A but it should be 100.
There are 2 models with min value 100, model 1 and 3. And I assume these are sum together = 100 + 100 = 200.
Issue a) But I want to just take the min value, no matter how many times this value exists.
Issue b) Also, I am trying to get the SUM of those two output SUM of Group A and Group B.
But I am not sure how to do it.
I want it to be done in this query.
Desired output
Sum of all groups
100
Can anyone lead me to the right direction please?
You can use the following query:
SELECT SUM(min_price)
FROM (
SELECT po.group_id,
MIN(CASE WHEN pgo.group_id IS NULL THEN price ELSE 0 END) AS min_price
FROM Product AS p
INNER JOIN Product_overview AS po
ON p.product_id = po.product_id AND p.model_id = po.model_id
LEFT JOIN Product_group_optional AS pgo ON po.group_id = pgo.group_id
GROUP BY po.group_id) AS t
I'm not sure that I understand the keys of your tables, and the problem as well.
There is few questions.
a) The answer should be 120?
b) If the Product has no price, the is price null?
c) If there is a Product in a group with null price and others with price, should it be counted as 0?
Here is how you could get the sum of the lower prices of each group, ignoring the product_group_optional for while:
SELECT t2.group_id, sum(t2.new_price)
FROM
(
SELECT t.group_id, t.new_price
FROM
(
SELECT po.group_id, if(ifnull(pgo.product_id, true), p.price, 0) as new_price
FROM product p, product_overview po
LEFT JOIN product_group_optional pgo ON po.group_id = pgo.group_id
WHERE p.model_id = po.model_id
ORDER by po.group_id, new_price
) t
GROUP BY t.group_id
) t2

Min() with INNER JOIN and Full-text-search

So Ive got the following Query to work just fine, searching for 'test*' in 'products_desc' column and fetching all its prices in the 'prices' table.
SELECT products.id, prices.price, products.product_desc FROM products
INNER JOIN prices
ON prices.product_id = products.id
WHERE
MATCH (products.product_desc) AGAINST ('test*' IN BOOLEAN MODE)
Although the 'prices' table consists of multiple prices per product and I only want to fetch the lowest one to each product. I've previously filtered the prices using
INNER JOIN (
SELECT min(price) as price, prices.product_number as product_number FROM prices
WHERE prices.product_number LIKE'".$q."%'
GROUP BY prices.product_number
) min_prices
on prices.price = min_prices.price
and prices.product_number = min_prices.product_number
but this was when I used products_numbers within the prices table (now there is just a product_id-column.
Products
id | product_desc
----------------------------------------
1 | product1
2 | product2
Prices
id | product_id | price
------------------------------------------------
1 | 1 | 312
2 | 1 | 219
3 | 2 | 312
4 | 2 | 111
Also, the table consists of 10+ million rows so, efficiency matters a lot :)
EDIT
What if I need to access value of columns on the min(prices.price) row?
SELECT products.id, MIN(prices.price) as prices_price, prices.id as prices_id, products.product_desc, products.product_number, prices.supplier_id, suppliers.name FROM products
INNER JOIN prices
ON prices.product_id = products.id
INNER JOIN suppliers
ON prices.supplier_id = suppliers.id
WHERE
MATCH (products.product_desc) AGAINST ('test*' IN BOOLEAN MODE)
GROUP BY prices.product_id
The above returns the lowest price per product but also the wrong value in the other columns?
You can use GROUP BY for this, with MIN() group by function.
GROUP BY is used to group values from a column, and perform calculations on column.
In our case we want to group the result by product_id as it's repeating in second table and perform calculation (min()) on price column of second table.
This is how your Query would look like:
SELECT products.id, MIN(prices.price), products.product_desc FROM products
INNER JOIN prices
ON prices.product_id = products.id
WHERE
MATCH (products.product_desc) AGAINST ('test*' IN BOOLEAN MODE)
GROUP BY prices.product_id

Getting a daily summary of figures from shop database

I have the following tables, in a standard shop:
(id is always primary key, auto-increment, ts is always type TIMESTAMP, updated ON_UPDATE CURRENT_TIMESTAMP)
table sales:
id | total | tendered | flag | userID | ts
1 0.6 0.6 0 4 2013-11-21 08:12:23
Sales is the parent table, userID is related to the user that made the sale. total and tendered are both of type FLOAT. flag is of type VARCHAR and could be Free Order.
table receipts:
id | oID | pID | quantity | ts
1 1 26 1 2013-11-21 08:11:25
Receipts holds a line for each unique type of product sold. oID is type INT and relates to the id of table sales. pID is of type INT and relates to the id of table products.
table products:
id | name | price | cID | display | ts
1 Mars 0.6 3 1 2014-01-17 07:55:25
Products is the central data for each product in the database. Here is a line for mars bars. cID relates to the id in table categories.
table categories
id | name | display | ts
3 Snacks 1 2013-11-14 12:06:44
Categories is the table holding all the data about each category, and can have multiple products relating to a single row. display is of type INT and dictates when the category is enabled or disabled (1 = 'true')
My question is, I want to output information like this:
**Snacks**
(name) (quantity) (price) (total)
Fruit 3 50p £1.50
Twix 1 60p 60p
Boost 1 60 60p
**Hot Drinks**
(name) (quantity) (price) (total)
English Tea 15 60p £9.00
Speciality Teas 2 60p £1.20
Which I have the following SQL for:
SELECT categories.name AS category, products.name, pID,
(SELECT SUM(quantity) FROM receipts WHERE pID=r.pID AND DATE(ts) = CURDATE()) AS quantity,
products.price,r.ts
FROM receipts r
LEFT JOIN products ON r.pID = products.id
LEFT JOIN categories ON products.cID = categories.id
WHERE DATE(r.ts) = CURDATE()
GROUP BY r.pID
ORDER BY categories.name;
Which seems to give me the correct information, but I am not 100% certain. If anyone could verify that this works, I would be most grateful. But when I want to see a particular day, I get unusual figures with the following SQL:
$postfrom = $_POST['from_mm']."/".$_POST['from_dd']."/20".$_POST['from_yy'];
$postto = $_POST['to_mm']."/".$_POST['to_dd']."/20".$_POST['to_yy'];
$from = strtotime($postfrom . " 6:00");
$to = strtotime($postto . " 23:59");
$itemised = select("SELECT categories.name AS category, products.name, pID,
(SELECT SUM(quantity) FROM receipts WHERE pID = r.pID AND UNIX_TIMESTAMP(r.ts) > '{$from}' AND UNIX_TIMESTAMP(r.ts) < '{$to}')
AS quantity, products.price
FROM receipts r
LEFT JOIN products ON r.pID = products.id
LEFT JOIN categories ON products.cID = categories.id
WHERE UNIX_TIMESTAMP(r.ts) > '{$from}'
AND UNIX_TIMESTAMP(r.ts) < '{$to}'
GROUP BY r.pID
ORDER BY categories.name;");
(function 'select' simply returns an array of the SQL table). The thing is, I could find the results easily by looping through in PHP and adding it up that way. But I know this is possible with SQL, I just don't know why It isnt working. Can somebody please help?
Edit SQL sample fiddle is here: http://sqlfiddle.com/#!2/23af4 although I couldn't do more than half a day of data due to 8000 character restrictions.
Try this:
SELECT categories.name AS category, products.name AS name,
receipts.quantity AS quantity, products.price AS price,
(receipts.quantity * products.price) AS total
FROM categories
JOIN products
ON categories.id = products.cID
JOIN receipts
ON receipts.pID = products.ID
WHERE DATE(receipts.ts) = CURDATE()
ORDER BY categories.name
SQLFiddle demo
With regard to the date restriction, you could use BETWEEN ... AND ... to specify the date and time. Using an absolute date and time moment or relative to the current day and time, for example WHERE DATE(receipts.ts) BETWEEN concat(curdate() -5,' 6:00:00 AM') AND curdate() -4

Getting Sum of db field using join active record in codeigniter

I have a table name products with all product details and another whs_products with quantity details of the products for each warehouse.
i want select id, code and name from products table and sum of quantity where products.id = whs_products.product_id
I am trying this
$this->db->select("id, code, name");
$this->db->from("products");
$this->db->join('whs_products', 'products.id = whs_products.product_id');
$this->db->select("quantity");
I getting the list products that exists in whs_products not the sum. Some products are listed twice as they have 2 entries in whs_products.
I want list all the products once only where no quantity I want put 0 in quantity and where its is more than 1 in whs_products I want display sum of all the quantity
Help will be much appreciated!
Table Structure
Products
id, code, name, unit, price
whs_products
id, product_id, warehouse_id, quantity
I have whs table too for warehouse
id, name, address
I tried this Sir,
$this->db->select("products.id as productid, products.code, products.name, products.unit, products.cost, products.price, sum(whs_products.quantity) as 'totalQuantity'")
->from('products')
->join('whs_products', 'whs_products.product_id=products.id', 'left')
->group_by("products.id");
$this->db->get();
Every thing is fine. But the total number of products are calculated wrongly. I think system add 1 to total products, each time gets quantity from whs_products. For some products quantity is 2 or 3 time depending on each warehouse.
Any solutions for this. I am very thankful for your support.
Please try out the following query and comment.
SQLFIDDLE DEMO
Sample data:-
Products
PID PNAME
1 j
2 k
3 m
whs_Products
WID PID QUANTITY
11 2 300
11 2 200
14 2 500
11 1 300
15 3 100
14 3 800
Query to get total by pid in whs_products
select pid, wid, sum(quantity)
from whs_products
group by pid, wid
;
Results:
PID WID SUM(QUANTITY)
1 11 300
2 11 500
2 14 500
3 14 800
3 15 100
query using a variable to get user input for pid and by pid, wid
-- group by pid and wid
set #var:='2'
;
select a.pid, b.pname, a.wid, sum(a.quantity)
from whs_products a
join products b
on b.pid = a.pid
where a.pid = #var
group by a.pid, wid
;
Results:
PID PNAME WID SUM(A.QUANTITY)
2 k 11 500
2 k 14 500
final query to show quantity by user input pid only
Query:
-- by pid only
set #var:='2'
;
select a.pid, b.pname, sum(a.quantity)
from whs_products a
join products b
on b.pid = a.pid
where a.pid = #var
group by a.pid
;
Results:
PID PNAME SUM(A.QUANTITY)
2 k 1000
Since OP wants in CodeIgniter
Here is a headstart for you to try. At first I had the impression you already know the syntax of codeigniter and you are looking for SQL logic, so you could convert it into the desired format you need.
$this->db->select("a.pid, b.pname, count(a.quantity) as 'toalQuantity'");
$this->db->from('wsh_products a');
$this->db->join('products b', 'a.pid=b.pid', 'inner');
$this->db->group_by("a.pid");
$where = "a.pid = 2";
$this->db->get();
$query->results_array();
Or write a funciton :) :
function getQuantity($prodid = false)
{
$this->db->select(a.pid, b.pname, count(a.quantity) as 'toalQuantity');
$this->db->join('wsh_products a', 'a.pid=b.pid');
if ($prodid !== false)
$this->db->where('a.pid', $prodid);
$query = $this->db->get('products b');
if($query->result() == TRUE)
{
foreach($query->result_array() as $row)
{
$result[] = $row;
}
return $result;
}
}
Edit as OP requested for LEFT JOIN in comments
SQLFIDDLE
To show all products in Products table, do the following:
In select show pid from Products table.
Use from Products Left Join Whs_Products
Group by pid from Products table

Categories