I'm completely fine with PHP IF() and ELSEIF(). However today I started to work with MySQL commands, I have a long MySQL query and I can't understand how I modify it.
Here is the code:
$db->colums[] = "
IF(
(SELECT
IF(p.delivery = '1',IF(distance < deliverymilesto1, deliverysurcharge1,
IF (distance < deliverymilesto2, deliverysurcharge2,
IF (distance < deliverymilesto3, deliverysurcharge3,
IF (distance < deliverymilesto4, deliverysurcharge4,
IF(distance < deliverymilesto5,deliverysurcharge5,0)
)
)
)
), 0)
FROM products prod WHERE prod.id_product = p.id_product
) > 0,
(p.price + (SELECT
IF(p.delivery = '1',IF(distance < deliverymilesto1, deliverysurcharge1,
IF (distance < deliverymilesto2, deliverysurcharge2,
IF (distance < deliverymilesto3, deliverysurcharge3,
IF (distance < deliverymilesto4, deliverysurcharge4,
IF(distance < deliverymilesto5,deliverysurcharge5,0)
)
)
)
), 0)
FROM products prod WHERE prod.id_product = p.id_product
)),
(p.price)
)as pricedelivery";
What I need to do is create an ELSE statement if p.delivery equals '2', and add in the following code:
IF(p.delivery = '2', IF(find_in_set('".$outcoder."', deliveryoutcode1), deliveryoutcharge1,
IF(find_in_set('".$outcoder."', deliveryoutcode2), deliveryoutcharge1, deliveryoutcharge2, 0)), 0)
in PHP this is simple as it would be:
if($delivery = '1') {
...code here
} elseif($delivery = '2') {
...otherwise
}
But I just can't understand the logic of how to change my statment. Maybe my brain is tired because I'be been learning about normalisation today. Can anyone help me here, thanks.
EDIT: I have spent the entire morning cleaning up the MySQL, result of which is above, based on what I have so far learnt about MySQL.
However, I would now like to be able to rewrite this using case, based on the useful comments I've received. I have 5 such MySQL queries on my page and I wonder if someone can be kind enough to rewrite the above in caseformat, then I can use this to rewrite the other queries? Thank you.
SELECT
price +
case delivery
when '1' then
case
when distance < deliverymilesto1 then deliverysurcharge1
when distance < deliverymilesto2 then deliverysurcharge2
when distance < deliverymilesto3 then deliverysurcharge3
when distance < deliverymilesto4 then deliverysurcharge4
when distance < deliverymilesto5 then deliverysurcharge5
else 0
end
when '2' then
case
when distance < deliverymilesto1 then deliveryoutcharge1
when distance < deliverymilesto2 then deliveryoutcharge2
when distance < deliverymilesto3 then deliveryoutcharge3
when distance < deliverymilesto4 then deliveryoutcharge4
when distance < deliverymilesto5 then deliveryoutcharge5
else 0
end
else 0
end
FROM products
IF() as a function is not the same as the branching you're used to. The first argument is a condition, the second argument is what is returned when the condition evaluates to true, the third is the opposite case when it's false.
Your code above just uses a lot of nesting in the third argument (the else branch) to accomplish what you'd normally do with IF...ELSE... or equivalently with the CASE expression.
You'll have to incorporate that into the rest of your query since you've left some of it out.
Related
I am learning PHP. I decided to adapt a solution to the famous FizzBuzz problem from Javascript to PHP, just to see how JS and PHP compare.
For those who forgot what the FizzBuzz problem is :
Write a short program that prints each number from 1 to 100 on a new
line. For each multiple of 3, print "Fizz" instead of the number.
For each multiple of 5, print "Buzz" instead of the number. For
numbers which are multiples of both 3 and 5, print "FizzBuzz" instead
of the number.
I am using a lot of short-circuit evaluations in the following examples.
Here's the clever solution (not written by me) that I adapted to PHP:
Works great!
Then, for the sake of challenge, I decided to try and rewrite it in one single line.
Here's how I started:
Seems like a good start. My condition in the while loop: if $i is not set set, then I set it to zero, but if it is already set, I skip the first part and check if it's inferior to 100.
As you can see on this picture, my loop works.
Since my goal is to write it in one line, I need to increment $i inside my conditional statement, just as with my previous multi-line solution. But when I write $i++ < 100 as before, something weird happens. My loop only runs once and stops.
Very weird indeed.
Even weirder, if I use both increments (one in the condition and one in the loop), the loop then works fine, and applies both increments.
I'm puzzled. Is it because there is a safeguard for infinite loops somewhere? With all those short-circuit evaluations, even my IDE PHP Storm says 'variable $i is probably undefined'. But it gets defined, and my loop works fine under certain conditions. What did I miss ?
EDIT:
Here's the code:
Multi-line working FizzBuzz:
$i = 0;
while ($i++ < 100) {
$msg = '';
($i % 3) || ($msg = $msg . 'Fizz');
($i % 5) || ($msg = $msg . 'Buzz');
echo $msg . "\n";
}
Weird loop iteration (you can delete the increment either in the loop or in the condition, or leave both to see the different effects):
while ( (!isset($i) && ($i=0 || true) ) || ($i++ < 100) ) {
echo $i . "\n";
$i = $i +1;
}
if $i is not set set, then I set it to zero
This is not quite right. Here's what's going on.
Your statement ($i=0 || true) sets $i to TRUE.
PHP's type juggling with print "1" for "$i". Consider $v = TRUE; echo "$v"; to see this in effect.
On the next iteration, your second condition is evaluated as TRUE < 100 which evaluates to FALSE, thereby exiting the loop.
So, in order to fix your problem, simply drop the || true and be on your merry way.
$i=0 || true results in $i being true.
true++ doesn’t actually do anything.
true < 100 is false.
echo true outputs 1.
An explicit true + 1 creates 2.
OMG, Yes! I thought I needed the || true part because ( !isset($i) && ( $i = 0 ) ) will either do both sides of the &&, or neither. I never thought that ( $i = 0 ) would evaluate to "true". But it looks like it does :)
OBSOLETE COMMENT: I found the origin of the quirk. Not sure why it happens though.
If I rewrite $i++ as ($i = $i + 1), it works fine.
while ( (!isset($i) && ($i=0 || true) ) || (($i = $i + 1) < 100) ) {
echo $i . "\n";
}
I am running a for loop 10 times in order to populate data in a data table. In doing this, I wanted to use number_format in order to format the numbers. However, when I apply the number_format the For loop for some reason runs one additional time.
It works just fine when I exclude the number_format. Can anyone explain why this happens?
<?php
foreach($data['data'] as $result) {
For ($n = 0; $n <= 10; $n++){
echo "<td>";
echo number_format($result[$n], 0, ".", ",");
echo "</td>";
}
}
?>
TL;DR: Your loop will always run an additional time. Assuming that there are no errors in your number_format function call, all you have to do to get this to run 10 times is change your code to for($n = 0; $n < 10; n++). Note the use of < and not <=.
For loops are really just syntactical sugar for while loops. The statement for(initial_statement; bound_condition; loop_statement) { code; } is equivalent to
initial_statement;
while(bound_condition) {
code;
loop_statement;
}
Which, functionally, is equivalent to
initial_statement;
while(true) {
code;
loop_statement;
if(!bound_condition) break;
}
This means that if you want a loop to run, say, 2 times, and you write for($i = 0; $i <= 2; $i++) your code will loop as follows:
$i = 0
i++; (i now equals 1)
i <= 2 (condition is true, so continue)
$i = 1
i++; (i now equals 2)
i <= 2 (condition is true, so continue)
$i = 2
i++; (i now equals 3)
i <= 2 (condition is FALSE, so break)
Using the <= operator when your control variable starts at 0 causes an extra iteration to occur, since there are three integer values of i such that 0 <= i <= 2 (0, 1, and 2). To ensure that there are only two iterations, use the < operator, and now the loop will only be executed for values in the domain 0 <= i < 2 (0 and 1).
If you are still bent on using the <= operator and are fine with a non-zero-based iteration count, then you can simply change the initial value of i to 1 to offset the error.
By the way your code is written, I assume that you wish for your inner loop to run 10 times, not 11. This would explain why you are getting an extra iteration, and the issue is quite unrelated to the use of number_format. If you are only getting 10 iterations when you don't use that function, you might want to make sure that the statement 1 == 1 evaluates to true in your PHP interpreter.
Additionally, as a code styling issue, I would recommend using consistent case in your statements; you write foreach (lowercase) but also use For (uppercase). The convention is to use lowercase for both.
I have no clue why you would be only getting 10 iterations without number_format. You might be counting incorrectly? Try changing it to < and see if that resolves your issue.
I just coded a solution for the TapeEquilibrium problem in php. The thing is i`m getting some wrong answers for the automatic code revitions provided by Codility.
The link to the finished test is
https://codility.com/demo/results/demo2G35FU-EJQ/.
Any idea whats the two elements wrong answer error?
Is it necessary to type (typecast) the variables in php for this test? i mean, how do u tell the compiler X variable is double or int in php? Thx a lot!
Two elements test case is probably {-1000, 1000}. This array can be splited only in one place and the distance is |-1000-1000| = 2000. Your program for this input returns 0, because in the second iteration of the for loop $arr_h is 0 and $sum_total is also 0.
Simply change condition in loop for ($i = $count_-1; $i > 0 ; $i--) to get 100%.
"Two elements" test case is {-1000, 1000}, additional testing (if (N == 2)) can be performed to solve this issue.
"Small elements" test case should be passed after setting a starting value to tmp variable (take a look at my example) prior to entering in the second loop. My example is on C, but it can be easily implementing in other languages:
int TapeEquilibrium(int A[], int N) {
long long sum = 0;
int i = 0, j = 0;
int min = 1000;
if (N == 2) {
return abs(A[0] - A[1]);
}
for(i = 0; i < N; i++)
{
sum += A[i];
}
long long left = 0;
long long right = sum;
int tmp = abs(left - right);
for(j = 1; j < N; j++)
{
right -= A[j - 1];
left += A[j - 1];
tmp = abs(left - right);
if (min > tmp)
{
min = tmp;
}
}
return min;
}
Apologies - I'm not even sure I'm using the right terminology here.
I have a series of confidential documents and I'm creating a bitmask (?) to represent which of those documents a given user can view. (1 represents Doc1, 2 represents Doc2, 4 represents Doc3, 8 represents Doc4, 16 represents Doc5, etc.)
So, if a user can view docs 1, 2, and 5, the bitmask will be 19.
Where I'm really stumped, though, is how to reverse calculate the individual values "stored" in the bitmask. Currently, I'm using
if($docs2view==1) {
$nextdoc = 1;
}
if($docs2view==2) {
$nextdoc = 2;
}
. . .
if($docs2view==127) {
$nextdoc = 1;
}
which is REALLY tedious and obviously very inefficient. Can someone point me toward the right way to do this?
You need a bitwise-and:
if( $docs2view & 1 ) {...}
if( $docs2view & 2 ) {...}
if( $docs2view & 4 ) {...}
if( $docs2view & 8 ) {...}
if( $docs2view & 16 ) {...}
Here, I'm testing individual bits. If the bit is set, the condition will be non-zero (hence will evaluate to 'true').
You could possibly avoid a lot of code repetition by putting this in a loop and using the bit-shift operators (>> and <<).
You said:
Thank you, paddy! But I only need the lowest value that evaluates
true. How do I tell the loop to stop as soon as it finds that?
You can either convert those statements to elseif so that only the first becomes true, or you can do this (assuming you only check the first 8 bits):
$nextdoc = 0;
for( $i = 0; $i < 8; $i++ ) {
if( $docs2view & (1<<$i) ) {
$nextdoc = $i + 1;
break;
}
}
I've got 108,002 items that I am keeping track of.
55011 of them are going to be given the name "item1"
32531 of them are going to be given the name "item2"
8060 of them are going to be given the name "item3"
12400 of them are going to be given the name "item4"
I'm using a for loop to do this.
for ($i = 1; $i <= 108002; $i++) {
if($i <= 55011){
$item = "item1";
}else if($i > 55011 && $i < 87542 ){
$item = "item2";
}else if($i > 87542 && $i < 95602 ){
$item = "item3";
}else if($i > 95602 && $i <= 108002 ){
$item = "item4";
}
}
And then I insert each one into a MySQL database.
But the trick is I want them to be inserted in a random order so I don't have the first 55,011 items all with the same name etc.
Any ideas how I can do this but still allow for the exact amount of each item?
Put them into an array and shuffle() it.
I hope you're doing this only once, or at least infrequently, in which case you'll probably just want to make a huge array in php and shuffle() it as Amber said.
If that array would be too big/slow you could do something like this (untested pseudo-code)
left = [0, 55011, 32531, 8060, 12400]
for i in 108001..0
r = rnd_zero_to_one_less_than(i)
for j in 1..4
if r < left[j]
left[j] -= 1
insert("item"+j)
break
r -= left[j]
That make sense?
Edit: instead of having an array with all the values, you just have "left" which says how many there are left of the 1s, 2s, 3s and 4s. Instead of removing an element at random from the huge array, you can just decrement one of the counts in "left".
Edit: added last two lines of code (how'd I forget those?)