how can extract part of a text from a field in mysql? - mysql

I have fields like this:
-----------------
id | name
-----------------
1 | name123
-----------------
2 | name
-----------------
3 | name456
-----------------
4 | name
I want to extract rows which have digit in name and a field that contains the number like this
------------------------------
id | name | number
-----------------------------
1 | name123 | 123
-----------------------------
3 | name456 | 456
how can we find the records that have digit and extract digit as a new field?

Here is another way to do with mysql
SELECT
id,
name,
SUBSTRING(
name,LEAST (
if (Locate('0',name) >0,Locate('0',name),999),
if (Locate('1',name) >0,Locate('1',name),999),
if (Locate('2',name) >0,Locate('2',name),999),
if (Locate('3',name) >0,Locate('3',name),999),
if (Locate('4',name) >0,Locate('4',name),999),
if (Locate('5',name) >0,Locate('5',name),999),
if (Locate('6',name) >0,Locate('6',name),999),
if (Locate('7',name) >0,Locate('7',name),999),
if (Locate('8',name) >0,Locate('8',name),999),
if (Locate('9',name) >0,Locate('9',name),999)
),LENGTH(name)
) as number
from users
having number <> '' ;

you can use MySQL's string conversion on an int to strip out the name like so
SELECT
t.id,
t.name,
REVERSE(REVERSE(t.name)+ 0) AS num,
REPLACE(t.name,REVERSE(REVERSE(t.name)+ 0),'') AS actualname
FROM foobar t
HAVING num <> 0
the trick with this is by adding a 0 mysql is comparing the numeric value in the name... however the name has to start with a number... so I reverse it do the calculation and then reverse again... NOTE all of your names have to start with the name and end with a number for this to work for all of them
FIDDLE DEMO
EDIT:
since you say that some can start with a number and others end with a number.. then try this
SELECT
t.id,
t.name,
REVERSE(REVERSE(t.name)+ 0) AS num,
REPLACE(t.name,REVERSE(REVERSE(t.name)+ 0),'') AS actualname
FROM foobar t
HAVING num <> 0
UNION ALL
SELECT
t.id,
t.name,
t.name + 0 AS num,
REPLACE(t.name,t.name + 0,'') AS actualname
FROM foobar t
HAVING num <> 0
ANOTHER DEMO

Another way, assuming the number you want is at the end of the string. REVERSE() to put the number part in front, then CONVERT() to make it a number and strip off the text, then REVERSE() again WHERE name ends in a number. Feels like a kludge though:
select id, name, reverse(convert(reverse(name),signed ))
from tbl
where name REGEXP '[0-9]+$';
SQL Fiddle Example

Related

MySQL Insert with functionality similar to Window's default file naming

