MySQL, how to get the string with highest suffix - php

My table is:
After Query:
SELECT * FROM `table` WHERE `identifier` LIKE '000123_%'
I get:
How to get the row with highest suffix, i.e: 000123_5 with query.

Why dont create the function SPLIT_STRING at your DB like this:
CREATE FUNCTION `SPLIT_STRING`(
str VARCHAR(255) ,
delim VARCHAR(12) ,
pos INT
) RETURNS VARCHAR(255) CHARSET utf8 RETURN REPLACE(
SUBSTRING(
SUBSTRING_INDEX(str , delim , pos) ,
CHAR_LENGTH(
SUBSTRING_INDEX(str , delim , pos - 1)
) + 1
) ,
delim ,
''
);
And later you can call her from your query to get the last characters:
-- Example table
CREATE TABLE `test`(
`keywords` VARCHAR(255) DEFAULT NULL
) ENGINE = INNODB DEFAULT CHARSET = utf8;
INSERT INTO `test`(`keywords`)
VALUES
(
'keyword 1,keyword 2, keyword 3, keyword 4'
);
-- Example query
SELECT
-- keyword 1
SPLIT_STRING(`keywords`,',',1) AS keyword_1,
-- keyword 4, NOT trimmed
SPLIT_STRING(`keywords`,',',4) AS keyword_4,
-- keyword 4, trimmed
trim(SPLIT_STRING(`keywords`,',',4)) AS keyword_4_trimmed
FROM `test`;
Using it at your example:
SELECT SPLIT_STRING(`identifier`,'_',2) as identifier2,* FROM `table`
WHERE `identifier` LIKE '000123_%'
ORDER BY SPLIT_STRING(`identifier`,'_',2) DESC

In this particular answer, you could use RIGHT(identifier,1) to take the rightmost character. Then you would take the MAX() of the right to find the highest. However, if the suffix would end up being 1 or more characters then you would need to find the right regex to capture the characters that come after the last underscore and then take the MAX() of that group.

You can try this query, where you pick the highest value by comparation from all the right side values of the varchar 'identifier'. My code may not work exactly as I don't have a pc here. Good luck!
SELECT *, select RIGHT(identifier,1) as rvalue FROM `table` WHERE `identifier` LIKE '000123_%' and rvalue >= ALL (SELECT RIGHT(identifier,1) as rvalue FROM `table`) t

If it's just for a single group of rows you can apply:
order by char_length(identifier) desc, identifier desc
limit 1
Longer string means larger numbers...

Related

get the maximum number that extracted from a string using mysql

