Mysql and between\in range condition - mysql

We have x2 columns min and max. Each can be null or integer. When we start search throw table we cannot use BETWEEN command... Question is, how to find in range with this conditions
value is greater then min (if it's not null)
and
value is less then max (if it's not null)
and
value is in range of min and max (if they BOTH not null)
value - our integer number. As you can see we cannot use BETWEEN command.

So NULL means no limit. You can still use BETWEEN:
select *
from mytable
where #value between coalesce(minvalue, #value) and coalesce(maxvalue, #value);
Or simply AND:
select *
from mytable
where #value >= coalesce(minvalue, #value)
and #value <= coalesce(maxvalue, #value);
Or the very basic AND and OR:
select *
from mytable
where (#value >= minvalue or minvalue is null)
and (#value <= maxvalue or maxvalue is null);

Use this:
WHERE col BETWEEN COALESCE(min, -2147483648) AND COALESCE(max, 2147483647)
According to your logic, if either the min or max be NULL, then the restriction should be ignored. In the above WHERE clause, if min be NULL then col will always be greater than the lower boundary, assuming that col is an integer. Similar logic applies to the max condition.
The large (and small) numbers you see represent the largest and smallest possible values for an integer in MySQL.

Without the option of using BETWEEN, I would recommend using a simple WHERE-AND clause.
If null values are not allowed, you should use the COALESCE function
http://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#function_coalesce
Returns the first non-NULL value in the list, or NULL if there are no non-NULL values.
SELECT *
FROM SCORES
WHERE score >= COALESCE(min_score, score)
AND score <= COALESCE(max_score, score)
Here is a sample fiddle I created
http://sqlfiddle.com/#!9/306947/2/0

My solution Yii2 AR like
$query
->joinWith(['vacancySalary'])
->andWhere([
'and',
'IF (vacancy_salary.min IS NULL, ' . $this->salaryMin . ', vacancy_salary.min) >= ' . $this->salaryMin,
'IF (vacancy_salary.max IS NULL, ' . $this->salaryMin . ', vacancy_salary.max) <= ' . $this->salaryMin
]);
Simple answer is use IF condition and proper values.
ADDED:
Another way to go
$query
->joinWith(['vacancySalary'])
->andWhere($this->salaryMin . ' BETWEEN IF(vacancy_salary.min IS NULL, 0, vacancy_salary.min) AND IF(vacancy_salary.max IS NULL, 0, vacancy_salary.max)');

Related

Adding a single Digit to a 4-digits long number in mysql

I'm trying to get a mysql script, that changes every 4-digit long number "into" a 5-digit long, by adding a "0" at the start of each number. This is, what I tried:
SELECT * FROM `customer_address_entity_text` WHERE CHAR_LENGTH(value) < 5;
SELECT CONCAT("0", CAST(value as CHAR(50)) AS value;
but it shows an error, that there is no field "value" found:
#1054 - Unknown field 'value' in field list (translated)
would be nice, if someone could help me with this.
(it also gives out this error, when I'm not trying to Cast 'value' to a CHAR)
tl;dr: I want 'value = "0" + value' in mysql
example:
'value = 1234; value = "0" + value; value = 01234' and that in mysql
Two issues:
There is a missing closing parenthesis for CONCAT(
Your second SELECT has no FROM clause, so indeed there is no value field there.
So move that CONCAT expression inside the first SELECT clause and balance the parentheses:
SELECT c.*, CONCAT("0", CAST(value as CHAR(50))) AS value
FROM `customer_address_entity_text` c
WHERE CHAR_LENGTH(value) < 5;
If your purpose is to pad all values with zeroes so they get 5 digits, so that it also transforms 1 to "00001" and 12 to "00012", then use LPAD:
SELECT c.*, LPAD(value, 5, "0") AS value
FROM `customer_address_entity_text` c;
To update the value field:
UPDATE `customer_address_entity_text`
SET value = LPAD(value, 5, "0");
Or, with your original concat version:
UPDATE `customer_address_entity_text`
SET value = CONCAT("0", CAST(value as CHAR(50)))
WHERE CHAR_LENGTH(value) < 5;

Why CONCAT does not insert text for the first time into mySQL table?

I am using UPDATE to insert simple text into a table where the field is MEDIUMTEXT (nullable field).
It is strange that it does not work when the field is null initially. If I manually enter at least a one character/space, then it's working.
I want to append the new text into existing text in the field.
UPDATE pen SET
PEN_STATUS = #PenStat,
PEN_STATUS_CHANGE_REASON = CONCAT(PEN_STATUS_CHANGE_REASON,'\n',ChangeDate,':',EmployeeID,':',ChangeReason)
WHERE PEN_ID = PenID;
Why is this?
CONCAT does not handle NULL values. As explained in the MySQL manual:
CONCAT() returns NULL if any argument is NULL.
You want to use COALESCE to handle that use case, like :
UPDATE pen SET
PEN_STATUS = #PenStat,
PEN_STATUS_CHANGE_REASON = CONCAT(
COALESCE(PEN_STATUS_CHANGE_REASON, ''),
'\n',
ChangeDate,
':',
EmployeeID,
':',
ChangeReason
)
WHERE PEN_ID = PenID;
Presumably, because something is NULL. Try using CONCAT_WS() instead:
UPDATE pen
SET PEN_STATUS = #PenStat,
PEN_STATUS_CHANGE_REASON = CONCAT_WS('\n',
PEN_STATUS_CHANGE_REASON,
CONCAT_WS(':', ChangeDate, EmployeeID, ChangeReason
)
)
WHERE PEN_ID = PenID;
CONCAT_WS() ignores NULL arguments. Plus, the separator only needs to be listed once.

MySql search integer range from number with dash

I have table in that I have one field with dash value. Like...
I need to search this with between condition.
For example if I have one value 25 then I need to search the records which include the value 25 like 20-31. In above image there are 6 records which include 25 value. So it should return 6 records.
Please help me in this query ? What would be the query for that ?
You can use MySQL's substring_index() function to easily get the data before and after the dash:
select substring_index(yourcolumn,'-',1) as `lower`, substring_index(yourcolumn,'-',-1) as `upper`
from yourtable
This way you can return the records where a certain value falls between the range:
select * from yourtable
where 25 between substring_index(yourcolumn,'-',1) + 0 and substring_index(yourcolumn,'-',-1) + 0
The + 0 forces MySQL to convert the result of substring_index() to a numeric value before the comparison.
You can use the following solution using SUBSTRING_INDEX:
SELECT *
FROM table_name
WHERE 25 >= CONVERT(SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '-', 1), '-', -1), UNSIGNED INTEGER)
AND 25 <= CONVERT(SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '-', 2), '-', -1), UNSIGNED INTEGER)
-- or
SELECT *
FROM table_name
WHERE 25 BETWEEN CONVERT(SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '-', 1), '-', -1), UNSIGNED INTEGER)
AND CONVERT(SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '-', 2), '-', -1), UNSIGNED INTEGER)
demo: http://sqlfiddle.com/#!9/4ac7b3/3/0
I recommend you to change your table design. I would split the column using the VARCHAR datatype to two columns using the INTEGER datatype. You can add two new columns with the the following ALTER TABLE commands:
ALTER TABLE table_name ADD colNameA INT;
ALTER TABLE table_name ADD colNameB INT;
To split the values of you current column and update the values to the new columns you can use the following UPDATE command:
UPDATE table_name SET
colNameA = CONVERT(SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '-', 1), '-', -1), UNSIGNED INTEGER),
colNameB = CONVERT(SUBSTRING_INDEX(SUBSTRING_INDEX(column_name, '-', 2), '-', -1), UNSIGNED INTEGER)
At the end you can remove the VARCHAR column using this ALTER TABLE command:
ALTER TABLE table_name DROP COLUMN col_name
Now you can use the following (simple) query to get the expected results:
SELECT *
FROM table_name
WHERE 25 >= colNameA AND 25 <= colNameB
-- or
SELECT *
FROM table_name
WHERE 25 BETWEEN colNameA AND colNameB
If you want to get values beween 35 and 39, you can use below query,
SELECT
*
FROM
yourtable
WHERE
35 && 39
BETWEEN SUBSTRING_INDEX(tablecolumn, '-', 1) + 0 AND
SUBSTRING_INDEX(tablecolumn, '-', - 1) + 0
I don't know how it possible with MySQL.
But using php it possible to check with range.
For e.g.
// First of all get all record from database.
$search = 10; // Your searching value.
// Loop all rows.
while($rows = mysqli_fetch_array($r)){
$explode = explode("-",$rows['dash']); // For get from-to value.
$range = isset($explode[0])&&isset($explode[1])?range($explode[0],($explode[1]-1)):array(); // For get range.
if(in_array($search,$range)){ // For check searching value is exist or not !
echo "Yes ! I get into ".$rows['dash']; // Do stuff.
}
}
Note: If 10-15 then it will check with 10,11,12,13,14.
According to me if you dont want to change the table structure then,
Just fetch the records as per your other condition, Then from that data check your amount between that field using foreach loop and explode. like
If you have $data as all data
foreach($data as $value){
$new_val=explode(',',$value['new_field']);
if(25 >= $new_val[0] && 25 <= $new_val[1]){
// here create new array
}
}