I have a table with a name column. Initially a name is not added to the table but I would like to add a default name when a new row is inserted, much like window's functionally when creating a new file. I'm trying to figure out how to query the number which is suppose to be next in the sequence.
For example, if my table looks like this:
id | name
==========
1 | New Name (1)
2 | real name
3 | New Name
The next inserted row's name should be "New Name (2)". If my table looks like this:
id | name
==========
1 | New Name (2)
2 | real name
or this:
id | name
==========
1 | another name
2 | real name
The next inserted row's name should be "New Name". If my table looks like this:
id | name
==========
1 | New Name (2)
2 | real name
3 | New Name
4 | New Name (3)
The next inserted row's name should be "New Name (1)". Thus far I was able to create the query to get the existing numbers ("New Name" = 0)
SELECT SUBSTRING_INDEX(SUBSTR(d.name,INSTR(d.name,'(') + 1), ')', 1)
FROM data d
WHERE d.widget_name REGEXP '^New[[:space:]]Name[[:space:]]\\([[:digit:]]\\)$'
UNION
SELECT 0
FROM data d
WHERE d.name REGEXP '^New[[:space:]]Name$'
Now I need a way to to turn that list of numbers to a single number which will indicate whats the next default name enumeration. I've tried using NOT EXISTS from this question but I couldn't figure out how to use the code above both in FROM and the WHERE.
I also tried to do this by creating a row_num functionality using a_horse_with_no_name's answer in this question. Assuming num_data is the result of the query(trying to keep it clean) table and it's column name is name_num:
SELECT IFNULL(row_number, (SELECT IFNULL(MAX(name_num),0)
FROM num_data))
FROM (SELECT #rownum:=#rownum + 1 AS row_number, t.*
FROM (num_data) t,
(SELECT #rownum := 0) r) gap_table
WHERE gap_table.row_number <> gap_table.widget_num
ORDER by row_number
LIMIT 1;
But that didn't seem to get it right too.
If you want the next name for a given name, I would expect the query to look like this:
select (case when count(*) = 0 then $name
else concat($name, '(',
max(substring_index(name, ' (', -1) + 0) + 1,
')')
end)
from num_data
where name rlike concat($name, ' [(][0-9]+[)]' or
name = $name;
Here is a SQL Fiddle demonstrating it.
This assumes that name being tested is provided by a parameter called $name.
Note: The above query generates the new name. It should be obvious how to get the next number in sequence.
This did the trick, thanks to #GorodonLinoff's answer:
select (case when count(*) = 0 then '$name'
when max(name = '$name') = 0 then '$name'
when max(name = '$name(1)') = 0 then '$name(1)'
else concat('$name', '(', max(substring_index(name, '(', -1) + 0) + 1, ')')
end)
from data
where name rlike concat('$name', '[(][0-9]+[)]') or
name = '$name';

select one row multiple time when using IN()

I have this query :
select
name
from
provinces
WHERE
province_id IN(1,3,2,1)
ORDER BY FIELD(province_id, 1,3,2,1)
the Number of values in IN() are dynamic
How can I get all rows even duplicates ( in this example -> 1 ) with given ORDER BY ?
the result should be like this :
name1
name3
name2
name1
plus I shouldn't use UNION ALL :
select * from provinces WHERE province_id=1
UNION ALL
select * from provinces WHERE province_id=3
UNION ALL
select * from provinces WHERE province_id=2
UNION ALL
select * from provinces WHERE province_id=1
You need a helper table here. On SQL Server that can be something like:
SELECT name
FROM (Values (1),(3),(2),(1)) As list (id) --< List of values to join to as a table
INNER JOIN provinces ON province_id = list.id
Update: In MySQL Split Comma Separated String Into Temp Table can be used to split string parameter into a helper table.
To get the same row more than once you need to join in another table. I suggest to create, only once(!), a helper table. This table will just contain a series of natural numbers (1, 2, 3, 4, ... etc). Such a table can be useful for many other purposes.
Here is the script to create it:
create table seq (num int);
insert into seq values (1),(2),(3),(4),(5),(6),(7),(8);
insert into seq select num+8 from seq;
insert into seq select num+16 from seq;
insert into seq select num+32 from seq;
insert into seq select num+64 from seq;
/* continue doubling the number of records until you feel you have enough */
For the task at hand it is not necessary to add many records, as you only need to make sure you never have more repetitions in your in condition than in the above seq table. I guess 128 will be good enough, but feel free to double the number of records a few times more.
Once you have the above, you can write queries like this:
select province_id,
name,
#pos := instr(#in2 := insert(#in2, #pos+1, 1, '#'),
concat(',',province_id,',')) ord
from (select #in := '0,1,2,3,1,0', #in2 := #in, #pos := 10000) init
inner join provinces
on find_in_set(province_id, #in)
inner join seq
on num <= length(replace(#in, concat(',',province_id,','),
concat(',+',province_id,',')))-length(#in)
order by ord asc
Output for the sample data and sample in list:
| province_id | name | ord |
|-------------|--------|-----|
| 1 | name 1 | 2 |
| 2 | name 2 | 4 |
| 3 | name 3 | 6 |
| 1 | name 1 | 8 |
SQL Fiddle
How it works
You need to put the list of values in the assignment to the variable #in. For it to work, every valid id must be wrapped between commas, so that is why there is a dummy zero at the start and the end.
By joining in the seq table the result set can grow. The number of records joined in from seq for a particular provinces record is equal to the number of occurrences of the corresponding province_id in the list #in.
There is no out-of-the-box function to count the number of such occurrences, so the expression at the right of num <= may look a bit complex. But it just adds a character for every match in #in and checks how much the length grows by that action. That growth is the number of occurrences.
In the select clause the position of the province_id in the #in list is returned and used to order the result set, so it corresponds to the order in the #in list. In fact, the position is taken with reference to #in2, which is a copy of #in, but is allowed to change:
While this #pos is being calculated, the number at the previous found #pos in #in2 is destroyed with a # character, so the same province_id cannot be found again at the same position.
Its unclear exactly what you are wanting, but here's why its not working the way you want. The IN keyword is shorthand for creating a statement like ....Where province_id = 1 OR province_id = 2 OR province_id = 3 OR province_id = 1. Since province_id = 1 is evaluated as true at the beginning of that statement, it doesn't matter that it is included again later, it is already true. This has no bearing on whether the result returns a duplicate.

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);

how to move up row to find the last or first occurrence of the same number in mysql

I have a table name current_record in mysql database.
id num
1 0
2 1
3 1
4 1
5 0
my question is : how find the 1st id of num = 1 from last means down to up or 3 times up
the output should return like this
id = 2 num = 1
please write a sql query.
You can use MIN() combined with GROUP BY to achieve this. Something like this:
SELECT MIN(id) AS id, num FROM current_record GROUP BY num
Optionally you can add a WHERE clause if you specifically want just one value instead of one for each num:
WHERE num == 1

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.