Why mysql order by alphanumeric not working - mysql

I am facing a problem with the MySQL query. The problem is
I have a table called 'members' and a column 'area'. In 'area' column there are alphanumeric values like
1
2
Street # 2
5
78
Street # 1A
Street # 1
Street # 1C
Street # 1B
3
Now What I want? I want to sort like this
1
2
3
5
78
Street # 1
Street # 1A
Street # 1B
Street # 1C
Street # 2
I tried almost every code but not fulfilling my requirements. Last code that is good but not as per my requirement. Currently, I have this code:
SELECT DISTINCT(area) FROM members ORDER BY LENGTH(area), area ASC
One thing that I want to clear that area filed has duplicate values in it.
I'll be thankful if someone helps me.
Thanks in advance

Extract the first part of the string and order it if it is a number:
select t.*
from t
order by (area regexp '^[0-9]') desc, -- numbers first
substring_index(area, ' ', 1) + 0, -- by number
area asc -- rest alphabetically
Note that this handles the awkward case where the initial number start with 0.
And depending on how you want the strings ordered, you might still want to end with len(area), area as the last two order by keys.
Here is a db<>fiddle.

This should work for your sample data:
SELECT DISTINCT area
FROM members
ORDER BY (area + 0 > 0 or area = '0') desc, area + 0, area
Demo on DB Fiddle:
| area |
| :---------- |
| 1 |
| 2 |
| 3 |
| 5 |
| 78 |
| Street # 1 |
| Street # 1A |
| Street # 1B |
| Street # 1C |
| Street # 2 |

One more query:
SELECT DISTINCT(area)
FROM members
ORDER BY
area+0=0 ASC, -- get numbers first
area+0 ASC, -- order numbers
area ASC; -- order strings
Live fiddle here SQLize.online

Related

MySQL Selection for top x rankings

I want to make a selection in my MySQL database where I have stored numerical values in a specific column.
I have seen some websites that show 10 ten based on some criteria, and I've been wondering if it will be possible to make a selection based on numerical values.
I have my database like so:
| id | name | pts |
+----+-----------+-----+
| 1 | Brad | 3 |
| 2 | Jane | 8 |
| 3 | Jones | 10 |
| 4 | Paty | 15 |
| 5 | Sammy | 2 |
Now my question is, how do I make a query that selects only the top 3 users, based on the pts, such that it returns the result as:
1st Position = Paty => 15pts
2nd Position = Jones => 10pts
3rd Position = Jane => 8pts
?
try this :
SELECT * FROM tablename ORDER BY pts desc limit 3
Your query should use LIMIT to get the top results:
SELECT id, name, points FROM table ORDER BY pts DESC LIMIT 3
shoul do the trick.
Order by will order the table from the highest to the lowest and limit will tell mysql to get only the first three results
You can find more on this topic here for example.
And this is a question very close to your
you can use this query
SELECT CONCAT(`id` , 'Postion = ' , `name` , '=>' ,`pts` , 'pts' ) AS result FROM table ORDER BY pts DESC LIMIT 3
This Question I seen many places anyway the query is.
select top 3 * from tablename order by cols_name desc
This Query brings top 3 row with highest values based on your column.

Count occurrences of character in a string and Group By with SQL without group_concat

I try to count occurrences and to add group by after
My table :
id | book | chapter | text
1 | 1 | 1 | 'hello hello world'
2 | 1 | 2 | 'hello hello hello hello'
3 | 1 | 3 | 'world'
4 | 1 | 4 | 'hello test'
I execute my request :
SELECT
book,
chapter,
group_concat(text) as text,
ROUND (
(
LENGTH(group_concat(text))
- LENGTH( REPLACE ( group_concat(text), "hello", "") )
) / LENGTH("hello")
) AS 'count'
FROM my_table
WHERE book=43
GROUP BY chapter, book
I would like this result :
book | chapter | count
----------------------
43 | 1 | 2
43 | 2 | 4
43 | 3 | 0
43 | 4 | 1
It's works !
But I must redefine a variable group_concat_max_len with :
SET SESSION group_concat_max_len = 1000000;
My server is an mutualized server and I don't have the permission (to SET GLOBAL, for example).
I would like to execute an other request to have the same result without the group_concat_max_len, do you have an idea ?
I think this would be simpler as just a SUM():
SELECT book, chapter,
SUM(LENGTH(REPLACE(text, 'hello', 'hellox')) - LENGTH(text)) as num_hellos
FROM my_table
WHERE book = 43
GROUP BY chapter, book;
You would only need to deal with GROUP_CONCAT() if you output the concatenated text.
Note that this method of calculating the number of "hello"s in the text is much simpler. It replaces the string with a string one character longer, and then simply takes the difference of lengths. If your string is UTF-8 encoded, you should be sure that the additional character is 1 byte or use CHAR_LENGTH() instead of LENGTH().
EDIT:
Let me also note that for the example in the question, aggregation is not necessary, so this suffices:
SELECT book, chapter,
(LENGTH(REPLACE(text, 'hello', 'hellox')) - LENGTH(text)) as num_hellos
FROM my_table
WHERE book = 43;
However, the OP's issue with group_concat_max_len suggests that the actual problem is more complex.

should I use MySQL procedure for this query?