Mysql: CONCAT_WS('', col) vs (col is null OR col=0)

Is there any difference between:
CONCAT_WS('', column)=''
AND
column is null OR column=0 *(and optionally 'OR column="" ')*
Is one of them better/faster...?
SELECT my_fields FROM my_table
WHERE my_terms_clause='anything' AND CONCAT_WS( '', nb_check ) = ''
OR
SELECT my_fields FROM my_table
WHERE my_terms_clause='anything' AND (p.nb_check is null OR p.nb_check = 0)
I usually use "column is null OR column=0", but I just want "expert's tips".
You should definitely use:
where col is null or column = 0
First, the intention of the code is much clearer. Second, the function call prevents the optimizer from using an index. To be honest,the or also makes it hard for the optimizer to use an index.
Probably the most efficient way to write the query is using union all:
SELECT my_fields
FROM my_table p
WHERE my_terms_clause = 'anything' AND p.nb_check is null
UNION ALL
SELECT my_fields
FROM my_table p
WHERE my_terms_clause = 'anything' AND p.nb_check = 0;
This can take advantage of an index on my_table(my_terms_clause, nb_check).
If a column holds the value of 0, then concat_ws() with empty string as separator will return '0', not '', so the 2 expressions are not equal. If you need to check for null or 0, then better use that version, that actually checks this condition.

