mysql union or join just one query - php

I need to find a better way to get the discount for each article price in our web shop depending on which pricelist or pricelist/discount list a customer has. I think this is possible to do in just one query instead of the 5 I have today, but I really do not know where to start.
All our customers have a pricelist and some have both pricelist and one extra discount list. Today we have about 25 different pricelists and about 100 extra discount lists.
All the pricelists are structured in the same manner; they have a price group and a discount in percent.
For example pricelist 01 could look like
A 20
B 35
C 20
The extra discount list is structured in a different manner and can have a fixed price or percentage. It also has three different priority levels: discount based on the article code (has priority 1), based on category (has priority 2) and based on price group (has priority 3).
Discount list 0013 could look like:
In the article tab
PL-2344-444 40 (%)
P-0455-23 200 (SEK)
In the category tab
C12 50 (%)
N12 35 (%)
Today I have three different queries to see if I get a hit in the discount list:
First I check to see if I get a hit in priority 1: (FIXED returns f and PERCENTAGE r)
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013'
If the above returns 0, I do the second query, priority 2:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013'
And the last one priority 3:
SELECT DISCOUNT, FIXED, PERCENTAGE FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013'
If none of the extra discount lists returns 0 I get the discount from the pricelist
SELECT DISCOUNT FROM PUPRIREG WHERE PRICELIST = '01' AND PRICEGROUP = 'F'
I call the function like follows
$discount = discount($articlecode, $category, $pricegroup);
function discount($articlecode, $category, $pricegroup){
$articlecode = sanitizingData($articlecode);
$category = sanitizingData($category);
$pricegroup = sanitizingData($pricegroup);
// do priority 1
// prio 2
// prio 3
// pricelist
return $discount;
}
I would be so happy if someone could show me how to do this. I am using mysqli and php.
Many thanks
Best regards linda

You can do the queries with a union:
(SELECT DISCOUNT, FIXED, PERCENTAGE, 1 priority FROM PUPRIREG
WHERE ARTCODE = 'JO-23455' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 2 priority FROM PUPRIREG
WHERE CATEGORY = 'C15' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, FIXED, PERCENTAGE, 3 priority FROM PUPRIREG
WHERE PRICEGROUP = 'F' AND DISCOUNTLIST = '0013')
union
(SELECT DISCOUNT, 0 fixed, 0 percentage, 4 priority FROM PUPRIREG
WHERE PRICELIST = '01' AND PRICEGROUP = 'F')
order by priority;
The additional artificial priority column and order by ensures, that you get the discounts properly sorted.

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.

How to stop the loop and add the value?

I have the following table:
id tax rate quantity
1 20 400 5
2 20 566 2
3 5 200 4
Here is my expected output:
taxableamount taxamount total
3132.00 626.40 3758.40
800.00 40.00 840.00
Let me explain my attempts here:
If each row have the same tax value, I calculated the amount (rate × quantity) from each row of data and then calculate the tax amount (amount × tax ÷ 100) and displayed the result in a single row even if they come from multiple rows. But if the rows do not have same tax value, it will be displayed in separate rows.
$query = "SELECT SUM(
CASE WHEN tax=tax
THEN rate*quantity ELSE 0 END) AS taxableamount,
SUM(CASE WHEN tax=tax
THEN (rate*quantity)+(rate*quantity)*tax/100 ELSE 0 END) AS tax,
SUM(CASE WHEN tax=tax
THEN (rate*quantity)*tax/100 ELSE 0 END) AS taxamount,
SUM(CASE WHEN tax=tax
THEN (rate*quantity)+(rate*quantity)*tax/100 ELSE 0 END) AS total
FROM pricing group by tax";
while($row = show($query)):
echo '<td>'.$row->taxableamount.'</td>';
echo '<td>'.$row->taxamount.'</td>';
echo '<td>'.$row->total.'</td>';
endwhile;
The above code gives me wrong and unexpected result. So looking at my expected output, how do I go about it. Please help.
You should remove your CASE statements. As i said in my comment, tax it´s allways equal to tax, because you are comparing with the same row. I have added the tax column, so you know the amounts for each kind of tax:
SELECT
SUM(rate * quantity) AS taxableamount,
SUM(rate * quantity * tax / 100) AS taxamount,
SUM(rate * quantity * (1 + tax) / 100) AS total,
tax
FROM
pricing
GROUP BY
tax
what you are looking for is a group by tax in your sql statement. remove the remove case when. and two lines giving col called "tax"? that lokks like an error to me.
group by
aggregation

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

