MySQL: compare a mixed field containing letters and numbers - mysql

I have a field in the mysql database that contains data like the following:
Q16
Q32
L16
Q4
L32
L64
Q64
Q8
L1
L4
Q1
And so forth. What I'm trying to do is pull out, let's say, all the values that start with Q which is easy:
field_name LIKE 'Q%'
But then I want to filter let's say all the values that have a number higher than 32. As a result I'm supposed to get only 'Q64', however, I also get Q4, Q8 and so for as I'm comparing them as strings so only 3 and the respective digit are compared and the numbers are in general taken as single digits, not as integers.
As this makes perfect sense, I'm struggling to find a solution on how to perform this operation without pulling all the data out of the database, stripping out the Qs and parsing it all to integers.
I did play around with the CAST operator, however, it only works if the value is stored as string AND it contains only digits. The parsing fails if there's another character in there..

Extract the number from the string and cast it to a number with *1 or cast
select * from your_table
where substring(field_name, 1, 1) = 'Q'
and substring(field_name, 2) * 1 > 32

Related

How to output calculation to 4 decimal places

I am in SQL Workbench and want my output to include 4 decimal places. I have tried different combinations of casting 'sessions', 'transactions', and 'cvr' as DECIMAL as well as using ROUND, and I can't seem to get the output I'm looking for. 'sessions' and 'transactions' are both in NUMERIC(19,2) format.
Ideally, I want to stay away from casting to float to avoid losing precision.
select cast((transactions/sessions) as decimal(10,5)) as cvr
from(select name
,sum(cast(sessions as decimal(10,5))) as sessions
,sum(cast(transactions as decimal(10,5))) as transactions
How to get output with 4 decimal places
SELECT FORMAT(123.456789, 4) as col
123.4567
SELECT FORMAT(123.456, 4) as col
123.4560

How do I Query for used BETWEEN Operater for text searches in MySql database?

I have a SQL Table in that i use BETWEEN Operater.
The BETWEEN Operater selects values within range. The values can be numbers, text , dates.
stu_id name city pin
1 Raj Ranchi 123456
2 sonu Delhi 652345
3 ANU KOLKATA 879845
4 K.K's Company Delhi 345546
5 J.K's Company Delhi 123456
I have a query like this:-
SELECT * FROM student WHERE stu_id BETWEEN 2 AND 4 //including 2 & 4
SELECT * FROM `student` WHERE name between 'A' and 'K' //including A & not K
Here My Question is why not including K.
but I want K also in searches.
Don't use between -- until you really understand it. That is just general advice. BETWEEN is inclusive, so your second query is equivalent to:
WHERE name >= 'A' AND
name <= 'K'
Because of the equality, 'K' is included in the result set. However, names longer than one character and starting with 'K' are not -- "Ka" for instance.
Instead, be explicit:
WHERE name >= 'A' AND
name < 'L'
Of course, BETWEEN can be useful. However, it is useful for discrete values, such as integers. It is a bit dangerous with numbers with decimals, strings, and date/time values. That is why I encourage you to express the logic as inequalities.
In supplement to gordon's answer, one way to get what you're expecting is to turn your name into a discrete set of values:
SELECT * FROM `student` WHERE LEFT(name, 1) between 'A' and 'K'
You need to appreciate that K.K's Company is alphabetically AFTER the letter K on its own so it is not BETWEEN, in the same way that 4.1 is not BETWEEN 2 and 4
By stripping it down to just a single character from the start of the string it will work like you expect, but take cautionary note, you should always avoid running functions on values in tables, because if you had a million names, thats a million strings that mysql has to strip out to just the first letter and it might no longer be able to use an index on name, battering the performance.
Instead, you could :
SELECT * FROM `student` WHERE name >= 'A' and name < 'L'
which is more likely to permit the use of an index as you aren't manipulating the stored values before comparing them
This works because it asks for everything up to but not including L.. Which includes all of your names starting with K, even kzzzzzzzz. Numerically it is equivalent to saying number >= 2 and number < 5 which gives you all the numbers starting with 2, 3 or 4 (like the 4.1 from before) but not the 5
Remember that BETWEEN is inclusive at both ends. Always revert to a pattern of a >= b and a < c, a >= c and a < d when you want to specify ranges that capture all possible values
Compare in lexicographical order, 'K.K's Company' > 'K'
We should convert the string to integer. You can try that mysql script with CAST and SUBSTRING. I've updated your script here. It will include the last record as well.
SELECT * FROM student WHERE name CAST(SUBSTRING(username FROM 1) AS UNSIGNED)
BETWEEN 'A' AND 'K';
The script will work. Hope it will helps to you.
Here I've attached my test sample.

select int column and compare it with Json array column

this is row in option column in table oc_cart
20,228,27,229
why no result found when value is 228 but result found when value is 20 like below :
select 1 from dual
where 228 in (select option as option from oc_cart)
and result found when I change value to 20 like
select 1 from dual
where 20 in (select option as option from oc_cart)
The option column data type is TEXT
In SQL, these two expressions are different:
WHERE 228 in ('20,228,27,229')
WHERE 228 in ('20','228','27','229')
The first example compares the integer 228 to a single string value, whose leading numeric characters can be converted to the integer 20. That's what happens. 228 is compared to 20, and fails.
The second example compares the integer 228 to a list of four values, each can be converted to different integers, and 228 matches the second integer 228.
Your subquery is returning a single string, not a list of values. If your oc_cart.option holds a single string, you can't use the IN( ) predicate in the way you're doing.
A workaround is this:
WHERE FIND_IN_SET(228, (SELECT option FROM oc_cart WHERE...))
But this is awkward. You really should not be storing strings of comma-separated numbers if you want to search for an individual number in the string. See my answer to Is storing a delimited list in a database column really that bad?

MySQL sorting product codes that have both alpha and numeric characters

I have to sort output of a query for product names where the names have both alpha and numeric characters.
I have already found various solutions that convert the variables to numeric values (+0, etc.), and they sort the numeric part of the product names. But the preceding part of the product name string is of varying lengths, so the names aren't sorted alphabetically:
Post Lantern PL1
Post Lantern PL2
Post Lantern PL10
Post Lantern PL22
Landscape Light LV1
Landscape Light LV2
Landscape Light LV10
Landscape Light LV11
I guess the shorter names are sorted first?
I want the results sorted naturally: alphabetically, with the numbers in a natural order as well. I have tried:
ORDER by CAST(`product_name` AS DECIMAL), product_name
...
ORDER by product_name+0
The shorter names get sorted first, even though they are later in the alphabet. The numbers in the last part need to be in numerical order.
Here's a long query doing what you're looking for. I'm not sure about performance, though. You might also want to make some tests on several records to make sure.
SELECT
*
,SUBSTRING(
REVERSE(CAST(REVERSE(CONCAT(`product_name`,'8')) AS UNSIGNED)),1,
CHARACTER_LENGTH(
REVERSE(CAST(REVERSE(CONCAT(`product_name`,'8')) AS UNSIGNED))
)-1
) AS 'numericVal'
FROM `some_table`
ORDER BY
SUBSTRING(`product_name`,1,CHAR_LENGTH(`product_name`)-CHAR_LENGTH(`numericVal`)),
CAST(`numericVal` AS UNSIGNED INTEGER)
The 8's in the CONCAT() functions are there for numbers that end with zero(s). Otherwise when you reverse e.g. the string "etc30" and parse the number there it will be 3, not 03. So reversing it back will again produce 3 instead of 30.
You can change those two 8's in the CONCAT() functions with any single digit(s) (except zeros) you like.
[EDIT 2]
Here's the breakdown.
# Example record "Post Lantern PL10"...
SELECT
*
,SUBSTRING( # 5a) substring of this is calculated
REVERSE( # 4) gets re-reversed into "108"
CAST( # 3) gets casted into an integer so "801" part is parsed
REVERSE( # 2) gets reversed: "801LP nretnaL tsoP"
CONCAT(`product_name`,'8') # 1) is concatenated with an 8: "Post Lantern PL108"
)
AS UNSIGNED)
),
1, # 5b) from the first character (index is 1 for this in SQL)
CHARACTER_LENGTH( # 5c) and the length is recalculated (steps 1-4 repeated)
REVERSE(
CAST(
REVERSE(
CONCAT(`product_name`,'8')
)
AS UNSIGNED)
)
)-1 # 5d1) minus 1 because at the beginning we appended an 8 and we
# 5d2) want to get rid of it now, so we're dropping the last digit
) AS 'numericVal'
FROM `some_table`
ORDER BY # 6) order by
SUBSTRING(`product_name`, # 7a) first, substring `product_name`
1, # 7b) from the first character
CHAR_LENGTH(`product_name`)-CHAR_LENGTH(`numericVal`) # 7c) with the total length - length of numeric part
),
CAST(`numericVal` AS UNSIGNED INTEGER) # 8a) then, by the numeric part, which gets casted into
# 8b) an integer for accurate numeric ordering
[EDIT 1]
I think the best shot you have (to have total control over the varying data) is to separate product_name into 3 columns - product_name (like "Landscape Light"), product_class (or whatever, like "LV") and product_version (the numeric part).

Single quotes affecting the calculations in Select query

SELECT COUNT(*) FROM area
WHERE ROUND(SQRT(POWER(('71' - coords_x), 2) +
POWER(('97' - coords_y), 2))) <= 17
==> 51
SELECT COUNT(*) FROM area
WHERE ROUND(SQRT(POWER((71 - coords_x), 2) +
POWER((97 - coords_y), 2))) <= 17
==> 22
coords_x and coords_y are both TINYINT fields containing values in the range [1, 150]. Usually MySQL doesn't care if numbers are quoted or not.. but apparently it does in this case.
The question is just: Why?
MySQL always cares about data types. What happens is that your code relies in automatic type casting and performs math on strings (which can hold a number or not). This can lead to all sort of unpredictable results:
SELECT POW('Hello', 'World') -- This returns 1
To sum up: you need to learn and use the different data types MySQL offers. Otherwise, your application will never do reliable calculations.
Update:
One more hint:
TINYINT[(M)] [UNSIGNED] [ZEROFILL]
A very small integer. The signed range
is -128 to 127. The unsigned range is
0 to 255.
URL:
http://dev.mysql.com/doc/refman/5.1/en/numeric-type-overview.html
I hope you are not trying to store 150 in a signed tinyint column.