Comparing number in formatted string in MySQL? - mysql

I have a PolicyNo column in my table in MySQL with a format like this:
XXXX-000000
A four capital-case characters followed by a dash and a six digit number.
The six digit number is incremental, adding 1 for the next row, and the the four characters is always the same for all rows. The PolicyNo column is unique with a type of varchar(11).
If ordered, it will look like this:
XXXX-000001
XXXX-000002
XXXX-000003
...
Now I want to get all PolicyNo whose number is greater than a specified number.
For example: Retrieve all PolicyNo greater than 'XXXX-000100':
XXXX-000101
XXXX-000102
XXXX-000103
...
I test this query and it works fine, but I just didn't know if it is really safe to do such:
SELECT 'XXXX-000099' > 'XXXX-000098'
, 'XXXX-000099' > 'XXXX-000100'
, 'XXXX-000099' > 'XXXX-000101'
Result:
+-------------------------------+-------------------------------+-------------------------------+
| 'XXXX-000099' > 'XXXX-000098' | 'XXXX-000099' > 'XXXX-000100' | 'XXXX-000099' > 'XXXX-000101' |
+-------------------------------+-------------------------------+-------------------------------+
| 1 | 0 | 0 |
+-------------------------------+-------------------------------+-------------------------------+
Is there any other way to do this or is it already OK to use this?

