How should I sort my SELECT ?
EXAMPLE LIST
1, 2, 2A, 4, 10, 10A
SQL
$query = "SELECT * FROM table WHERE column1 = '$var' ORDER BY length(column2), column2";
If I ORDER BY length() like my example, 2A will end up in the bottom.
If I remove length(), 10 comes after 1.
How do I sort so it appears as above?
Like this:
$query = "SELECT * FROM table WHERE column1 = '$var' ORDER BY CAST(column2 as UNSIGNED), column2";
order by hex is best way to do this:
$query = "SELECT *, cast(hex(column2 as unsigned) as l FROM table WHERE column1 = '$var' ORDER BY l";
With a custom function such as alphas found here, you can order it by first the number portion of the value followed by the string portion of the value.
If you define and populate a table as such:
CREATE TABLE test (t VARCHAR(255));
INSERT INTO test VALUES
('10A'),
('2'),
('2A'),
('4'),
('10'),
('1');
Then create a custom function called alphas which extracts the string portion (no numbers):
DELIMITER |
DROP FUNCTION IF EXISTS alphas;
CREATE FUNCTION alphas( str CHAR(32) ) RETURNS CHAR(16)
BEGIN
DECLARE i, len SMALLINT DEFAULT 1;
DECLARE ret CHAR(32) DEFAULT '';
DECLARE c CHAR(1);
SET len = CHAR_LENGTH( str );
REPEAT
BEGIN
SET c = MID( str, i, 1 );
IF c REGEXP '[[:alpha:]]' THEN
SET ret=CONCAT(ret,c);
END IF;
SET i = i + 1;
END;
UNTIL i > len END REPEAT;
RETURN ret;
END |
DELIMITER ;
Then you can do a ordered query like this:
SELECT t FROM test ORDER BY CAST(t AS UNSIGNED), alphas(t);
The CAST function converts strings like 10A to an unsigned number of 10.
Related
I'm trying to figure out how to create a single MySQL query that will allow me to display only the next 10 characters following the string "filter" in the Message field. The string "filter" appears at various positions in each record, so I can't use a position filter.
I've been trying to use something like like what I have below, however I've been unable to get the correct query.
SELECT RIGHT(Message,LOCATE('filter',Message) - 10) FROM table
The Message field records within the table looks like:
QgySSW8fwD25iQ.filter0019p3las1-31205-59C3D
6t2fJw.filter0010p3las1-9745-59
filter0025p3las1-13130-59C3D317
And I'm looking for them to look like this after the query:
0019p3las1
0010p3las1
0025p3las1
Any help is greatly appreciated.
Use a combination of LOCATE() within SUBSTRING(). See this SQL Fiddle
CREATE TABLE Table1
(`message` varchar(200))
;
INSERT INTO Table1
(`message`)
VALUES
('QgySSW8fwD25iQ.filter0019p3las1-31205-59C3D'),
('6t2fJw.filter0010p3las1-9745-59'),
('filter0025p3las1-13130-59C3D317')
;
Query 1:
select
SUBSTRING(message,LOCATE('filter',Message)+6,10)
from table1
Note that the +6 is to offset for the length of "filter" because LOCATE finds the position of the "f" and you then need to add 6 for the other characters "ilter". Once that number is determined then just get the next 10 characters.
Results:
| SUBSTRING(message,LOCATE('filter',Message)+6,10) |
|--------------------------------------------------|
| 0019p3las1 |
| 0010p3las1 |
| 0025p3las1 |
See SQLFiddle.
Result table structure
Create table resulttbl (
id int(6) primary key auto_increment ,
resultFIlter varchar(1000)
);
Function to split string
CREATE FUNCTION strSplit(x VARCHAR(65000), delim VARCHAR(12), pos INTEGER)
RETURNS VARCHAR(65000)
BEGIN
DECLARE output VARCHAR(65000);
SET output = REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos)
, LENGTH(SUBSTRING_INDEX(x, delim, pos - 1)) + 1)
, delim
, '');
IF output = '' THEN SET output = null; END IF;
RETURN output;
END;
Stored procedure to split and insert into result table
CREATE PROCEDURE FilterTable()
BEGIN
DECLARE i INTEGER;
DECLARE endpos INTEGER;
DECLARE fullstr VARCHAR(1000);
DECLARE result VARCHAR(1000);
SET fullstr = 'QgySSW8fwD25iQ.filter0019p3las1-31205-59C3D 6t2fJw.filter0010p3las1-9745-59 filter0025p3las1-13130-59C3D317';
SET i = 2;
SET endpos=LENGTH(fullstr) - LENGTH(REPLACE(fullstr, 'filter', '')) ;
delete from resulttbl;
REPEAT
SET result=strSplit(fullstr, 'filter', i);
IF result IS NOT NULL THEN
SET result=LEFT(result,10);
INSERT INTO resulttbl (resultFIlter) values(result);
END IF;
SET i = i + 1;
UNTIL i >= endpos
END REPEAT;
END ;
Call the procedure using the statement CALL FilterTable().
Now the result of your procedure is available on the table resulttbl.
You can get the values from that table using select statement as SELECT * from resulttbl.
Result
id resultFIlter
1 0019p3las1
2 0010p3las1
3 0025p3las1
I have a table named as 'Costdetails'.
There is a column named as 'cost', it is a VARCHAR column. it can be anything, as given below.
Cost ssss
20000 - $
Rs - 1000/-
10000 Rupees etc.
I want to take out exact amount (Ex: 1000, 20000) From this varchar column.
Tried on google about this and i got a query, and then i tried this query.
SELECT cost
FROM Costdetails
WHERE (cost REGEXP '^[0-9]' or cost REGEXP '[0-9]^' or cost REGEXP '[0-9]');
Output :
Rs-1000/-
10000 - $
$ 10000
This query helps me to fetch the rows which is having integer values.
But want to remove the extra stuff from the column (Output: Like 1000, Not like Rs-1000/-).
Any idea, Thanks in advance!!!.
You can use a function to complete your query . For example :-
SET GLOBAL log_bin_trust_function_creators=1;
DROP FUNCTION IF EXISTS digits;
DELIMITER |
CREATE FUNCTION digits( str CHAR(32) ) RETURNS CHAR(32)
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 ;
SELECT digits(cost) from Costdetails;
You can use the cast function for this, as follows :-
SELECT cast('1000/-' as UNSIGNED)
I am trying to run a query that can find all the records from a field contains the letters.
For example suppose a state field contains a record value "New York" and another record conatains NY. Now i am searching for NY or New york will return these 2 records. What will be the query.
Currently i am using
like %New York%" or "%NY%"
Any suggestion
No your query is not correct as it searches for anything containing New York or NY.
So if there is PENNY that will be matched although it shouldn't be....
Your query must be something like this.
SELECT * from TABLE where field in ('NEW YORK','NY')
Now to fetch acronym,you can use
delimiter $$
drop function if exists `initials`$$
CREATE FUNCTION `initials`(str text, expr text) RETURNS text CHARSET utf8
begin
declare result text default '';
declare buffer text default '';
declare i int default 1;
if(str is null) then
return null;
end if;
set buffer = trim(str);
while i <= length(buffer) do
if substr(buffer, i, 1) regexp expr then
set result = concat( result, substr( buffer, i, 1 ));
set i = i + 1;
while i <= length( buffer ) and substr(buffer, i, 1) regexp expr do
set i = i + 1;
end while;
while i <= length( buffer ) and substr(buffer, i, 1) not regexp expr do
set i = i + 1;
end while;
else
set i = i + 1;
end if;
end while;
return result;
end$$
drop function if exists `acronym`$$
CREATE FUNCTION `acronym`(str text) RETURNS text CHARSET utf8
begin
declare result text default '';
set result = initials( str, '[[:alnum:]]' );
return result;
end$$
delimiter ;
So,your final query will be something like this.
SELECT * from TABLE where field in ('NEW YORK',select acronym('Come Again? That Cant Help!'))
Source:- Mysql extract first letter of each word in a specific column
Presumably, the logic that you want is:
col like '%New York%' or col like '%NY%'
or, if you want to use regular expressions:
col regexp 'New York|NY'
Select * from table where col like '%N' or col like '%n'
I wrote a function to replace first occurence in MySQL Text colum, but it's a little bit complicated...
UPDATE
table_name
SET
column=CONCAT(
LEFT(column,LOCATE('some string', column)-1),
CONCAT(substring(column, LOCATE('some string', column) + $length),
'new string'))
Where $length is length of string, that we want to replace. If we use php it is strlen() function but in MySQL it would be CHAR_LENGTH() function.
Do you know better way to replace only first match in text columns ?
You could use TRIM:
UPDATE table_name SET column = TRIM(LEADING 'some string' FROM column);
assuming 'some string' does not have more than 1 consecutive occurrence at the start of the contents of 'column'.
So, it would work if column contained:
"some string foo some string"
but not for:
"some string some string foo some string"
Edit - Added MySQL function to simplify process
I can't see an alternative to the mechanism you are using, but executing it could be simplified by creating a function in MySQL (if you have the privilege):
delimiter $$
create function replace_first(
p_text_to_search varchar(255),
p_text_to_replace varchar(255)
)
returns varchar(255)
begin
declare v_found_pos int(11);
declare v_found_len int(11);
declare v_text_with_replacement varchar(255);
select locate(p_text_to_replace, p_text_to_search)
into v_found_pos;
select char_length(p_text_to_replace)
into v_found_len;
select concat(
left(p_text_to_search, v_found_pos-1),
mid(p_text_to_search, (v_found_pos + v_found_len))
)
into v_text_with_replacement;
return v_text_with_replacement;
end$$
delimiter ;
then you can call it using:
select replace_first('bar foo foo baz foo', 'foo');
result:
'bar foo baz'
I have created function can replace any index of text:
/************** REPLACE_TEXT_OF_INDEX ***************/
DROP FUNCTION IF EXISTS REPLACE_TEXT_OF_INDEX;
DELIMITER $$
CREATE FUNCTION REPLACE_TEXT_OF_INDEX(_text VARCHAR(3072), _subText VARCHAR(1024), _toReplaceText VARCHAR(1024), _index INT UNSIGNED)
RETURNS VARCHAR(3072)
BEGIN
DECLARE _prefixText, _sufixText VARCHAR(3072);
DECLARE _starIndex INT;
DECLARE _loopIndex, _textIndex INT UNSIGNED DEFAULT 0;
IF _text IS NOT NULL AND LENGTH(_text) > 0 AND
_subText IS NOT NULL AND LENGTH(_subText) > 0 AND
_toReplaceText IS NOT NULL AND _index > 0 THEN
WHILE _loopIndex < LENGTH(_text) AND _textIndex < _index DO
SELECT LOCATE(_subText, _text, _loopIndex + 1) INTO _loopIndex;
IF _loopIndex > 0 THEN
SET _textIndex = _textIndex + 1;
ELSE
SET _loopIndex = LENGTH(_text) + 1;
END IF;
END WHILE;
IF _textIndex = _index THEN
SELECT LOCATE(_subText, _text, _loopIndex) INTO _starIndex;
SELECT SUBSTRING(_text, 1, _starIndex -1) INTO _prefixText;
SELECT SUBSTRING(_text, _starIndex + LENGTH(_subText), LENGTH(_text)) INTO _sufixText;
RETURN CONCAT(_prefixText, _toReplaceText, _sufixText);
END IF;
END IF;
RETURN _text;
END;
$$
DELIMITER ;
SELECT REPLACE_TEXT_OF_INDEX('WORD1 WORD2 WORD3 WORD4 WORD5', 'WORD', '*',1);
I have a table with two columns: price (int) and price_display (varchar).
price is the actual numerical price, e.g. "9990"
price_display is the visual representation, e.g. "$9.99" or "9.99Fr"
I've been able to confirm the two columns match via regexp:
price_display not regexp
format(price/1000, 2)
But in the case of a mismatch, I want to extract the value from the price_display column and set it into the price column, all within the context of an update statement. I've not been able to figure out how.
Thanks.
This function does the job of only returning the digits 0-9 from the string, which does the job nicely to solve your issue, regardless of what prefixes or postfixes you have.
http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#815
Copied here for reference:
SET GLOBAL log_bin_trust_function_creators=1;
DROP FUNCTION IF EXISTS digits;
DELIMITER |
CREATE FUNCTION digits( str CHAR(32) ) RETURNS CHAR(32)
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 ;
SELECT digits('$10.00Fr');
#returns 1000
One approach would be to use REPLACE() function:
UPDATE my_table
SET price = replace(replace(replace(price_display,'Fr',''),'$',''),'.','')
WHERE price_display not regexp format(price/1000, 2);
This works for the examples data you gave:
'$9.99'
'9.99Fr'
Both result in 999 in my test. With an update like this, it's important to be sure to back up the database first, and be cognizant of the formats of the items. You can see all the "baddies" by doing this query:
SELECT DISTINCT price_display
FROM my_table
WHERE price_display not regexp format(price/1000, 2)
ORDER BY price_display;
For me CASTING the field did the trick:
CAST( price AS UNSIGNED ) // For positive integer
CAST( price AS SIGNED ) // For negative and positive integer
IF(CAST(price AS UNSIGNED)=0,REVERSE(CAST(REVERSE(price) AS UNSIGNED)),CAST(price AS UNSIGNED)) // Fix when price starts with something else then a digit
For more details see:
https://dev.mysql.com/doc/refman/5.0/en/cast-functions.html
This is a "coding horror", relational database schemas should NOT be written like this!
Your having to write complex and unnecessary code to validate the data.
Try something like this:
SELECT CONCAT('$',(price/1000)) AS Price FROM ...
In addition, you can use a float, double or real instead of a integer.
If you need to store currency data, you might consider adding a currency field or use the systems locale functions to display it in the correct format.
I create a procedure that detect the first number in a string and return this, if not return 0.
DROP FUNCTION IF EXISTS extractNumber;
DELIMITER //
CREATE FUNCTION extractNumber (string1 VARCHAR(255)) RETURNS INT(11)
BEGIN
DECLARE position, result, longitude INT(11) DEFAULT 0;
DECLARE string2 VARCHAR(255);
SET longitude = LENGTH(string1);
SET result = CONVERT(string1, SIGNED);
IF result = 0 THEN
IF string1 REGEXP('[0-9]') THEN
SET position = 2;
checkString:WHILE position <= longitude DO
SET string2 = SUBSTR(string1 FROM position);
IF CONVERT(string2, SIGNED) != 0 THEN
SET result = CONVERT(string2, SIGNED);
LEAVE checkString;
END IF;
SET position = position + 1;
END WHILE;
END IF;
END IF;
RETURN result;
END //
DELIMITER ;
Return last number from the string:
CREATE FUNCTION getLastNumber(str VARCHAR(255)) RETURNS INT(11)
DELIMETER //
BEGIN
DECLARE last_number, str_length, position INT(11) DEFAULT 0;
DECLARE temp_char VARCHAR(1);
DECLARE temp_char_before VARCHAR(1);
IF str IS NULL THEN
RETURN -1;
END IF;
SET str_length = LENGTH(str);
WHILE position <= str_length DO
SET temp_char = MID(str, position, 1);
IF position > 0 THEN
SET temp_char_before = MID(str, position - 1, 1);
END IF;
IF temp_char BETWEEN '0' AND '9' THEN
SET last_number = last_number * 10 + temp_char;
END IF;
IF (temp_char_before NOT BETWEEN '0' AND '9') AND
(temp_char BETWEEN '0' AND '9') THEN
SET last_number = temp_char;
END IF;
SET position = position + 1;
END WHILE;
RETURN last_number;
END//
DELIMETER;
Then call this functions:
select getLastNumber("ssss111www222w");
print 222
select getLastNumber("ssss111www222www3332");
print 3332