I want to make stored procedure but I don't know what should be the right approach or if this is even possible to do in MySQL.
Let me introduce you with my problem. Let's say I have table that has columns like this:
TABLE A
id | Hotel | city_name | region_name | country
1 | A | Amsterdam | North-Holland | Netherlands
2 | B | Amsterdam | North-Holland | Netherlands
3 | C | Leiden | North-Holland | Netherlands
4 | D | Katwijk | North-Holland | Netherlands
5 | E | Leiden | North-Holland | Netherlands
6 | F | Katwijk | North-Holland | Netherlands
I would like to get only 3 results each time I execute this query and results need to be created in this order:
If there are 3 or more cities ( user selects in this case we can take Amsterdam ) that city_name=Amsterdam then return random 3 cities in Amsterdam
If there is less then 3 records in Amsterdam return any of the record that has Amsterdam + return random records where region=North-Holland but the total amount of the records that are returned should always be 3 ( example: we have 2 records where city= Amsterdam + we take one random record from region=North Holand;
example2: we have one record where city=Amsterdam + we take 2 random records where region="north Holland")
Is that possible to this with the SQL Or should I get all the records in php and then iterate through each of the records?
I probably need to pass 2 arguments in the procedure (city_name, region ).
So far I have tried some basic SQL queries and I couldn't get it to work.
You should try something like
(SELECT *
FROM A
WHERE city_name = 'Amsterdam'
ORDER BY RAND()
LIMIT 3)
UNION
(SELECT *
FROM A
WHERE region = 'North Holand' AND city_name <> 'Amsterdam'
ORDER BY RAND()
LIMIT 3)
LIMIT 3;
I would recommend you do the logical part of this primarily in php. While it is possible to do in sql, I've found that logic structures in sql tend to be hard to follow, and that is less of an issue in php.
Doing the logic in php could require two separate queries (but only if you don't get 3 initially).
I'd run a query to get the initial three (use LIMIT 3 in the sql). Check to see if you got three results. If you didn't, subtract the amount you do get from 3, then use that as the LIMIT in a second query to get the other random results.

MySQL: Case not evaluating correctly. Where did I went wrong?

This is the query I am executing to search for the autocomplete function for keyword "sector 2"
SELECT display_text,token, relevancy ,
(CASE token
WHEN token = 'sector' THEN relevancy*1000
WHEN token = '2' THEN relevancy*1000
ELSE relevancy / 100
END) as real_relevancy
FROM location_search WHERE display_text LIKE '%sector%' AND display_text LIKE '%2%'
ORDER BY real_relevancy DESC
The table have five fields: id , area_id, display_text, token and relevancy.
display text is what i want to show in auto complete search result. And token contains the display text split by spaces.
like for a display text "Sector 2 Noida" there will be 3 entries in the table
id | area_id | display_text | token | relevancy
6 | 234 | Sector 2 Noida | sector | 441
7 | 234 | Sector 2 Noida | 2 | 986
8 | 234 | sector 2 Noida | noida | 660
And similarly for all other display areas. Its pretty much clear by the query what I am trying to do here, that is getting more relevant results on the top.
The problem is that the first case evaluates true and is execulted in random cases where token is niether sector nor 2.
Like the first reult in resultset is
display_text | token | relevancy | real_relevancy
Sector 29 - Powergrid , Gurgaon | Powergrid | 789 | 789000
Why is it happening. WHere did I go wrong with the query? Probably m doing some silly mistake. Please help.
I tried few things and modified the case condition from token = 'sector' to just 'sector' ... its working now.
So the new query looks like this :
SELECT display_text,token, relevancy ,
(CASE token
WHEN 'sector' THEN relevancy*1000
WHEN '2' THEN relevancy*1000
ELSE relevancy / 100
END) as real_relevancy
FROM location_search WHERE display_text LIKE '%sector%' AND display_text LIKE '%2%'
ORDER BY real_relevancy DESC
Silly mistake indeed.

MySql string manipulation, selecting items from text

I have a "changesets" table which has a comments column where people enter references to bug issues in the format "Fixed issue #2345 - ......", but can also be "Fixed issues #456, #2956, #12345 ...."
what's the best way to select these reference numbers so i can access the issues via a join.
given this change sets table
id comments
===========================
1 fixed issue #234 ....
2 DES - #789, #7895, #123
3 closed ticket #129
i'd like results like this
changeset_id issue_id
=====================
1 234
2 789
2 7895
2 123
3 129
I've used substring_index(substring_index('#',-1),' ',1) type construct but that will only return a single reference per line.
Also looking for the most efficient way to do this text lookup
Any help appreciated
Thanks
Here's one (bloated/messy) approach on how to get the desired dataset...
Step 1 - figure out what the maximum # of issue ids is
SELECT MAX(LENGTH(comments)- LENGTH(REPLACE(comments,'#',''))) AS max_issues
FROM change_sets
Step 2 - recursively create a UNION'd query with a number of "levels" equal to the maximum number of issue ids. For your example,
SELECT changeset_id, issue_id FROM
(
SELECT id AS changeset_id, CAST(SUBSTRING_INDEX(comments,'#',-1) AS UNSIGNED) AS issue_id FROM change_sets
UNION
SELECT id AS changeset_id, CAST(SUBSTRING_INDEX(comments,'#',-2) AS UNSIGNED) AS issue_id FROM change_sets
UNION
SELECT id AS changeset_id, CAST(SUBSTRING_INDEX(comments,'#',-3) AS UNSIGNED) AS issue_id FROM change_sets
) a
HAVING issue_id!=0
ORDER BY changeset_id, issue_id
I'm taking advangage of UNION's ability to remove duplicate rows, and CAST's ability to use the leading numeric values when deciding the integer.
The result using your toy dataset:
+--------------+----------+
| changeset_id | issue_id |
+--------------+----------+
| 1 | 234 |
| 2 | 123 |
| 2 | 789 |
| 2 | 7895 |
| 3 | 129 |
+--------------+----------+