Because your numbers are zero padded, as long as the four letter prefix is the same and always the same length, then this should work as MySQL will do a lexicographical comparison.
Note that one less 0 in the padding will cause this to fail:
SET #policy1 = 'XXXX-00099';
SET #policy2 = 'XXXX-000598';
SELECT #policy1, #policy2, #policy1 > #policy2 AS comparison;
=========================================
> 'XXXX-00099', 'XXXX-000598', 1
If you need to truly compare the numbers at the end, you will need to parse them out and cast them:
SET #policy1 = 'XXXX-00099';
SET #policy2 = 'XXXX-000598';
SELECT #policy1, #policy2,
CONVERT(SUBSTRING(#policy2, INSTR(#policy2, '-')+1), UNSIGNED) >
CONVERT(SUBSTRING(#policy2, INSTR(#policy2, '-')+1), UNSIGNED) AS comparison;
=========================================
> 'XXXX-00099', 'XXXX-000598', 0

You can also use SUBSTRING function provided by MySQL, like the following query.
SELECT count(*) FROM Table1 where substring(policyNo,6)>YOUR_RANGE;
here 6 is passed as the 6 digit number start from 6th position And if you do want to pass initial 4 charecter as well then you can use following query. Here second where clause will take intial 4 letters from the policyNo.
SELECT count(*) FROM Table1 where substring(policyNo,6)>YOUR_RANGE AND substring(policyNo,1,4) = 'ABCD'

Related

Return rows matching one condition and if there aren't any then another in MYSQL

I have the following table as an example:
numbers type
--------------
1 1
5 2
6 1
8 2
9 3
14 2
3 1
From this table I would like to select the closest number that is less or equal to 5 AND of type 1 and if there is no such row matching, then (and only then) I would like to return the first closest number larger than 5 of type 2
I can solve this by running two queries:
SELECT number FROM numbers WHERE number <= 5 AND type = 1 ORDER BY number LIMIT 1
and if above query returns 0 results, I simply run the second query:
SELECT number FROM numbers WHERE number > 5 AND type = 2 ORDER BY number LIMIT 1
But is it possible, to achieve the same result by only using one query?
I was thinking something like
SELECT number FROM numbers WHERE (number <= 5 AND type = 1) OR (number > 5 AND type = 2) ORDER BY number LIMIT 1
But that would only work, if mysql first checks the first conditional in the parentheses against all rows and if it finds a match, it returns it, and if not, then it checks all rows against the second parenthesed conditional. It will not work, if it checks each row against both parentheses and only then moves to the next row, which is how I suspect it works.
This query will do what you want. It selects all numbers that match your two query constraints, and orders the results first by type (so that if there is a result for type 1 it will appear first) and then by either -number or number dependent on type (so that numbers <= 5 sort in descending order but numbers > 5 sort in ascending order):
SELECT number
FROM numbers
WHERE ( number <= 5 AND type = 1 )
OR ( number > 5 AND type = 2 )
ORDER BY type, CASE WHEN type = 1 THEN -number ELSE number END
LIMIT 1
Output:
3
Demo on dbfiddle
Combine the two, and you always prefer type 1 over type 2, hence the ORDER BY and LIMIT. The ABS means whichever is first by type, is the closes to the number 5.
SELECT number, type
FROM numbers
WHERE (number <=5 AND type=1) OR
(number > 5 AND type=2)
ORDER BY type ASC, ABS(number-5) ASC
LIMIT 1

MySQL strange behavior when comparing comma-separated string with number

I am experiencing some weird behavior with MySQL. Basically I have a table like this:
ID string
1 14
2 10,14,25
Why does this query pull id 2?
SELECT * FROM exampletable where string = 10
Surely it should be looking for an exact match, because this only pulls id 1:
SELECT * FROM exampletable where string = 14
I am aware of FIND_IN_SET, I just find it odd that the first query even pulls anything. Its behaving like this query:
SELECT * FROM exampletable where string LIKE '10%'
When you compare a numeric and a string value, MySQL will attempt to convert the string to number and match. Number like strings are also parsed. This we have:
SELECT '10,14,25' = 1 -- 0
SELECT '10,14,25' = 10 -- 1
SELECT 'FOOBAR' = 1 -- 0
SELECT 'FOOBAR' = 0 -- 1
SELECT '123.456' = 123 -- 0
SELECT '123.456FOOBAR' = 123.456 -- 1
The behavior is documented here (in your example it is the last rule):
...
If one of the arguments is a decimal value, comparison depends on the
other argument. The arguments are compared as decimal values if the
other argument is a decimal or integer value, or as floating-point
values if the other argument is a floating-point value.
In all other cases, the arguments are compared as floating-point
(real) numbers.

MySQL match area code only when given the full number

I have a database that lists a few area codes, area code + office codes and some whole numbers and a action. I want it to return a result by the digits given but I am not sure how to accomplish it. I have some MySQL knowledge but its not very deep.
Here is a example:
match | action
_____________________
234 | goto 1
333743 | goto 2
8005551212| goto 3
234843 | goto 4
I need to query the database with a full 10 digit number -
query 8005551212 gives "goto 3"
query 2345551212 gives "goto 1"
query 3337431212 gives "goto 2"
query 2348431212 gives "goto 4"
This would be similar to the LIKE selection, but I need to match against the database value instead of the query value. Matching the full number is easy,
SELECT * FROM database WHERE `match` = 8005551212;
First the number to query will always be 10 digits, so I am not sure how to format the SELECT statement to differentiate the match of 234XXXXXXX and 234843XXXX, as I can only have one match return. Basically if it does not match the 10 digits, then it checks 6 digits, then it will check the 3 digits.
I hope this makes sense, I do not have any other way to format the number and it has to be accomplished with just a single SQL query and return over a ODCB connection in Asterisk.
Try this
SELECT match, action FROM mytable WHERE '8005551212' like concat(match,'%')
The issue is that you will get two rows in one case .. given your data..
SELECT action
FROM mytable
WHERE '8005551212' like concat(match,'%')
order by length(match) desc limit 1
That should get the row that had the most digits matched..
try this:
SELECT * FROM (
SELECT 3 AS score,r.* FROM mytable r WHERE match LIKE CONCAT(SUBSTRING('1234567890',1,3),'%')
UNION ALL
SELECT 6 AS score,r.* FROM mytable r WHERE match LIKE CONCAT(SUBSTRING('1234567890',1,6),'%')
UNION ALL
SELECT 10 AS score,r.* FROM mytable r WHERE match LIKE CONCAT(SUBSTRING('1234567890',1,10),'%')
) AS tmp
ORDER BY score DESC
LIMIT 1;
What ended up working -
SELECT `function`,`destination`
FROM reroute
WHERE `group` = '${ARG2}'
AND `name` = 0
AND '${ARG1}' LIKE concat(`match`,'%')
ORDER BY length(`match`) DESC LIMIT 1

Mysql select to get only part of a row from a column

I have a table with a column that contains a string of numbers and I only want to return the last couple of digits.
For example:
column1 | column2
_________________
Blah | 1231357
I need a select that will return the last couple of digits from the second column.
Use the RIGHT function:
SELECT RIGHT(column2, 3) AS LastDigits FROM TableName
Change 3 to the number of digits you want.
A modulus operator will take only the last two digits.
SELECT MOD(column2, 100) FROM mytable
Change 100 to 1000 to get three digits, etc.

MySQL prefix SET field values with custom string

I have a MySQL 5 database table field media like set('audio','video','photo')
What i need to do, is in single SELECT statement prefix it's values with some custom string and space after it, if any of the values are present. For example:
audio,video becomes mediaaudio mediavideo
photo becomes mediaphoto
The specifics of the data does not require an external relationship table to be made for corresponding values, so set is sufficient for the current task. I need to prefix them to uniquely identify them later in search results.
Real example:
id media
1 audio,video
2 audio
3 video
4 photo,video
5
Expected result:
id media
1 mediaaudio mediavideo
2 mediaaudio
3 mediavideo
4 mediaphoto mediavideo
5
Here's one way to do it with INSTR, CONCAT, REPLACE and LENGTH:
SELECT ID,
CASE WHEN INSTR(YourField, ',') > 0
THEN CONCAT('media', REPLACE(YourField, ',', ' media'))
WHEN LENGTH(YourField) > 0
THEN CONCAT('media', YourField)
ELSE ''
END media
FROM YourTable
And the Fiddle.
Good luck.