SQL search with REGEX instead with BETWEEN operator - mysql

I have MySQL database, and inside table with ads. In one field of table of that database, data is being saved in json format. In that json formatted data, I have key which value contains price (with decimal values).
That field (named for example ad_data), which is saved in database field, contains (json) data like this:
{"single_input_51":"Ad 44 test.","price":"20.00","single_input_4":"ad test title, ad tes title, .","single_input_11":"8.8.2015.","single_input_5":"video test","single_input_6":"https://www.youtube.com/watch?v=nlTPeCs2puw"}
I would like to search in that field, so I can find price range that is searched. If for example, user sets in html form he wants to search in ranges from 100.00 do 755.00, SQL should return only rows where that field (which data is saved as json) contains those values that are from 100.00 to 755.00.
So basically, I would want to write something like this with REGEX in SQL for that json formatted contents of that field (numbers here are just examples, I must be able to to this for every starting and closing decimal number, and numbers I will pass programatically):
SELECT id, price FROM ads WHERE price BETWEEN 100.00 AND 755.00
What would be SQL command for that search via REGEX?

Don't use REGEX for doing the match, that will be painful. If you had a particular range of prices you were looking for, it might be doable, but to dynamically generate the regular expression to "work" for any specified range of prices, when the price could be two, three or more characters, that's going to be hard. (The REGEXP function in MySQL only returns a boolean indicating whether a match was found or not; it won't return the portion of the string that was matched.)
If I had to do a comparison on "price", I would parse the value for price out of the string, then cast that to a numeric value, and the do a comparison on that.
For example:
SELECT t.col
FROM mytable t
WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(t.col,'"price":"',-1),'"',1) + 0
BETWEEN 100.00 AND 755.00
To answer the question you asked: what expression would you use to perform this match using a REGEX...
For "price between 100.00 and 755.00", using MySQL REGEXP, the regular expression you would need would be something like the second expression in the SELECT list of this query:
SELECT t.col
, t.col REGEXP '"price":"([1-6][0-9][0-9]\.[0-9][0-9]|7[0-4][0-9]\.[0-9][0-9]|75[0-4]\.[0-9][0-9]|755\.00)"' AS _match
FROM ( SELECT 'no' AS col
UNION ALL SELECT 'no "price":"14.00"def'
UNION ALL SELECT 'ok "price":"99.99" def'
UNION ALL SELECT 'ok "price":"100.00" def'
UNION ALL SELECT 'ok "price":"699.99" def'
UNION ALL SELECT 'ok "price":"703.33" def'
UNION ALL SELECT 'ok "price":"743.15" def'
UNION ALL SELECT 'ok "price":"754.99" def'
UNION ALL SELECT 'no "price":"755.01" def'
) t
The regular expression in this example is almost a trivial example, because the price values we're matching all have three digits before the decimal point.
The string used for a regular expression would need to be crafted for each possible range of values. The crafting would need to take into account prices with different number of digits before the decimal point, and handle each of those separately.
For doing a range check of price between 95.55 to 1044.44, that would need to be crafted into a regular expression to check price in these ranges:
95.55 thru 95.59 95\.5[5-9]
95.60 thru 95.99 95\.[6-9][0-9]
96.00 thru 99.99 9[6-9]\.[0-9][0-9]
100.00 thru 999.99 [1-9][0-9][0-9]\.[0-9][0-9]
1000.00 thru 1039.99 10[0-3][0-9]\.[0-9][0-9]
1040.00 thru 1043.99 1040[0-3]\.[0-9][0-9]
1044.00 thru 1044.39 1044\.[0-3][0-9]
1044.40 thru 1044.44 1044\.4[0-4]
It could be done, but the code to generate the regular expression string won't be pretty. (And getting it fully tested won't be pretty either.)

