I'm storing permissions into DB with Array JSON String, and i want select them by permission specific permission. at this time I'm selecting them like this:
1 | Dog | [3,4]
2 | Cat | [33,4]
3 | Tiger | [5,33,4]
4 | wolf | [3,5]
SELECT * FROM `pages` WHERE access REGEXP '([^"])3([^"])'
it works but not as it should work. This query gives me all records which contains 3 but also it gives which contains 33. my question is how i must format my regexp to get row by specific value into json string.
p.s i have mysql 5.5 so as i know on this version json functions is not supported
If you only have numbers in the fields, you can alter your regexp to only take values where the string you are looking for (here the '3') does not have another number immediately close to it :
SELECT * FROM `pages` WHERE access REGEXP '([^"0-9])3([^"0-9])'
REGEXP '[[:<:]]3[[:>:]]'
That is, use the "word boundary" thingies.
Related
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
Let's assume this users table:
-----------------------------------------
| id | ... | info |
-----------------------------------------
| 1 | ... | {"items":["132","136"]} |
I need to make a request to fetch users that have items with id == 136.
This following is the sql I built but it does not work and I dont understand why:
SELECT _u.id FROM users _u WHERE _u.info REGEXP '("items":)([)("136")(])'
Thank you in advance!
Here is one approach using the MySQL JSON functions:
SELECT *
FROM yourTable
WHERE JSON_SEARCH(JSON_EXTRACT(json, "$.items"), 'one', "136") IS NOT NULL;
Demo
The call to JSON_EXTRACT first extracts the JSON array under the items key. Then, we use JSON_SEARCH to try to find an element "136".
Edit:
If you are certain that the JSON to be searched would always just be one key items along with a single level JSON array, then REGEXP might be viable here:
SELECT *
FROM yourTable
WHERE json REGEXP '"items":\\[.*"136".*\\]';
Demo
We currently have a database that has stored over 100,000 records of data over the years however in a structure that does not work anymore.
There is a field in the table called youtube_video
It has been storing all of the embed YouTube videos like this:
http://www.youtube.com/embed/3mHuu5NklOs?rel=0
http://www.youtube.com/embed/3mHuu5NklOs
We need to change it to:
https://www.youtube.com/watch?v=3mHuu5NklOs
Is there a way to write a query that makes this change with a single query?
You can use REGEXP_REPLACE :
SELECT REGEXP_REPLACE(
youtube_video,
'^http://www.youtube.com/embed/([^?]+).*',
'https://www.youtube.com/watch?v=\1'
) FROM mytable
Regex breakdown :
^ : start of string
http://www.youtube.com/embed/ : constant string part
([^?]+) : as many consecutive characters as possible others than a question mark ; the surrounding parentheses capture that part of the string, and make it available as \1 in the second argument to REGEXP_REPLACE()
.* : anything (until end of string)
This demo on DB Fiddle returns :
| youtube_video | new_youtube_video |
| ---------------------------------------------- | ------------------------------------------- |
| http://www.youtube.com/embed/3mHuu5NklOs?rel=0 | https://www.youtube.com/watch?v=3mHuu5NklOs |
| http://www.youtube.com/embed/3mHuu5NklOs | https://www.youtube.com/watch?v=3mHuu5NklOs |
If needed, you can easily turn this into an UPDATE :
UPDATE mytable
SET youtube_video = REGEXP_REPLACE(
youtube_video,
'^http://www.youtube.com/embed/([^?]+).*',
'https://www.youtube.com/watch?v=\1'
);
I have the following table:
+-------------+----------------+
| id | server |
+-------------+----------------+
| 1 | ["1", "15"] |
+-------------+----------------+
I need to get only value that is grather that 1 so in above example i need to get from output only 15
I try using this:
SELECT
JSON_EXTRACT(server, "$[*]") as server
FROM streams
WHERE JSON_EXTRACT(server, "$[*]") != JSON_QUOTE('1')
AND id=1;
But i always get ["1", "15"] and need to get ["15"].
Unfortunately, you can't do this with MySQL's JSON_EXTRACT and JSON_SEARCH functions as they perform extraction and exact matching (not comparison) respectively. So, you have two options:
Normalise the table and have server values into a new column (recommended)
Fetch all the values and perform the filtering in service layer
I added this:
SELECT CASE WHEN
JSON_UNQUOTE(JSON_SEARCH(server, 'all', 1)) IS NULL THEN
JSON_REMOVE(server, '$."1"')
ELSE
JSON_REMOVE(server, JSON_UNQUOTE(JSON_SEARCH(server, 'one', 1)))
END AS server
FROM streams WHERE id=2 AND server NOT LIKE '%[]%';
Now it works but if ["1"] is only 1 in json column i get []...a added NOT LIKE '%[]%' but it prints out always []...where i need to add to get no results found from 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.