I have a table called Elements
id reference
101 AES/JN/2001
102 AMES/JN/2001
103 AES/JN/2002
104 AES/JN/2003
105 AMES/JN/2002
I want to get the maximum number from the string. If my search key word is AMES/JN I should get 2002. And If my key word is AES/JN then output should be 2003
I have tried the following code:
select max(convert(substring_index(reference,'/', -1), unsigned)) as max
FROM Elements WHERE reference like 'AES/JN/'
I almost agree with Shyam except for that horribly convoluted function.
I recommend this query:
SELECT SUBSTRING_INDEX(reference,'/',-1) as `max`
FROM `Elements`
WHERE reference LIKE 'AES/JN/%'
ORDER BY reference DESC
LIMIT 1
This will output a single row with with 2003 as the value in the max column.
The reason I like this method is because CONVERT() is omitted/unnecessary.
I've compared my query against Xenofexs' on my server and mine is only .0001 seconds faster -- but this is only running on the 5 rows that the OP posted. As the database volume increases, I am confident that my query's performance lead will increase.
Even if you don't care about the micro-optimization, I think this query is easier to read/comprehend because it doesn't have a function inside a function inside a function.
In fact, I believe this next query may outperform my above query:
SELECT SUBSTRING_INDEX(reference,'/',-1) as `max`
FROM `Elements`
WHERE LOCATE('AES/JN/',reference)
ORDER BY reference DESC
LIMIT 1
Because LOCATE() will be checking the leading characters from the reference column, and the targeted substring will not occur later in the string, LOCATE() has been benchmarked to outperform LIKE.
Additional reading:
MySQL LIKE vs LOCATE
For the record, here is the table that I used:
CREATE TABLE `Elements` (
`id` int(10) NOT NULL,
`reference` varchar(100) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `Elements` (`id`, `reference`) VALUES
(101, 'AES/JN/2001'),
(102, 'AMES/JN/2001'),
(103, 'AES/JN/2002'),
(104, 'AES/JN/2003'),
(105, 'AMES/JN/2002');
ALTER TABLE `Elements`
ADD PRIMARY KEY (`id`);
ALTER TABLE `Elements`
MODIFY `id` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=106;
Please check how "LIKE" work's.
You can use % as joker
Just change your query and add % character. And it's work
SELECT
max(
CONVERT (
substring_index(reference, '/', - 1),
UNSIGNED
)
) AS max
FROM
reference
WHERE
reference LIKE 'AES/JN/%'
Please note : LIKE 'AES/JN/%'
Please find below solution.
Query:
select id,reference,digits(reference) as num_values from asasas where reference like 'AMES/JN%' order by num_values DESC limit 1
You need to create one function in mysql
DELIMITER $$
DROP FUNCTION IF EXISTS `test`.`digits`$$
CREATE FUNCTION `digits`( str CHAR(32) ) RETURNS char(32) CHARSET latin1
BEGIN
DECLARE i, len SMALLINT DEFAULT 1;
DECLARE ret CHAR(32) DEFAULT '';
DECLARE c CHAR(1);
IF str IS NULL
THEN
RETURN "";
END IF;
SET len = CHAR_LENGTH( str );
REPEAT
BEGIN
SET c = MID( str, i, 1 );
IF c BETWEEN '0' AND '9' THEN
SET ret=CONCAT(ret,c);
END IF;
SET i = i + 1;
END;
UNTIL i > len END REPEAT;
RETURN ret;
END$$
DELIMITER ;
Let me know if it not works for you
SELECT MAX(Z.COUNT),reference FROM
(
SELECT reference,CAST(SUBSTRING_INDEX(reference, '/', -1) AS DECIMAL) count
FROM Elements where reference like 'AES/JN/%'
)Z
Try above code.
Hope this helps.

Store URL with wildcards in MySQL and return matches

I'm writing a PHP router, which lookups page's id in database with such query
SELECT `id` FROM `pages` WHERE '/about/test' REGEXP `url` ORDER BY `url`
from such table
id type_id url name lookup
1 0 /about/[^/]* NULL NULL
2 1 /about/company NULL NULL
So it gets id 2 on '/about/company' and id 1 on /about/whatever on first row of result
I'm looking for the best way to retrieve the wildcard values from MySQL, e.g. get ID of '/about/[^/]+' entry and 'test' as second argument when passing '/about/test'; and get only ID when passing '/about/company'
Simply, i want to get variadic number of columns, where first column return ID and others - wildcard values in right order
Let me assume that you have a flag that specifies if something is a wildcard pattern or a simple comparison.
If you want all matches, you can do:
SELECT `id`
FROM `pages`
WHERE '/about/test' REGEXP `url` AND url_has_wildcard = 0
UNION ALL
SELECT `id`
FROM `pages`
WHERE '/about/test' REGEXP `url` AND url_has_wildcard = 1 AND
NOT EXISTS (SELECT 1
FROM pages
WHERE '/about/test' REGEXP `url` AND url_has_wildcard = 0
);
You can define such a flag using -- what else -- a regular expression. Say, something like this:
WHERE '/about/test' REGEXP `url` AND
(url REGEXP '^[a-zA-Z0-9/]*$') -- or whatever the valid characters are
If you want only one row returned, you can do:
SELECT `id`
FROM `pages`
WHERE '/about/test' REGEXP `url`
ORDER BY (url REGEXP '^[a-zA-Z0-9/]*$') DESC
LIMIT 1;

MySql sum cell with delimited values [duplicate]

I'm writing a query that selects data from one table into another, one of the columns that needs to be moved is a DECIMAL column. For reasons beyond my control, the source column can sometimes be a comma separated list of numbers. Is there an elegant sql only way to do this?
For example:
source column
10.2
5,2.1
4
Should produce a destination column
10.2
7.1
4
I'm using MySQL 4, btw.
To do this kind of non trivial string manipulations, you need to use stored procedures, which, for MySQL, only appeared 6 years ago, in version 5.0.
MySQL 4 is now very old, the latest version from branch 4.1 was 4.1.25, in 2008. It is not supported anymore. Most Linux distributions don't provide it anymore. It's really time to upgrade.
Here is a solution that works for MySQL 5.0+:
DELIMITER //
CREATE FUNCTION SUM_OF_LIST(s TEXT)
RETURNS DOUBLE
DETERMINISTIC
NO SQL
BEGIN
DECLARE res DOUBLE DEFAULT 0;
WHILE INSTR(s, ",") > 0 DO
SET res = res + SUBSTRING_INDEX(s, ",", 1);
SET s = MID(s, INSTR(s, ",") + 1);
END WHILE;
RETURN res + s;
END //
DELIMITER ;
Example:
mysql> SELECT SUM_OF_LIST("5,2.1") AS Result;
+--------+
| Result |
+--------+
| 7.1 |
+--------+
Here is a mysql function to split a string:
CREATE FUNCTION SPLIT_STR(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '');
And u have to use it this way:
SELECT SPLIT_STR(FIELD, ',', 1) + SPLIT_STR(FIELD, ',', 2) FROM TABLE
Unfortunately mysql does not include string split functions or aggregates, so you will need to do this either in a stored procedure or on the client side.
A number table-based parse approach can be found at this SQLFiddle link. Esentially, once you have the substrings, the sum function will auto-cast the numbers. For convenience:
create table scores (id int primary key auto_increment, valueset varchar(30));
insert into scores (valueset) values ('7,6,8');
insert into scores (valueset) values ('3,2');
create table numbers (n int primary key auto_increment, stuffer varchar(3));
insert into numbers (stuffer) values (NULL);
insert into numbers (stuffer) values (NULL);
insert into numbers (stuffer) values (NULL);
insert into numbers (stuffer) values (NULL);
insert into numbers (stuffer) values (NULL);
SELECT ID, SUM(SCORE) AS SCORE
FROM (
SELECT
S.id
,SUBSTRING_INDEX(SUBSTRING_INDEX(S.valueset, ',', numbers.n),',',-1) score
, Numbers.n
FROM
numbers
JOIN scores S ON CHAR_LENGTH(S.valueset)
-CHAR_LENGTH(REPLACE(S.valueset, ',', ''))>=numbers.n-1
) Z
GROUP BY ID
;

Find first available identifier from database table

The requirement is to find the first available identifier where an identifier is an alphanumeric string, such as:
ABC10000
ABC10345
ABC88942
ABC90123
The database table has a structure such as:
id, user, identifier
Note that the alpha component ABC is consistent throughout and won't change. The numeric component should be between 10000 and 99999.
How best to tackle this? It does not seem like an overly complex problem - looking for simplest solution using either just MySQL or a combination of SQL and PHP. The current solution pulls each record from the database into an array and then loops from 10000 onwards, prepending ABC and checking availability, which seems like it could be improved significantly.
Edit: Original question was not clear enough in that a certain amount of identifiers have been assigned already, and I am looking to fill in the gaps. From the short list I provided, the next available would be ABC10001. Eventually, however, it would be ABC10346 and then ABC88943 and so on
Edit: Sorry for a poorly structured question. To avoid any further confusion, here is the actual table structure:
CREATE TABLE IF NOT EXISTS `User_Loc` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(11) DEFAULT NULL,
`value` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_64FB41DA17323CBC` (`user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=4028 ;
You have to self join the table and look for the first NULL value in the joined table
SELECT CONCAT('ABC', SUBSTRING(t1.value, 4)+1) AS next_value
FROM test t1
LEFT JOIN test t2 on SUBSTRING(t1.value, 4)+1 = SUBSTRING(t2.value, 4)
WHERE ISNULL(t2.value)
ORDER BY t1.value ASC
LIMIT 1
http://sqlfiddle.com/#!2/d69105/22
edit
With the comment about some 'specialities' at ncatnow. There are slight adjusments to make with the help of subselects for ridding the 'ABC' and UNION for having a default value
SELECT
CONCAT('ABC', t1.value+1) AS next_value
FROM
((SELECT '09999' AS value) UNION (SELECT SUBSTRING(value, 4) AS value FROM test)) t1
LEFT JOIN
((SELECT '09999' AS value) UNION (SELECT SUBSTRING(value, 4) AS value FROM test)) t2
ON t1.value+1 = t2.value
WHERE
ISNULL(t2.value)
AND t1.value >= '09999'
ORDER BY t1.value ASC
LIMIT 1
http://sqlfiddle.com/#!2/28acf6/50
Similar to the above reply by #HerrSerker, but this will cope with existing identifiers which have the numeric part starting with a zero.
SELECT CONCAT('ABC',SUBSTRING(CONCAT('00000', CAST((CAST(SUBSTRING(a.identifier, 4) AS SIGNED) + 1) AS CHAR)), -5)) AS NextVal
FROM SomeTable a
LEFT OUTER JOIN SomeTable b
ON b.identifier = CONCAT('ABC',SUBSTRING(CONCAT('00000', CAST((CAST(SUBSTRING(a.identifier, 4) AS SIGNED) + 1) AS CHAR)), -5))
WHERE b.identifier IS NULL
ORDER BY NextVal
LIMIT 1
what comes to my mind is one table with all indentifiers and use this sql
SELECT identifier FROM allIdentifiersTable WHERE identifier NOT IN (SELECT identifier FROM yourTable) LIMIT 1
Reconsidering this from your edit, you should go the PHP route and add another table or other means to store the last filled id:
$identifier = 0;
$i = mysql_query("SELECT identifier FROM last_identifier");
if ($row = mysql_fetch_assoc($i)) $identifier = $row["identifier"];
if ($identifier < 10000) $identifier = 10000;
do {
$identifier += 1;
$result = mysql_query("
INSERT IGNORE INTO table (id, user, identifier)
VALUES ('[...]', '[...]',
'ABC" . str_pad($identifier, 5, "0", STR_PAD_LEFT) . "'
)");
if (mysql_affected_rows($result) < 1) continue;
} while (false);
mysql_query("UPDATE last_identifier SET identifier = '$identifier'");
Of course, you need to add a UNIQUE index on the identifier field.

Escape special characters from the database - MySql

Hi guys I need to search for a data in the database like 123.456.789
how can I search it even if I only entered 123456789 ?
I need to escape the special characters from the database so that even if i search for 123456789 it can also display values like 123.456.789.
Here is my query:
SELECT *
FROM clients
WHERE REPLACE(phone, '.', '') LIKE ".$searchtext."
... where searchtext is the number im looking for. It should return all values that match regardless of whatever special characters are present.
#Kiel
Here is the sample table & query. Please see if this can help you. Not sure about your table structure.
CREATE TABLE `clients` (
`id` int(11) NOT NULL,
`phone` varchar(255) NOT NULL
) ENGINE=InnoDB;
INSERT INTO `test`.`clients` (
`id` ,
`phone`
)
VALUES (
'1', '123.456.789'
), (
'2', '123.456.785'
);
mysql> select * from clients where replace(phone,'.','') = '123456789';
+----+-------------+
| id | phone |
+----+-------------+
| 1 | 123.456.789 |
+----+-------------+
Hope this help !
select phone from table_name
where replace (cast (phone as text) , '.','') like '%123456789%'
You could use MySQL's REPLACE() function:
SELECT * FROM my_table WHERE REPLACE(my_column, '.', '') = '123456789';
But if my_column just contains integers, you should really change it to one of MySQL's integer types.
Replace() is the best function to do this :
Select * from TableName where Replace(ColumnName,'escaping character','replaced character')='your Search option'
for your case escaping character is dot(.), replaced character is '' and search option is '123456789'

Categories