MySql: updating a column with the column's content plus something else

I'm don't have a lot of knowledge of MySql (or SQL in general) so sorry for the noobness.
I'm trying to update a bunch of String entries this way:
Lets say we have this:
commands.firm.pm.Stuff
Well I want to convert that into:
commands.firm.pm.print.Stuff
Meaning, Add the .print after pm, before "Stuff" (where Stuff can be any Alphanumerical String).
How would I do this with a MySql Query? I'm sure REGEXP has to be used, but I'm not sure how to go about it.
Thanks
Try something like this. It finds the last period and inserts your string there:
select insert(s, length(s) - instr(reverse(s), '.') + 1, 0, '.print')
from (
select 'commands.firm.pm.Stuff' as s
) a
To update:
update MyTable
set MyColumn = insert(MyColumn, length(MyColumn) - instr(reverse(MyColumn), '.') + 1, 0, '.print')
where MyColumn like 'commands.firm.pm.%'
Perhaps use a str_replace to replace commands.firm.pm to commands.firm.pm.print
$original_str = "commands.firm.pm.15hhkl15k0fak1";
str_replace("commands.firm.pm", "commands.firm.pm.print", $original_str);
should output: commands.firm.pm.print.15hhkl15k0fak1
then update your table with the new value...How to do it all in one query (get column value and do the update), I do not know. All I can think of is you getting the column value in one query, doing the replacement above, and then updating the column with the new value in a second query.
To update rows that end in '.Stuff' only:
UPDATE TableX
SET Column = CONCAT( LEFT( CHAR_LENGTH(Column) - CHAR_LENGTH('.Stuff') )
, '.print'
, '.Stuff'
)
WHERE Column LIKE '%.Stuff'
To update all rows - by appending .print just before the last dot .:
UPDATE TableX
SET Column = CONCAT( LEFT( CHAR_LENGTH(Column)
- CHAR_LENGTH(SUBSTRING_INDEX(Column, '.', -1))
)
, 'print.'
, SUBSTRING_INDEX(Column, '.', -1)
)
WHERE Column LIKE '%.%'