How to convert a string value of json into dateTime and compare - mysql

SELECT JSON_EXTRACT(z2schedule,'$[*].start') as startDate from
cpmdev_z2weekly_schedule
After running above code I am getting response as :-
Now If I tried to compare each value to time value using below code but it is not working:-
SELECT JSON_EXTRACT(z2schedule,'$[*].start') as startDate from
cpmdev_z2weekly_schedule where
JSON_EXTRACT(z2schedule,CONVERT('$[*].start'),'TIME')>
'CONVERT('2022-11-02 13:10:00:000', TIME)
My requirement is only to compare each value with the time value and return only if the value is greater than given time.
For Example in Table I have Data as:-
[{"start":"09:00:00.000","end":"17:00:00.000"}]
[{"start":"10:00:00.000","end":"17:00:00.000"}]
[{"start":"11:00:00.000","end":"17:00:00.000"}]
Now I want all the start Date which is greater then 10:00:00
In above case then it should return :
11:00:00.000

The JSON you show is an array of objects. When you use $[*].start, it returns a JSON array. This is not a single time. You can see the square brackets around the time value:
mysql> set #j = '[{"start":"09:00:00.000","end":"17:00:00.000"}]';
mysql> select json_extract(#j, '$[*].start') as times;
+------------------+
| times |
+------------------+
| ["09:00:00.000"] |
+------------------+
The square brackets make it not valid as a time value.
mysql> select convert(json_extract(#j, '$[*].start'), time) as times;
+-------+
| times |
+-------+
| NULL |
+-------+
Since your JSON array seems to have only one object in it, you could use $[0] to select the first object in the array. Then it returns a single string value and that is convertable to a time:
mysql> select convert(json_extract(#j, '$[0].start'), time) as time;
+----------+
| time |
+----------+
| 09:00:00 |
+----------+
Note also that the data type named in the CONVERT() function is a keyword, not a quoted string. That is, 'time' is incorrect, just use time.
If your JSON array may have more than one object, and you need to test all of them, then you should use the JSON_TABLE() function.
By the way, all these issues would be avoided if you stored your start and end times in normal rows and columns. Using JSON makes many queries more difficult to develop and optimize. You should consider normalizing your data, and not using JSON.

Related

Unexpected result in WHERE clause on AI ID field

I have a table which's name is users in my MySQL database, and I am using this DB with Ruby on Rails application with ORM structure for years. The table has id field and this field is configured as AI (auto-increment), BIGINT.
Example of my users table;
+----+---------+
| id | name |
+----+---------+
| 1 | John |
| 2 | Tommy |
| 3 | ... |
| 4 | ... |
| 5 | ... |
| 6 | ... |
+----+---------+
The problem I am facing is when I execute the following query I get unexpected rows.
SELECT * FROM users WHERE id = '1AW3F4SEFR';
This query is returning the exact same value with the following query,
SELECT * FROM users WHERE id = 1;
I do not know why SQL let me use strings in WHERE clause on a data type INT. And as we can see from the example, my DB converts the strings I gave to the integer at position 0. I mean, I search for 1AW3F4SEFR and I expect not to get any result. But SQL statement returns the results for id = 1.
In Oracle SQL, the behavior of this exact same query is completely different. So, I believe there is something different on MySQL. But I am not sure about what causes this.
As has been explained in the request comments, MySQL has a weird way of converting strings to numbers. It simply takes as much of a string from the left as is numeric and ignores the rest. If the string doesn't start with a number the conversion defaults to 0.
Examples: '123' => 123, '12.3' => 12.3, '.123' => 0.123, '12A3' => 12, 'A123' => 0, '.1A1.' => 0.1
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=55cd18865fad4738d03bf28082217ca8
That MySQL doesn't raise an error here as other DBMS do, can easily lead to undesired query results that get a long time undetected.
The solution is easy though: Don't let this happen. Don't compare a numeric column with a string. If the ID '1AW3F4SEFR' is entered in some app, raise an error in the app or even prevent this value from being entered. When running the SQL query, make sure to pass a numeric value, so '1AW3F4SEFR' cannot even make it into the DBMS. (Look up how to use prepared statements and pass parameters of different types to the database system in your programming language.)
If for some reason you want to pass a string for the ID instead (I cannot think of any such reason though) and want to make your query fail-safe by not returning any row in case of an ID like '1AW3F4SEFR', check whether the ID string represents an integer value in the query. You can use REGEXP for this.
SELECT * FROM users WHERE id = #id AND #id REGEXP '^[0-9]+$';
Thus you only consider integer ID strings and still enable the DBMS to use an index when looking up the ID.
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=56f8ee902342752933c20b8762f14dbb

How can I convert the string values inside a MySQL JSON array to upper case?

I have a table that contains a JSON column, and in it a JSON array:
mysql> SELECT profile->'$.countriesVisited' from users;
+-------------------------------+
| profile->'$.countriesVisited' |
+-------------------------------+
| ["us", "il"] |
| ["co", "ph"] |
+-------------------------------+
2 rows in set (0.00 sec)
I want to convert the values inside the array into upper case. (I am assuming this answer would also assist lower case, string replacements.. etc.)
I've been trying to use UPPER, JSON_ARRAY, JSON_QUOTE, JSON_UNQUOTE, etc - at best I end up with a string representation of what I want.
How can I do this? I'm running MySQL 5.7.19.
You need to use JSON casting. Try the following:
UPDATE users
SET profile = JSON_SET(
profile,
'$.countriesVisited',
CAST(
UPPER(profile->'$.countriesVisited')
AS JSON
)
);

Select a particular value of JSON string using MySQL

I have a table that looks like this
+------+------------------------------------+
| id | details |
+------+------------------------------------+
| 1 | {"price":"24.99","currency":"USD"} |
+------+------------------------------------+
Is it possible to, with a single MySQL select statement, obtain the value of price 24.99?
Yes, you can using JSON_EXTRACT
It probably should be like:
SELECT JSON_EXTRACT(details, "$.price")
FROM table_name
or another form:
SELECT details->"$.price"
FROM table_name
(I don't have MySql to test it)
Note that the price in your JSON stored as a string, not a number and you probably would want to cast it to a DECIMAL.

Fetching nested JSON data in HBase using Apache Drill

I am using Apache Drill to run SQL queries on a HBase table. The value in one of the columns is:
0: jdbc:drill:schema:hbase:zk=localhost> select cast(address['street'] as varchar(20)) from hbase.students;
+------------+
| EXPR$0 |
+------------+
| {"id": 123} |
+------------+
1 row selected (0.507 seconds)
I would like to access the id field using a query. Something like:
0: jdbc:drill:schema:hbase:zk=localhost> select tbl.address['street']['id'] from hbase.students as tbl;
+------------+
| EXPR$0 |
+------------+
| null |
+------------+
As you can see, this does not work. I am run to similar queries on JSON data in a file. My question is can I query JSON data in HBase.
OK. I found the answer to this question, in case someone else has the same requirement.
The first step is to convert the HBase data to JSON using the built-in convert_from() function. A view can be created against which the queries can be run.
> create or replace view Street as select convert_from(Students.address.street, 'JSON') json from hbase.Customer;
Then, run query against the view
> select * from Street;
> select Street.json.id from Street;
You can also use a subquery to convert the data in your HBase column into JSON:
select t.json.id
from (select convert_from(Students.address.street, 'JSON') json
from hbase.Customer) t;

How do I convert from a date to string in mySQL?

I need to treat a date field in mySQL as if it is a string. For the purposes of using the date in a LIKE statement:
select * from table where dob like some_string;
Doing this currently produces the following warning:
mysql> show warnings;
+---------+------+--------------------------------------------------------+
| Level | Code | Message |
+---------+------+--------------------------------------------------------+
| Warning | 1292 | Incorrect date value: '1492' for column 'dob' at row 1 |
+---------+------+--------------------------------------------------------+
1 row in set (0.00 sec)
I would use DATE_FORMAT to get a string representation of your DATE column:
SELECT
*
FROM
yourtable
WHERE
DATE_FORMAT(dob, '%Y-%m-%d') LIKE '1492%';
and use the pattern that suits your need best. You find the specifiers right in the linked manual page.
Note:
MySQL can't use an index for this kind of query, so it will be slow.
Use a cast
Select *
From Table
Where Cast(dob as nvarchar(20)) Like some_string;
using the date in a LIKE statement
No. Things that this approach does wrong:
Wastes CPU time converting dates to strings.
Wastes time doing string comparisons when integer comparisons could be done.
Throws away any index on dob that might otherwise make the query more efficient.
A better approach would be something like:
SELECT *
FROM table
WHERE dob BETWEEN '1492-01-01' AND '1492-12-31'
You will always want to do as few type conversions as possible and keep the table data as-is so that indexes are properly utilized.