Exploding data from mysql using PHP

I have a database table where I am storing all the values from an external xml file. Since my project requirement demands me to deal with this unnormalized data. I need to help to extract data in an appropriate way.
I have two web pages (one for categories) and one for products.My database table looks like this:
**product_id Code Name ProductRange ProductSubRange WebCategory**
1 1002 Lid 30l Crystal;Uni LIDs Household products
2 10433 Casa Basket Silver Casa Casa Hipster BASKET Kitchenware
3 17443 Casa Basket Ice White Casa;Laundry LAUNDRY BASKET Laundry products
4 13443 Garden tub Eden Eden Garden Pots Household products
5 199990 Black Lid 201 Crystal Crystal Lids Household products
The product that belong to more than one productRange is indicated my semicolon(;). For example,above product_id 1 with name "Lid 301" belongs to two Product Ranges "Crystal" and "Uni". Same is for product_id 3. However product 2 belongs to single ProductRange.
MY QUESTIONs:
1) How can I construct a query so that it could return "ProductRange" based on my query_string values of "Webcategory"? For example:
if I get "Household Products" as my WebCategory from query string, it could give me distinct like this:
Household Products
|-Crystal
|-Uni
|-Eden
Laundry Products
|-Casa
|-Laundry
Kitchenware
|-Casa
2) Based on extracted ProductRanges, I want to display products separately in my webpages according to the product range and webcategory. For example, if I choose "Crystal" from above, it could give me Products with product_id "1" and "5" respectively like this:
Household Products|
|-Crystal
|-Lid 301 (product_id=1)
|-Balck Lid 201 (product_id=5)
|-Uni
|-Lid 301 (product_id=1)
|-Eden
|-Garden Tub
Kitchenware|
|-Casa
|-Casa Basket silver
Laundry Products|
|-Casa
|-Casa Basket Ice White
|
|-Laundry
|-Casa Basket Ice White
Can anyone guide me how can I retrieve records from the database and what I will need to do as I am new to programming? I would appreciate if anyone could help me in this regard.
In order to get distinct product ranges based on a give WebCategory input = 'XYZ', you can use the following - don't be intimidated by the numberstable, it's just a helpful table that contains rows each with increasing integer values from 1 ... up to N where N is the maximum number of characters in your ProductRange column. These can be made by hand or using a special insert/select query like the one found here:
http://www.experts-exchange.com/Database/MySQL/A_3573-A-MySQL-Tidbit-Quick-Numbers-Table-Generation.html
SELECT DISTINCT
SUBSTRING(ProductRange FROM number FOR LOCATE(';', ProductRange, number) - number) AS ProductRange
FROM (
SELECT ProductRange, CASE number WHEN 1 THEN 1 ELSE number + 1 END number
FROM (
SELECT mydatabasetable.ProductRange, numberstable.number
FROM mydatabasetable
INNER JOIN numberstable
ON numberstable.number >= 1
AND numberstable.number <= CHAR_LENGTH(mydatabasetable.ProductRange)
WHERE WebCategory = 'XYZ'
) TT
WHERE number = 1 OR (number + 1) <= CHAR_LENGTH(ProductRange)
) TT
WHERE SUBSTRING(ProductRange FROM number FOR 1) = ';'
OR numberstable.number = 1;
In order to retrieve a result set with all values WebCategory, ProductRange and Product for your website you can use the below slightly modified version derived from the above query. So that the results will appear more meaningful at first, I added an ORDER BY clause to keep all same-category, same-product-range products in sequence one after the other. This might or might not be desired as you might prefer to do that in your application/server-script code. In that case you can remove the ORDER BY clause without doing any harm.
SELECT WebCategory,
SUBSTRING(
ProductRange
FROM number
FOR LOCATE(';', ProductRange, number) - number
) AS ProductRange,
Product
FROM (
SELECT WebCategory, ProductRange, Product,
CASE number
WHEN 1 THEN 1
ELSE number + 1
END number
FROM (
SELECT WebCategory, ProductRange, Product, numberstable.number
FROM mydatabasetable
INNER JOIN numberstable
ON numberstable.number >= 1
AND numberstable.number <= CHAR_LENGTH(ProductRange)
) TT
WHERE number = 1 OR (number + 1) <= CHAR_LENGTH(ProductRange)
) TT
WHERE SUBSTRING(ProductRange FROM number FOR 1) = ';'
OR numberstable.number = 1
ORDER BY WebCategory, ProductRange, Product
You are probably going to want to do a GROUP BY clause in your query and maybe an JOIN if the detailed data is in a different table. If I understand you correctly it would look something like this.
SELECT T.WebCategory, T.ProductRange, T2.Product FROM table T
INNER JOIN table2 T2 ON T2.ProductRange = T.ProductRange
WHERE T.WebCategory = 'Household products'
GROUP BY T.WebCategory, T.ProductRange, T2.Product
It is tough to test my query without having a database setup to test against, but something like the above should return what you are looking for. You will of course need to rename your columns based on the actual names in the second table. Overall though, this should get you started if I understood the question correctly.

