In my database i have 2 columns namely: Min and Max they are varchar and they have number values which i need to compare with. My query is something like this
SELECT * FROM Pricing_tbl WHERE Productid='10'
and i have this to compare the price
while ($selected_row = $stmt - > fetch(PDO::FETCH_ASSOC)) {
if ($marketval > $selected_row['Min'] && $marketval < $selected_row['Max']) {
$price[] = array('price_level' => $selected_row['price_level']);
}
}
This is ok if values of Min and Max are always numbers. But there is an instance where the value of Max is above. How to compare this situation?
Example values will be
Or should i just change the Max value. Any suggestion is appreciated
Assuming a maximum of above means all the way to infinity and you want to keep the data as is 1, you can just change the condition:
$marketval > $selected_row['Min'] && $marketval < $selected_row['Max']
into something like:
$marketval >= $selected_row['Min'] &&
($selected_row['Max'] == 'above' || $marketval < $selected_row['Max'])
In other words a value is considered under the maximum always if the maximum is the word above. Otherwise the actual (original) comparison decides.
PHP short-circuiting on logical operators will ensure that the second half of the or section will never be evaluated if the first half is true.
Note the change I made to the minimum comparison as well, the use of >= rather than >. As it was, a value like 300000.01 would not have been caught.
You may also want to coerce the numerics in the comparisons with $marketValue. If both $marketValue and $selectedRow['whatever'] are strings, I think they'll still use numeric comparison but I usually try to be explicit so I don't have to think about it :-)
1 There are probably better ways to do this, other than storing what's mostly numeric data as strings just because you want to be able to store the value 'above'.
Both methods below involve converting the column type to a numeric one which will allow better comparisons, including having the DBMS itself work it out rather than having to get all data and post-process it with PHP. Data manipulation is what a DBMS does best so it's generally better to leave that manipulation up to the DBMS for efficiency.
The first is to put a ridiculously large number in the maximum for the 'above' row so that your simple 'in between min and max' check will work. Using the DBMS itself to get the pricing level would be a simple:
select pricing_level
from pricing_tbl
where product_id = '10'
and $market_val >= minval
and $market_val < maxval
That will give you a single row containing the correct pricing level.
The second is to store NULL in that column instead of a string. A nullable numeric column will still work with a slight modification:
select pricing_level
from pricing_tbl
where product_id = '10'
and $market_val >= minval
and ($market_val < maxval or maxval is null)
In both those cases, you may want to translate the upper region (large number or null) to and from the word above when presenting or editing the table itself. This will make the process look the same even though the underlying data has changed.
i have 2 columns namely: Min and Max they are varchar
this is wrong. It should be int or at least decimal values
My query is something like this
this is wrong. Your query should include $marketval to do all the calcs on the database side.
here is an instance where the value of Max is above.
this is wrong. No numerical column should ever contain a string. you can store a big number there. or a NULL, but not a string.
Related
I'm using PHP 7 with Phalcon PHP and I'm trying to create a method to generate a booking number. Here is my current method :
public function generateNumber($company_code) {
// Build the prefix : COMPANY20190820
$prefix = $company_code . date('Ymd');
// It's like SELECT count(*) FROM bookings WHERE number LIKE 'COMPANY20190820%'
$counter = Bookings::count(array(
"number LIKE :number:",
"bind" => array('number' => $prefix.'%')
));
// Concat prefix with bookings counter with str_pad
// COMPANY20190820 + 005 (if 4 bookings in DB)
$booking_number = $prefix . str_pad($counter + 1, 3, 0, STR_PAD_LEFT);
// Return COMPANY20190820005
return $booking_number;
}
So I have a problem because sometime I have to delete 1 or multiple bookings so I can get :
COMPANY20190820001
COMPANY20190820002
COMPANY20190820005
COMPANY20190820006
COMPANY20190820007
And I need to add after the last in my DB so here 007, because I can get duplicated booking number if I count like that.
So how can I do to take the last and increment according the last booking number of the current day ?
You need to rethink what you want to do here as it will never work that way.
As I see it you have at least two options:
Use an auto-increment id and use that in combination with the prefix
Use a random fairly unique string (e.g. UUID4)
You should never manually try to get the current maximum id as that may and most likely will at some point result in race conditions and brittle code as a result of that.
So I found a solution, maybe there is a better way to do that but my function works now:
public function generateNumber($company_code) {
// Build the prefix : COMPANY20190820
$prefix = $company_code . date('Ymd');
// Get the last booking with the today prefix
// e.g : COMPANY20190820005
$last_booking = Bookings::maximum(array(
"column" => "number",
"number LIKE :number:",
"bind" => array('number' => $prefix.'%')
));
// Get the last number by removing the prefix (e.g 005)
$last_number = str_replace($prefix, "", $last_booking);
// trim left 0 if exist to get only the current number
// cast to in to increment my counter (e.g 5 + 1 = 6)
$counter = intval(ltrim($last_number, "0")) + 1;
// Concat prefix + counter with pad 006
$booking_number = $prefix . str_pad($counter, 3, 0, STR_PAD_LEFT);
// Return COMPANY20190820006
return $booking_number;
}
I reckon that the use case you describe does not justify the hassle of writing a custom sequence generator in PHP. Additionally, in a scenario where booking deletion is expected to happen, ID reusing feels more a bug than a feature, so your system should store a permanent counter to avoid reusing, making it less simple. Don't take me wrong, it can be done and it isn't rocket science, but it's time and energy you don't need to spend.
Your database engine surely has a native tool to generate autoincremented primary keys, with varying names and implementations (SQL Server has identity, Oracle has sequences and identity, MySQL has auto_increment...). Use that instead.
Keep internal data and user display separated. More specifically, don't use the latter to regenerate the former. Your COMPANY20190820007 example is trivial to compose from individual fields, either in PHP:
$booking_number = sprintf('%s%s%03d',
$company_code,
$booking_date->format('Ymd'),
$booking_id
);
... or in SQL:
-- This is MySQL dialect, other engines use their own variations
SELECT CONCAT(company_code, DATE_FORMAT(booking_date, '%Y%m%d'), LPAD(booking_id, 3, '0')) AS booking_number
FROM ...
You can (and probably should) save the resulting booking_number, but you cannot use it as source for further calculations. It's exactly the same case as dates: don't need to store dates in plain English in order to eventually display them to the end-user and you definitively don't want to parse English dates back to actual dates in order to do anything else beyond printing.
You also mention the possibility of generating long pure-digit identifiers, as Bookings.com does. There're many ways to do it and we can't know which one they use, but you may want to considering generating a numeric hash out of your auto-incremented PK via integer obfuscation.
you could split your database field in two parts, so you hold the prefix and the counter separately.
then, you simply select the highest counter for your desired prefix and increment that one.
if you can't change the table structure, you could alternatively order by the id descendingly and select the first. then you can extract its counter manually. keep in mind you should pad the numbers then, or you get #9 even if #10 exists.
if padding is not an option, you can direct the database to replace your prefix. that way, you can cast the remaining string to a number and let the database sort - this will cost some performance, though, so keep the amount of records low.
As mentioned in https://www.sqlite.org/datatype3.html :
INTEGER: The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8
bytes depending on the magnitude of the value.
The problem is that the statement below gives the desired result as the bound values are comparatively smaller (e.g, $roll_no = 1111111111), however, the execution of statement fetches no result when the bound value is bigger(e.g, $roll_no =3333333333) whereas the SQLite table already holds record with that bigger value.
Is it because the parameter value is truncated or any other reason? What is to be done to get rid of it?
$stmt1 = $db->prepare("select sname,reg_no from student where roll_no=:roll_no");
$stmt1->bindParam(':roll_no', $roll_no, SQLITE3_INTEGER);
See the PDO driver of PHP:
https://github.com/php/php-src/search?q=SQLITE_INTEGER&unscoped_q=SQLITE_INTEGER
#if LONG_MAX <= 2147483647
if (val > ZEND_LONG_MAX || val < ZEND_LONG_MIN) {
ZVAL_STRINGL(data, (char *)sqlite3_column_text(stmt, column), sqlite3_column_bytes(stmt, column));
It supports returning larger integers, but only as strings.
There's no way to have 3333333333 as native integer on PHPs end (32-bit versions). Such it would become a float before it even reaches SQLite.
What you should do is not trying to bind it as integer. Use a string. SQL does type casting of its own. You could likely keep the column as INTEGER on SQLites end even. It's only PHP you have to work around. Or you know, do the overdue upgrade.
I know i can reduce the column value in laravel using this query
DB::table('users')->decrement('votes', 5);
But i want to restrict the value from being become negative value.
Is there anyway to do this with laravel?
You'll need to use raw queries for that.
The following code should do the trick for all DB engines that support GREATEST function:
DB::table('users')->update(['votes' => DB::raw('GREATEST(votes - 5, 0)')]);
It will decrement votes column in users table by 5, but won't go below zero - that's what GREATEST function is used for.
If you really want to use decrement in this case (could be handy if you're accessing it through a relationship for example), you could do something like:
$thing->decrement('votes', $thing->votes - $amount <= 0 ? $thing->votes : $amount);
Where $amount is 5 in your case. It's pretty ugly in my opinion. Worth noting if you already have $thing (say via a relationship) it won't trigger an additional query when accessing votes.
If you are only incrementing by 1, a simply if wrapped around is cleaner:
if($thing->votes > 0) {
$thing->decrement('votes');
}
I have a check I'm trying to run for whether the number of rows in a table is less than N+8. The reason it's very messy is because I have to use the WordPress API and get_results function (https://developer.wordpress.org/reference/classes/wpdb/get_results/) to get everything from MySQL land to PHP land.
What I have is
$stillSomeLeft = ($wpdb->get_results($wpdb->prepare("SELECT COUNT(*) FROM $thisTable < (%d + 8)", $_POST['numItemsLoaded']))) === true;
and $stillSomeLeft is always evaluating to false. Of course, since get_results returns an object corresponding to the result of the query, I have no way of knowing exactly what that object will be, but since the query would return true/false in MySQL land, I assume the PHP equivalent "object" will be the same.
Gotta love loosely-typed languages ...
Your problem is the select statement
You only need
SELECT COUNT(*) FROM $thisTable
With that value you compare to N + 8 in your PHP.
I've a database with lon and lat geo location data.
Both are saved as float / decimal attribute in the mysql table.
Now I want to compare this stuff like:
(u.user_location_lat <= ".$lat_max." AND u.user_location_lat >= ".$lat_min.") AND
(u.user_location_long <= ".$long_max." AND u.user_location_long >=".$long_min.")
But it does not show any result (and it should!) - but also no error.
How to EASILY solve this (I actually don't want to work with spatial indexes - at least I do not understand how to do)?
Thanks.
I recommend you verify what that statement looks like, after you do the variable substitution.
That is, echo or vardump the contents of the variable containing the SQL text, before you prepare/execute the SQL statement.
There doesn't appear to be anything wrong with the form of the predicates. (These could be written using equivalent BETWEEN comparators, but it's not a problem what you have written.)
It's possible you have the min and max values swapped, or have the longitude and latitude swapped. If that's not the issue, then I suspect that the variables being substituted may be represented in scientific notation, rather than as decimal values.
e.g. the SQL text gets generated
... u.lat <= 1.241E+2 ...
rather than
... u.lat <= 124.1 ...
In the former case, MySQL is going to evaluate that literal as decimal value of 1.241.
(There's a corner case issue when the bounding box crosses the +180/-180 boundary, but I don't see that's likely a problem for most of your values, that's going to be an exceptional case, which you would probably need to setup special test case to actually have happen.)
In order to debug this, you need the actual SQL text that's being sent to the database to be prepared/executed.
(There's not enough information in your question to identify the actual problem.)
Q: How do I get the actual SQL text?
A: Construct your SQL statement as a string into a variable; and then var_dump that variable for debugging:
$sqltext = "SELECT ... WHERE u.lat <= ". $lat_max . " AND u.lat ... ";
var_dump($sqltext);
$stmt = mysqli_prepare($db, $sqltext);