Sort by JSON field values - mysql

I have a table with json values like this:
-Table 1
id | name | data
------+----------+---------------
1 | Test | {"city_id": 3, "email":"test#test.com", "city_name":"something"}
2 | Test 2 | {"city_id": 1, "email":"test2#test2.com", "city_name":"another"}
3 | Test 3 | {"city_id": 6, "email":"test3#test3.com", "city_name":"blahblah"}
Now I want SELECT records with order by data.city_name, so I use this code:
SELECT id, name, JSON_EXTRACT(data, 'city_name') AS cityName
FROM table1
ORDER BY cityName ASC
but this query cannot sort my records correctly !
P.S: city_name have UTF-8 characters.

you do not seem to be using JSON_EXTRACT() properly, try with:
SELECT id, name, JSON_EXTRACT(data, '$.city_name') AS cityName
FROM demo ORDER BY cityName ASC
Demo Fiddle

I usually cast the JSON value (->>) to the correct type in order to sort properly:
SELECT id, name, data->>'$.city_name' AS cityName
FROM table1
ORDER BY CAST(cityName AS CHAR) ASC
Otherwise, you end up sorting as a blob (binary), which are treated as binary strings (byte strings) and thus they have the binary character set and collation, and comparison and sorting are based on the numeric values of the bytes in column values (ref).

the easiest way in my opinion
SELECT * FROM YourTable order by data->"$.city_name" desc

Check This.
SELECT Id ,name,SUBSTRING_INDEX(SUBSTRING_INDEX(data,'city_name":"',-1),'"',1) as CITY
FROM tempjson
order by SUBSTRING_INDEX(SUBSTRING_INDEX(data,'city_name":"',-1),'"',1)
OutPut :

Related

Mysql group by included the whitespace as same value

I have this table
CREATE TABLE table1 (
`ID` VARCHAR(100),
`Val` VARCHAR(100),
`Val2` VARCHAR(100)
);
and this value
INSERT INTO table1
(`ID`, `Val`, `Val2`)
VALUES
('1','1234 ','now'), // 1 whitespace
('2','1234 ','now1'), // 2 whitespace
('5','1234 ','now190'), // 2 whitespace
('3','1234 ','now2'), // 3 whitespace
('4','3123123','now3')
I need to group by the data and count how many data that have same value, so i used group by and count
select Val,count(*) from `table1` group by Val
the result not what i expect because the data for ID 1,2,5, and 3 is counted as same value like below result
Val count(*)
1234 4
3123123 1
how could i make the result like expected result below so the value didn't count as same value
Val count(*)
1234 1 // 1 whitespace
1234 2 // 2 whitespace
1234 1 // 3 whitespace
3123123 1
see this fiddle for demo
This is just how MySQL does it by default, unless you use some specific collation. A typical workaround is to use binary:
select binary val, count(*) cnt from table1 group by binary val
Or, if you do want a regular string in the resultset rather than a binary string:
select max(val) as newval, count(*) cnt from table1 group by binary val

How to define a custom ORDER BY in MySQL query

I need output in following order(firstly, group by last 3 letters and then arrange in order based on the first 3 digits)
ColumnA
001_eng
004_eng
002_chn
003_usa
But order by ColumnA gives me
ColumnA
001_eng
002_chn
003_usa
004_eng
This is just sample data. I have hundreds of entries in this format and the values keep changing everyday. So, specifying all the entries inside the field is not a feasible option.
I'm not sure of how to use FIELD() in my case.
You can use FIELD:
select *
from tablename
order by
FIELD(ColumnA, '001_eng', '004_eng', '002_chn', '003_usa')
(please be careful if ColumnA is not in the list the field function will return 0 and the rows will be put on top)
or you can use CASE WHEN:
select *
from tablename
order by
case
when ColumnA='001_eng' then 1
when ColumnA='004_eng' then 2
when ColumnA='002_chn' then 3
when ColumnA='003_usa' then 4
else 5
end
or you can use a different languages table where you specify the order:
id | name | sortorder
1 | 001_eng | 1
2 | 002_chn | 3
3 | 003_usa | 4
4 | 004_eng | 2
then you can use a join
select t.*
from
tablename t inner join languages l
on t.lang_id = l.id
order by
l.sortorder
(with proper indexes this would be the better solution with optimal performances)
You can use SUBSTRING_INDEX in case all ColumnA values are formatted like in the sample data:
SELECT *
FROM mytable
ORDER BY FIELD(SUBSTRING_INDEX(ColumnA, '_', -1), 'eng', 'chn', 'usa'),
SUBSTRING_INDEX(ColumnA, '_', 1)
Demo here
you can use substring() and get order by
SELECT *
FROM table_name
ORDER BY SUBSTRING(ColumnA, -7, 3);