(#spencer7593 has a good point; here's another point)
Performance... If you have an index on that field (and the optimizer decides to use the index), then BETWEEN can be much faster than a REGEXP.
BETWEEN can use an index, thereby minimizing the number of rows to look at.
REGEXP always has to check all rows.

Related

'SUM' is not a recognized built-in function name when converting VARCHAR to DECIMAL

I am new to the community so please bear with me. I am working on a sum function that will take the values of 3 columns (Exchange, Commission, Otherfees) and give me that total based on row. The datatypes for these 3 fields are VARCHAR. I started by using a CONVERT function and then addressed any NULLs. Please see the query below:
SELECT SUM(
(SELECT(SELECT
CONVERT(decimal(18,4), isnull(ExchangeFee,0)) AS decimal
FROM T_TABLE) as EXCHANGE_VALUE) +
(SELECT(
SELECT
CONVERT(decimal(18,4), isnull(Commission,0)) AS decimal
FROM T_TABLE) AS COMMISSION_VALUE) +
(SELECT(
SELECT
CONVERT(decimal(18,4), isnull(OtherFees,0)) AS decimal
FROM T_TABLE) AS OTHERFEES_VALUE) AS decimal) AS SUMMED_VALUE
When running this query, I get the message
'SUM' is not a recognized built-in function name.
Please let me know your thoughts.
You could start by using the correct data types for your fields.
ExchangeFee, Commission and OtherFees are all numeric, so why store them in a varchar?
If the values should never be NULL, and here these look like they probably probably shouldn't, set them as NOT NULL and default them to 0.
That said, mysql will convert strings to numbers in a numerical context so you only need to worry about any NULL values which COALESCE or IFNULL will deal with.
As for the query which you want to sum the rows, all of the data is coming from T_TABLE so the general structure of the query should be:
SELECT COALESCE(ExchangeFee,0) + COALESCE(Commission,0) + COALESCE(OtherFees,0) AS SUMMED_VALUE
FROM T_TABLE;

mysql performance <, > for varchar vs integer

I need to save unknown datatypes to a field. I therefore have to use varchar instead of integer (because the data could also be a string)
id, filter1, filter2
1, male, 24
2, female, 53
In this case filter1 is gender and filter2 is age. Is there a big performance impact if I query:
SELECT * FROM tbl WHERE filter2 > 30
compared to using integer?
If you write:
where filter > 30
Then MySQL will do what you want -- but you might get strange results. If you had a column with the value '44x', then it would also be chosen by the filter. Why? Because MySQL will convert filter to a string. In addition, the type conversion generally precludes the use of an index.
If you use strings:
where filter > '30'
Then you don't have the string conversion problem, but you will get all the strings that start with letters.
In other words, don't mix types like this. Values should be stored in their native types. You should revisit your data model -- you probably want a column called age somewhere (or better yet, date-of-birth).

How to retrieve a value after second hyphen using mysql select query

Below I am using substring index within max attribute in select statement to get max value from a column. But below code works fine for single digits after second dash but it doesn't retrieve value for double digit values after second dash.
Below is the query what i am using
select max(SUBSTRING_INDEX(pid,"-",-1)) from patient;
Values stored in column are of pattern as shown below
P-29082017-1,
P-29082017-2,
...
P-29082017-9,
P-29082017-10
The above query returns only single digit, i,e if i have 10 entries say pid from 1 to 10 listed in column, Value i am getting back from the above query is 9 and not 10
Please suggest where i am going wrong with the query
I think your max() function is being evaluated in string context rather than numeric context. In string context, 9 comes after 10.
So try this. It will turn SUBSTRING() output into numbers.
SELECT MAX(CAST(SUBSTRING_INDEX(pid,'-',-1) AS INT))

MySQL In clause not giving the right result

In a MySQL table i have a field, containing this value for a given record : "1908,2315,2316"
Here is my sql Query :
SELECT * FROM mytable WHERE 2316 IN (myfield)
I got 0 results!
I tried this :
SELECT * FROM mytable WHERE 2315 IN (myfield)
Still 0 results
And then i tried this :
SELECT * FROM mytable WHERE 1908 IN (myfield)
Surprisingly i obtained the record when searching with 1908! What should i do to also obtain the record when searching with 2315 and 2316 ? What am i missing ?
Thanks
You appear to be storing comma delimited values in a field. This is bad, bad, bad. You should be using a junction table, with one row per value.
But, sometimes you are stuck with data in a particular structure. If so, MySQL provides the find_in_set() functions.
SELECT *
FROM mytable
WHERE find_in_set(2316, myfield) > 0;
You can't use IN() over comma separated list of no.s its better to normalize your structure first for now you can use find_in_set to find results matching with comma separated string
SELECT * FROM mytable WHERE find_in_set('1908',myfield) > 0
This question has been asked and answered before, but I don't want to hunt for it; this question should be closed as a duplicate. But, to answer your question:
The commas in the string, the column value, are just characters. Those are part of the string. They aren't seen as "separators" between values in the SQL text. The way SQL sees it, the column contains a single value, not a "list" of values.
So, in your query, the IN (field) is equivalent to an equals comparison. It's equivalent to comparing to a string. For example:
... WHERE 2316 = '1908,2315,2316'
And those aren't equal, so the row isn't returned. The "surprisingly" finding of a match, in the case of:
... WHERE 1908 IN ('1908,2315,2316')
that's explained because that string is being evaluated in a numeric context. That is, the comparison returns true, because all of these also true:
... WHERE 1908 = '1908,2315,2316' + 0
... WHERE 1908 = '1908xyz' + 0
... WHERE 1908 = '1907qrs' + 1
(When evaluated in a numeric context, a string gets converted to numeric. It just happens that the string evaluates to a numeric value that equals the integer value it's being comparing to.)
You may be able to make use of the MySQL FIND_IN_SET function. For example:
... WHERE FIND_IN_SET(2316,'1908,2315,2316')
But, please seriously reconsider the design of storing comma separated list. I recommend Bill Karwin's "SQL Antipatterns" book...
http://www.amazon.com/SQL-Antipatterns-Programming-Pragmatic-Programmers/dp/1934356557
In mysql IN clause is utilized as
SELECT * FROM mytable WHERE column_name IN (set_of_values) ;
Mention column name instead of values
Please try
SELECT * FROM mytable WHERE LOCATE(CONCAT (',', 2316 ','), CONCAT (',',myfield,',' ) ) <>0

Use comma separated list from one table as clause in query for another table

I have an events table with a field called breaks. This is populated with data in a comma separated format, i.e. 1,2,3 or 1 or 1,4,5 - the same format that MySQL's IN command uses.
I'd then like to run a query - on the slots table - to return all rows apart from those specified in events.breaks.
The query, theoretically, should be something like this:
SELECT
`slots`.`id` AS id,
RIGHT(`slots`.`time`, 8) AS `time`
FROM
`slots`, `event`
WHERE
`slots`.`id` NOT IN (`event`.`breaks`)
But that doesn't appear to work - if event.breaks is 4,5,7, the only row from the slots table that doesn't return is 4!
SQLFiddle here: http://sqlfiddle.com/#!2/913fe/1/0
You're passing a single field to the NOT IN () clause, not a subexpression. Think of it like this
(1, 2, 3)
is roughly the same as
SELECT 1
UNION
SELECT 2
UNION
SELECT 3;
as a subexpression. What you're doing instead is
('4,5,7')
which is roughly equivalent to
SELECT '4,5,7';
which in turn MySQL probably converted to a number for the comparison and the result is
NOT IN (4)
What you're actually trying to do isn't really supposed to be done like that. It'd be better if you added an AxB relation table so you can select several rows with the IDs you don't want.
Give this a try:
SELECT slots.id AS id, RIGHT(slots.time, 8) time
FROM slots, event
WHERE FIND_IN_SET(slots.id, event.breaks) = 0
This is how the FIND_IN_SET(str,strlist) function works:
Returns a value in the range of 1 to N if the string str is in the string list strlist consisting of N substrings. A string list is a string composed of substrings separated by “,” characters. [...] Returns 0 if str is not in strlist or if strlist is the empty string.
Also note that IN (val1, val2, val3) is NOT the same as IN (val4) where val4 is a commma-separated string. The IN clause will compare by equality.
you may need a subselect to return the split string
... NOT IN (SELECT your_split_fnc(`event`.`breaks`) FROM `events`)
See answers here for a way to split strings in MySQL Can Mysql Split a column?
instr() MySQL function could be of help also
... INSTR(event.breaks,id) = 0
http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_instr