MYSQL sort function, depending on the arithmetic operation using the database field values as array keys

I have table of products, and there are 2 fields: price, currency. Possible values in currency fields are 1,2,3. 1 for MDL, 2 for USD, and 3 for EUR. I have to sort my products by price in mdl currency. So i have an array with rates:
$v = array(1,11.38,15.8);
Help my please with my query, I've tried something like this, but I've got errors:
$results = $this->Product
->query("SELECT `id`,`price`,`currency` FROM products
ORDER BY price*$v[currency] DESC");
Hmm... I`ll try to explain, through an example.
My table:
id|price|currency
_________________
1 | 500 | 2
2 | 300 | 3
It shows that first products price is saved in USD, and second products price is saved in EUR. But i have to sort them in MDL valute. So i get an array of rates for each value:
$rates = array([1] = > 1, [2] => 11.50, [3] => 15.50);
So i have to order my products by the result of formula: price*value rate
in first case: 500*$rates['currency value from db, in our case 2] = 500 * 11.50 etc.
Thanks in advance.
Because of the extended example on this problem I have edited this query.
Lets assume that the currencies are alse stored in some table, lets say currency (if not, it should be anyway).
Table currency should be as follows:
ID VALUE CODE
-----------------------------
1 1 USD
2 11.38 EUR
3 15.8 MDL
Then the query should be:
SELECT p.`id`, p.`price`, p.`price` * c.`value` AS 'ratio'
FROM products p
LEFT JOIN currency c ON c.`id` = p.`currency`
ORDER BY `ratio` DESC
By this query You select the currency value from the table currency depending on the currency ID from products table and finaly the results are ordered by the ration price * currency value.
I understand that maybe You have the currencies only hardcoded as array within some config, but it really would be better to put the currencies into the DB (if it is not).
You can`t use data base column name as array keys, because mysql is later instance than php. In php you simply generate query string that is passed to database managment system.
Your query should look like this:
$results = $this->Product->query
(
"SELECT `id`,`price`,
CASE `currency`
WHEN '1' THEN $v[0]
WHEN '2' THEN $v[1]
WHEN '3' THEN $v[2]
END AS 'ratio'
FROM products
ORDER BY price*ratio DESC
"
);

Categories