I have table in mysql which stores Discounts applicable on restaurants. Structure is as below:
id res_id disc_amt disc_percntge
------------------------------------------
1 2 300 15
2 2 500 25
$sqldisc = "select * from disc_n_coupons where restaurant_id=?";
$datadisc = DB::instance()->prepare($sqldisc)->execute
(array($_SESSION['res_in_sess']))->fetchAll();
Now, i want to apply that if cart total has reached to disc_amt then disc_percntge shall be applied.
Currently both the discounts are getting applied i.e. If Total in cart has reached 500 then 15% is applied and then on discounted price again 25% is getting applied.But i want only one disc to be applied as per amount mentioned.
Below is my logic
foreach($datadisc as $rowsdisc){
if($subtl>$rowsdisc['disc_amt']){
echo $subtl."*".$rowsdisc['disc_percntge']."/"."100";
$subtldisc = ($subtl*$rowsdisc['disc_percntge'])/100;
}
$subtl = $subtl-$subtldisc;
}
You should see to that the query returns discounts in descending order, biggest amount first (if a bigger discount applies, the smaller ones following should be ignored)
$sqldisc = "select * from disc_n_coupons where restaurant_id=? ORDER BY disc_amt DESC";
Then all you need to do is break the loop once the first allowed discount has been applied, something like;
foreach($datadisc as $rowsdisc) {
if($subtl < $rowsdisc['disc_amt'])
continue;
echo $subtl."*".$rowsdisc['disc_percntge']."/"."100";
$subtldisc = ($subtl*$rowsdisc['disc_percntge'])/100;
$subtl = $subtl-$subtldisc;
break;
}
May be you can use limit the query to apply one offer
$sqldisc = "select * from disc_n_coupons where restaurant_id=? and disc_amt >=? order by disc_amt desc limit 1";
$datadisc = DB::instance()->prepare($sqldisc)->execute
(array($_SESSION['res_in_sess'],$subtl))->fetchAll();
Related
there is a way through query to scale the quantity of a product that can be double on the table because it comes from 2 different orders taking into account the customer's request and the quantity available for each product
For example, I have 2 wines with the same id_wine but coming from 2 different orders, so they have a different id_order, where they have as availability:
id
id_wine
qty
id_order
1
1
4
1
2
1
1
2
Total qty 5
If I order 5 wines, is there a way to be able to scale the counter of the first wine by 4 units and the last one to scale it from the other wine?
I can scale if I place single orders, so as long as the availability is> 0 I scale from the first, if <switch to 2 wine, but if I place an order with 5 wines all together, I scale only the first one, bringing the units to -1.
I'm doing this at the moment:
$scaleqty=warehouse::where('id_restaurant',Auth::user()->id_restaurant)
->where('id_wine',$wine_id)
->whereRaw('quantita_restante > 0')
->orderBy('id_order')
->first()
->decrement('quantita_restante',$request->quantita);
I don't know if you can do it this way but if i were you i would get all the data and loop over it and decrement only the quantity it has in stock assuming you would validate the stock before performing operation.
$quantity = $request->quantita;
$stocks = warehouse::where('id_restaurant',Auth::user()->id_restaurant)
->where('id_wine',$wine_id)
->whereRaw('quantita_restante > 0')
->orderBy('id_order')
->get();
foreach($stocks as $stock){
if($stock->qty < $quantity){
$stock->decrement($stock->qty);
$quantity = $quantity-$stock->qty;
}
}
This is just for the part where the quantity of order is greater than the greatest quantity of stock in your database which you can check by ordering the data in descending order of quantity and check first and if it is greater than the requested order quantity you can subtract from that stock so you don't have to do this calculation.
$stock = warehouse::where('id_restaurant',Auth::user()->id_restaurant)
->where('id_wine',$wine_id)
->orderBy('qty','desc')
->first();
if($stock->qty > $request->quantita){
$stock->decrement($request->quantita)
}else{
//code from above make sure to check you stop the loop once the request varaible becomes zero
}
if someone needs it in the future, I have solved my problem:
$selectwine=warehouse::where('id_restaurant',Auth::user()->id_restaurant)
->where('id_wine',$wine_id)->whereRaw('quantita_restante > 0')
->orderBy('id_order')
->get();
$qta=$request->quantita;
foreach ($selectwine as $key => $value) {
if ($value->quantita_restante <= $qta) {
$qtarest=$value->quantita_restante;
$qta=$qta-$value->quantita_restante;
$value->decrement('quantita_restante',$qtarest);
}elseif($value->quantita_restante > $qta){
$value->decrement('quantita_restante',$qta);
if($qta=0){
break;
}
}
}
I would like to SELECT certain data out of my mysql DB. I am working with a php loop and a sql statement with a LIMIT and UNION.
Problem: The speed of my query is terrible. One UNION statement tooks 2-4 seconds. Due to the loop the Overall-Query takes 3 Minutes.
Is there a chance to optimize my query?
I tried to separate the "three" statements and merge the results. But this is not really faster. So I think that the UNION is not my problem.
PHP/SQL:
My code is running through two-foreach-loops. The code is working properly. But the performance is the problem.
$sql_country = "SELECT country FROM country_list";
foreach ($db->query($sql_country) as $row_country) { //first loop (150 entries)
$sql_color = "SELECT color FROM color_list";
foreach ($db->query($sql_color) as $row_color) { //second loop (10 entries)
$sql_all = "(SELECT ID, price FROM company
WHERE country = '".$row_country['country']."'
AND color = '".$row_color['color']."'
AND price BETWEEN 2.5 AND 4.5
order by price DESC LIMIT 2)
UNION
(SELECT ID, price FROM company
WHERE country = '".$row_country['country']."'
AND color = '".$row_color['color']."'
AND price BETWEEN 5.5 AND 8.2
order by price DESC LIMIT 2)
UNION
(SELECT ID, price FROM company
WHERE country = '".$row_country['country']."'
AND color = '".$row_color['color']."'
AND price BETWEEN 8.5 AND 10.8
order by price DESC LIMIT 2)";
foreach ($db->query($sql_all) as $row_all) {
$shopID[] = $row_all['ID']; //I just need these IDs
}
}
}
Do you have any idea or hints to get this faster?
An index on (country, color, price, ID) should improve the performance of single queries from seconds to a couple of milliseconds or even less. But you still have the problem of executing 1500 queries. Depending on your system, a single query execution can add an overhead of about 10 ms, which would add up to 15 seconds in your case. You need to find a way to minimize the number of queries - In best case to a single query.
For low limits (like 2 in your case), you can combine multiple LIMIT 1 subqueries with different offsets. I would generate such a query dynamically.
$priceRanges = [
['2.5', '4.5'],
['5.5', '8.2'],
['8.5', '10.8'],
];
$limit = 2;
$offsets = range(0, $limit - 1);
$queryParts = [];
foreach ($priceRanges as $range) {
$rangeFrom = $range[0];
$rangeTo = $range[1];
foreach ($offsets as $offset) {
$queryParts[] = "
select (
select ID
from company cmp
where cmp.country = cnt.country
and cmp.color = clr.color
and cmp.price between {$rangeFrom} AND {$rangeTo}
order by cmp.price desc
limit 1
offset {$offset}
) as ID
from country_list cnt
cross join color_list clr
having ID is not null
";
}
}
$query = implode(' UNION ALL ', $queryParts);
This will generate a quite long UNION query. You can see a PHP demo on rexester.com and SQL demo on db-fiddle.com.
I can't guarantee it will be any faster. But it's worth a try.
I have a script which returns the ID's of the warehouses (4,1,2,10,9) in order how close they are to the customer.
$warehouse_rank = array('0'=>4,'1'=>1, '2'=>2, '3'=>10, '4'=>9);
When I lookup a product from the database, I return a breakdown of which warehouses have it in stock and the quantity. Like so:
$product_breakdown = array(
'storage'=>array(
'10001'=>array(
'total_stock'=>89,
'breakdown'=>array(
'4'=>0,
'1'=>89,
'2'=>0,
'10'=>0,
'9'=>0
)
)
)
);
10001 is the product ID in this case.
I made this loop to determine which warehouse has the desired quantity, so then I can order it:
foreach ($warehouse_rank as $key => $warehouse_id){
if($product_breakdown['storage'][$product_id['output']]['breakdown'][$warehouse_id] >= $posted->order->quantity) {
}
}
However the problem is this will only detect when the requested quantity is available as whole in the warehouse.
I cannot wrap my head around how to go about when the requested quantity is spread out in multiple warehouses.
For example:
They request 20 pieces.
And the distribution is as follows '4'=>5pc, '1'=>5pc, '2'=>1pc, '10'=>8pc, '9'=>10pc.
So ideally the warehouses will be assigned something like this: WID:4=5pc, WID:1=5pc, WID:9=10pc.
There are two factors, how close the warehouse is; but also to accomplish the allocation with the least warehouses possible.
Any ideas, suggestions how to approach this?
The number of warehouses is dynamic, there could be more or less warehouses. And I'd like to take out as much quantity as possible from the closest warehouse. Thats why I have $warehouse_rank.
PS. I'm not looking for help on how to make the actual order. Just the quantity allocation per warehouse.
Alter your database query so it adds a generated column. Have it calculate the distance from each warehouse to the customer. Add a flat rate weighting value to this total to compensate for each extra warehouse added to the supply chain.
Divide the result by the amount of stock to get a cost per unit figure of supplying from each warehouse. Order your database query on this column pick from the top until you have enough stock for the order.
So you have a table like this:
CREATE TABLE IF NOT EXISTS `stock` (
`warehouse` char(11),
`stock` int(10),
`distance` int(10)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `stock` (`warehouse`, `stock`,
`distance`)
VALUES
('A', 4, 3),
('B', 6, 8),
('C', 10, 10);
And you query it like this, where the 15 in stock + 15 is the weighting value you want to add:
SELECT *, ((stock + 15) / distance) AS per_unit
FROM stock
ORDER BY stock DESC, per_unit ASC
Heres a demo sqlfiddle
Try this.
It will first check to see if there is enough quantity. Else echo not enough.
But you have to deal with that somehow.
Then it will foreach loop with key being the warehouse and breakdown being the quantity in that house.
Subtract the amount possible or what is left of order and store the order in variable order.
$orderqt = 25;
if($orderqt <= $product_breakdown['storage']['10001']['total_stock']){
foreach($product_breakdown['storage']['10001']['breakdown'] as $key => &$breakdown){
if($orderqt>0){
$possibleWarehouses = array_filter(
$product_breakdown['storage']['10001']['breakdown'],
function ($value) use($orderqt) {
return ($value >= $orderqt);
}
);
if(count($possibleWarehouses) != 0){
$house = key($possibleWarehouses);
$order[$house] = $orderqt;
$product_breakdown['storage']['10001']['breakdown'][$house] -= $orderqt;
$orderqt = 0;
}elseif($orderqt >= $breakdown){
$orderqt -= $breakdown;
$order[$key] = $breakdown;
$breakdown = 0;
}else{
$order[$key] = $orderqt;
$orderqt = 0;
$breakdown -= $orderqt;
}
}else{
break;
}
}
}else{
echo "not enough";
}
var_dump($order,$product_breakdown);
Added array_filter to see if any warehouse has the more than the orderd quantity.
$possibleWarehouses is an array that will hold warehouses that can fullfill the rest of the quantity that is ordered.
In this case it will be NULL, NULL and then warehouse 9 with 10 in quantity.
https://3v4l.org/ZeUrt
Each page lists all the coupons available for a specific retailer. I query the database for all the coupon codes in the header since I count the number of rows returned and use that info in the meta title of the page. I now also want to display the titles of the first 2 coupons in the array. How would I go about extracting the first 2 results from the array without querying the database again?
This is what I have so far:
$retailer_coupons = "select C.couponid,C.fmtc_couponid,C.merchantid,C.exclusive,C.label,C.shoppingtip,C.restrictions,C.coupon,C.custom_order,C.link,C.image,C.expire,C.unknown,M.name,M.approved,M.homepageurl,M.category from tblCoupons C,tblMerchants M where C.merchantid=M.merchantid and C.begin < ".mktime()." and C.expire > ".mktime()." and C.merchantid=".$merchantid." and M.display='1' and C.user_submitted='' order by C.custom_order desc, C.coupon desc";
$retailer_coupons_result = mysql_query($retailer_coupons) or die(mysql_error());
$count_coupons=mysql_num_rows($retailer_coupons_result);
$meta_title = ''.$name.' Coupon Codes ('.$count_coupons.' coupons available)';
Suppose I have 3 records in my table. If I execute below query, I will get 2 results however the count(*) will give me 3 as output
SELECT count(*) FROM temp.maxID limit 2
In your case it will be
$retailer_coupons =
"select C.couponid,C.fmtc_couponid,C.merchantid,C.exclusive,C.label,C.shoppingtip,C.restrictions,C.coupon,C.custom_order,C.link,C.image,C.expire,C.unknown,M.name,M.approved,M.homepageurl,M.category
from tblCoupons C,tblMerchants M
where C.merchantid=M.merchantid
and C.begin < ".mktime()." and C.expire > ".mktime()."
and C.merchantid=".$merchantid." and M.display='1'
and C.user_submitted=''
order by C.custom_order desc, C.coupon desc
limit 2";
limit 2 will do the magic... Cheers!!!
Good Luck!!!
Something like this:
$res = mysql_fetch_assoc($retailer_coupons_result);
$i = 0;
while ($i < 2){
echo $res[$i]['label']."\n";
$i++;
}
I have to calculate a price based on a rate structure along these lines:
$303.00 fixed price up to 500 units
$0.023 additional per unit from 501-10,000 units
$0.022 additional per unit from 10,001-25,000 units
$0.021 additional per unit from 25,001-50,000 units
I'm a little lost on setting up a database structure and algorithm (the larger sticking point) for calculating this. Has anyone done this? Is there a nice, elegant way of calculating this sort of thing?
edit: As an example, a 25,100 unit run would cost $303.00 for the first 500 units, $218.50 for the next 9,500 units, $330.00 for the next 15,000 units, and $2.10 for the next 100 units, for a total of $853.60.
It wouldn't be a simple 25,100 * $0.021 calculation - I'm well aware of how to select and calculate that.
Similar to the way income tax is assessed - on a marginal basis.
I assume you want something flexible, otherwise it would be trivial to hardcode it.
You could use a pricing table:
ID MAX FIX UNIT
1 500 303 0
2 9500 0 .23
3 15000 0 .22
4 25000 0 .21
Then you could calculate as follows:
$items = ?;
$cost = 0;
$rows = get_rows("select max, fix, unit from pricing order by id asc");
foreach ($rows as $r)
{
if ($items <= 0)
break;
$cost += $r['fix'] + min($r['max'], $items) * $r['unit'];
$items -= $r['max'];
}
I have assumed that you want the algorithm in PHP.
Python
from collections import namedtuple
RateRule= namedtuple( 'RateRule', ['qty_band','fixed','per_unit'] )
rate_table = [
RateRule(500, 303, None),
RateRule(9500, None, 0.023),
RateRule(15000, None, 0.022),
RateRule(25000, None, 0.021)
]
def total_price( units, rate_table ):
# Base
total = rate_table[0].fixed
units_purchased_so_far = rate_table[0].qty_band
# Whole Price Bands
rule = 1
while units > units_purchased_so_far + rate_table[rule].qty_band:
total += rate_table[rule].qty_band * rate_table[rule].per_unit
units_purchased_so_far += rate_table[rule].qty_band
rule += 1
# Units within the top Price Band
if units > units_purchased_so_far:
total += (units - units_purchased_so_far) * rate_table[rule].per_unit
return total
Something like this:
Product
-------
[PK] ProductID
Price
-----
[PK] PriceID
[FK] ProductID
Price
QtyMin
QtyMax
So effectively a 1-many relationship between product and price. You could use a sentinel value for the maximum if you require a flat rate regardless of quantity.
SELECT
CASE is_fixed_price
WHEN 1
THEN unit_price / ?
ELSE
unit_price
END
FROM rate_structure
WHERE ? BETWEEN min_qty AND max_qty
Where ? is the quantity your customer wants to order. Syntax off the top of my head, for mysql 5.x. The side effect of this is potential rounding error accumulation.
What I wound up doing:
size units fixed per
1 500 303.000 0.000
1 10000 0.000 0.023
1 25000 0.000 0.022
1 50000 0.000 0.021
function calculate_price($size, $quantity) {
global $db;
$price = 0;
$count = 0;
// fetch rates from the database
// note: $size is already sanitised by the calling function
$query = "SELECT units, flat, per FROM rates WHERE size={$size} ORDER BY units ASC";
$result = $db->query($query);
// step through the rates
while($rate = $result->fetch_object()) {
// figure out how many of our units fall within this tier
$tier_count = max(0, min($quantity - $count, $rate->units - $count));
// calculate the price for this tier, including any flat rate
$tier_price = $rate->flat + ($rate->per * $tier_count);
// add tier price and count to the totals
$price += $tier_price;
$count += $tier_count;
// store the last, largest number of units rate for any leftovers outside our tiers
$last_rate = $rate;
}
// if some of our units fall outside our defined tiers, use the last tier's values for them
if($count < $quantity) {
$tier_count = $quantity - $count;
$tier_price = $last_rate->flat + ($last_rate->per * $tier_count);
$price += $tier_price;
$count += $tier_count;
}
return $price;
}