count comma-separated values from a column - sql

I want count the length of a comma separated column
I have use these
(LENGTH(Col2) - LENGTH(REPLACE(Col2,",","")) + 1)
in my select query.
Demo:
id | mycolumn
1 2,5,8,60
2 4,5,1
3 5,Null,Null
query result for first two row is coming correctly.for 1 = 4 ,2 = 3 but for 3rd row it is calculating null value also.
Here is what I believe the actual state of your data is:
id | mycolumn
1 2,5,8,60
2 4,5,1
3 NULL
In other words, the entire value for mycolumn in your third record is NULL, likely from doing an operation involving a NULL value. If you actually had the text NULL your current query should still work.
The way to get around this would be to use COALESCE(val, "") when handling the NULL values in your strings.
Crude way of doing it is to replace the occurances of ',Null' with nothing first:-
SELECT a.id, (LENGTH(REPLACE(mycolumn, ',Null', '')) - LENGTH(REPLACE(REPLACE(mycolumn, ',Null', ''),",","")) + 1)
FROM some_table a
If the values refer to the id of rows in another table then you can join against that table using FIND_IN_SET and then count the matches (assuming that the string 'Null' is not an id on that other table)
SELECT a.id, COUNT(b.id)
FROM some_table a
INNER JOIN id_list_table b
ON FIND_IN_SET(b.id, a.mycolumn)
GROUP BY a.id

MySQL: Select 3 most occuring strings either in column 1 or column 2

I'd like to have the 3 most occuring strings from 2 rows of my database. The thing is the string may occur in column1 or column2. I'd like to use only one query. Thanks in advance
Sample DB:
id string1 string2
1 foo bar
2 bar foo
3 api foo
Output should then be:
string count
foo 3
bar 2
api 1
You can execute the following query:
SELECT string, COUNT(*) FROM
(
SELECT string1 string FROM mytable
UNION ALL
SELECT string2 string FROM mytable
) sub
GROUP BY string
ORDER BY COUNT(*) DESC
LIMIT 3
where mytable is your table name.
With this query:
You take the union of all strings, either string1 or string2.
You group these string by their count.
You order them by the biggest count to the lowest.
You take only the three firsts.

Mysql comma count from field value

I want to count string separators from a MySQL query, mean if the field value is
like :-
1,2,3,4,5
as the string is comma separated so the separator count will be 4.
any idea then please share
THANKS,
you can try to count the length of string and minus the length of string without commas as follows:
LENGTH('1,2,3,4,5') - LENGTH(REPLACE('1,2,3,4,5', ',', ''))
select length('1,2,3,4,5') - length(replace('1,2,3,4,5', ',', ''))
I suggest the following design :
Table name : USER_HOBBIES
| USER_ID | HOBBY_ID |
1 1
1 2
1 3
2 2
2 4
2 5
And now you can easily count user hobbies for a given user :
SELECT count(*) FROM USER_HOBBIES WHERE USER_ID = <user-id>
although it requires another table it is much clearer and on a long list of hobbies this will be much faster than using a function for manipulating strings.