Search for text between delimiters in MySQL - mysql

I am trying to extract a certain part of a column that is between delimiters.
e.g. find foo in the following
test 'esf :foo: bar
So in the above I'd want to return foo, but all the regexp functions only return true|false,
is there a way to do this in MySQL

Here ya go, bud:
SELECT
SUBSTR(column,
LOCATE(':',column)+1,
(CHAR_LENGTH(column) - LOCATE(':',REVERSE(column)) - LOCATE(':',column)))
FROM table
Yea, no clue why you're doing this, but this will do the trick.
By performing a LOCATE, we can find the first ':'. To find the last ':', there's no reverse LOCATE, so we have to do it manually by performing a LOCATE(':', REVERSE(column)).
With the index of the first ':', the number of chars from the last ':' to the end of the string, and the CHAR_LENGTH (don't use LENGTH() for this), we can use a little math to discover the length of the string between the two instances of ':'.
This way we can peform a SUBSTR and dynamically pluck out the characters between the two ':'.
Again, it's gross, but to each his own.

This should work if the two delimiters only appear twice in your column. I am doing something similar...
substring_index(substring_index(column,':',-2),':',1)

A combination of LOCATE and MID would probably do the trick.
If the value "test 'esf :foo: bar" was in the field fooField:
MID( fooField, LOCATE('foo', fooField), 3);

I don't know if you have this kind of authority, but if you have to do queries like this it might be time to renormalize your tables, and have these values in a lookup table.

With only one set of delimeters, the following should work:
SUBSTR(
SUBSTR(fooField,LOCATE(':',fooField)+1),
1,
LOCATE(':',SUBSTR(fooField,LOCATE(':',fooField)+1))-1
)

mid(col,
locate('?m=',col) + char_length('?m='),
locate('&o=',col) - locate('?m=',col) - char_length('?m=')
)
A bit compact form by replacing char_length(.) with the number 3
mid(col, locate('?m=',col) + 3, locate('&o=',col) - locate('?m=',col) - 3)
the patterns I have used are '?m=' and '&o'.

select mid(col from locate(':',col) + 1 for
locate(':',col,locate(':',col)+1)-locate(':',col) - 1 )
from table where col rlike ':.*:';

If you know the position you want to extract from as opposed to what the data itself is:
$colNumber = 2; //2nd position
$sql = "REPLACE(SUBSTRING(SUBSTRING_INDEX(fooField, ':', $colNumber),
LENGTH(SUBSTRING_INDEX(fooField,
':',
$colNumber - 1)) + 1)";

This is what I am extracting from (mainly colon ':' as delimiter but some exceptions), as column theline255 in table loaddata255:
23856.409:0023:trace:message:SPY_EnterMessage (0x2003a) L"{#32769}" [0081] WM_NCCREATE sent from self wp=00000000 lp=0023f0b0
This is the MySql code (It quickly did what I want, and is straight forward):
select
time('2000-01-01 00:00:00' + interval substring_index(theline255, '.', 1) second) as hhmmss
, substring_index(substring_index(theline255, ':', 1), '.', -1) as logMilli
, substring_index(substring_index(theline255, ':', 2), ':', -1) as logTid
, substring_index(substring_index(theline255, ':', 3), ':', -1) as logType
, substring_index(substring_index(theline255, ':', 4), ':', -1) as logArea
, substring_index(substring_index(theline255, ' ', 1), ':', -1) as logFunction
, substring(theline255, length(substring_index(theline255, ' ', 1)) + 2) as logText
from loaddata255
and this is the result:
# LogTime, LogTimeMilli, LogTid, LogType, LogArea, LogFunction, LogText
'06:37:36', '409', '0023', 'trace', 'message', 'SPY_EnterMessage', '(0x2003a) L\"{#32769}\" [0081] WM_NCCREATE sent from self wp=00000000 lp=0023f0b0'

This one looks elegant to me. Strip all after n-th separator, rotate string, strip everything after 1. separator, rotate back.
select
reverse(
substring_index(
reverse(substring_index(str,separator,substrindex)),
separator,
1)
);
For example:
select
reverse(
substring_index(
reverse(substring_index('www.mysql.com','.',2)),
'.',
1
)
);

you can use the substring / locate function in 1 command
here is a mice tutorial:
http://infofreund.de/mysql-select-substring-2-different-delimiters/
The command as describes their should look for u:
**SELECT substr(text,Locate(' :', text )+2,Locate(': ', text )-(Locate(' :', text )+2)) FROM testtable**
where text is the textfield which contains "test 'esf :foo: bar"
So foo can be fooooo or fo - the length doesnt matter :).

Related

substring_index skips delimiter from right

I have a table 'car_purchases' with a 'description' column. The column is a string that includes first name initial followed by full stop, space and last name.
An example of the Description column is
'Car purchased by J. Blow'
I am using 'substring_index' function to extract the letter preceding the '.' in the column string. Like so:
SELECT
Description,
SUBSTRING_INDEX(Description, '.', 1) as TrimInitial,
SUBSTRING_INDEX(
SUBSTRING_INDEX(Description, '.', 1),' ', -1) as trimmed,
length(SUBSTRING_INDEX(
SUBSTRING_INDEX(Description, '.', 1),' ', -1)) as length
from car_purchases;
I will call this query 1.
picture of the result set (Result 1) is as follows
As you can see the problem is that the 'trimmed' column in the select statement starts counting the 2nd delimiter ' ' instead of the first from the right and produces the result 'by J' instead of just 'J'. Further the length column indicates that the string length is 5 instead of 4 so WTF?
However when I perform the following select statement;
select SUBSTRING_INDEX(
SUBSTRING_INDEX('Car purchased by J. Blow', '.', 1),' ', -1); -- query 2
Result = 'J' as 'Result 2'.
As you can see from result 1 the string in column 'Description' is exactly (as far as I can tell) the same as the string from 'Result 2'. But when the substring_index is performed on the column (instead of just the string itself) the result ignores the first delimiter and selects a string from the 2nd delimiter from the right of the string.
I've racked my brains over this and have tried 'by ' and ' by' as delimiters but both options do not produce the desired result of a single character. I do not want to add further complexity to query 1 by using a trim function. I've also tried the cast function on result column 'trimmed' but still no success. I do not want to concat it either.
There is an anomaly in the 'length' column of query 1 where if I change the length function to char_length function like so:
select length(SUBSTRING_INDEX(
SUBSTRING_INDEX(Description, '.', 1),' ', -1)) as length -- result = 5
select char_length(SUBSTRING_INDEX(
SUBSTRING_INDEX(Description, '.', 1),' ', -1)) as length -- result = 4
Can anyone please explain to me why the above select statement would produce 2 different results? I think this is the reason why I am not getting my desired result.
But just to be clear my desired outcome is to get 'J' not 'by J'.
I guess I could try reverse but I dont think this is an acceptable compromise. Also I am not familiar with collation and charset principles except that I just use the defaults.
Cheers Players!!!!
CHAR_LENGTH returns length in characters, so a string with 4 2-byte characters would return 4. LENGTH however returns length in bytes, so a string with 4 2-byte characters would return 8. The discrepancy in your results (including SUBSTRING_INDEX) says that the "space" between by and J is not actually a single-byte space (ASCII 0x20) but a 2-byte character that looks like a space. To workaround this, you could try replacing all unicode characters with spaces using CONVERT and REPLACE. In this example, I have an en-space unicode character in the string between by and J. The CONVERT changes that to a ?, and the REPLACE then converts that to a space:
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX("Car purchased by J. Blow", '.', 1),' ', -1)
Output:
by J
With CONVERT and REPLACE:
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(REPLACE(CONVERT("Car purchased by J. Blow" USING ASCII), '?', ' '), '.', 1),' ', -1)
Output
J
For your query, you would replace the string with your column name i.e.
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(REPLACE(CONVERT(description USING ASCII), '?', ' '), '.', 1),' ', -1)
Demo on DBFiddle

Multiplying a range in MySQL

I have store weight in lbs in my column which I need to convert to KG. I was able to do it with a simple query:
SELECT ( weight * 0.45 ) as weight from TABLE
However, this doesn't work values which are stored as a range, ex. '200 - 300'. If I use the same query it returns 440.925 instead of 440-661. I understand this is happening because I'm multiplying string but is there a way I can multiple a range value (200-300) to get the desired result.
If not, how should I convert this range ideally?
SUBSTRING_INDEX is useful here:
SELECT IF(
INSTR(weight, '-') > 0, /* Does weight contain a dash? */
CONCAT( /* Yes? Multiply each, and return them: */
SUBSTRING_INDEX(weight, '-', 1) * 0.45,
' - ',
SUBSTRING_INDEX(weight, '-', -1) * 0.45
),
weight * 0.45 /* No? Just simply multiply the weight */
) AS `range`
FROM test
First, we check to see if weight contains a dash, and if it does, split it up, and multiply them out individually.
See an example here: https://www.db-fiddle.com/f/iELvWDjpVGBZEkpdR4jtsK/0
If i understand your question correctly you need to do something like below.
SELECT
CONCAT (
SUBSTRING_INDEX(SUBSTRING_INDEX(column, ' - ', 1), ' - ', -1) * 0.45
, ' - '
, SUBSTRING_INDEX(SUBSTRING_INDEX(column, ' - ', 2), ' - ', -1) * 0.45
)
FROM
table
1) Nesting SUBSTRING_INDEX functions makes it possible to extract items from a string.
2) CAST function is not needed because of MySQL autotype cast.
Using CAST, and SUBSTRING_INDEX functions.
CAST will ensure that even if there are more spaces on either side of the '-' (hyphen) character, it will still convert it into a valid number. You can change the DECIMAL(10,4) to any precision, as per your application requirements.
SUBSTRING_INDEX will find the substring before the occurence of '-' (hyphen) character.
Try this:
SELECT
CONCAT(CAST(SUBSTRING_INDEX(weight, '-', 1) AS DECIMAL(10,4)) * 0.45,
'-',
CAST(SUBSTRING_INDEX(weight, '-', -1) AS DECIMAL(10,4)) * 0.45)
AS weight
FROM table

MySQL: Extract regexp value from query

I would need to get value from given regexp.
For example:
> :"postalCode";s:4:"3150";
Is there any way I can extract 3150, from this part of column value. Column value stored serialized objects, so postalCode variable can be null type, that way I should check if positive integer follows ;s:POSITIVE_INT:"postalCodeValue
Use SUBSTRING_INDEX:
SELECT
SUBSTRING(SUBSTRING_INDEX(col, '"', -2), 1,
INSTR(SUBSTRING_INDEX(col, '"', -2), '"') - 1) AS num
FROM yourTable;
This query will extract the last quoted number in your string.
Demo
avoiding regexp you could use some string function eg:
SELECT LENGTH(':"postalCode";s:4:"3150"') - LOCATE(':', REVERSE(':"postalCode";s:4:"3150"'))+1
from dual ;
or
SELECT LENGTH(col_name) - LOCATE(':', REVERSE(col_name))+1
from my_table ;
It also work with 2 times SUBSTRING_INDEX
SELECT
SUBSTRING_INDEX (SUBSTRING_INDEX( ':"postalCode";s:4:"3150";', '"',-2) , '"', 1);

Need help formatting CONCAT() for MySQL query

I have a table where I am attempting to take 3 database table values and reformat them in a single value. Here is the SQL statement that I have at the moment:
SELECT
CASE WHEN cb_cardtype = 'Discover Credit Card'
THEN 'DS'
END +
';' + RIGHT(cardnumbers,4) + ';' + LPAD(MONTH(planexpdate), 2, '0') +
'/' + LPAD(YEAR(planexpdate), 2, '0') AS account_billing_key
FROM my_table
So what I wanted to get as an output here would be:
DS;4242;07/14
The problem is that I am using the + to attempt this, which actually adds the values together. Rather, I understand that I need to use CONCAT() to merge the values. I am unclear about how I can pull the individual values and then concatenate them as desired.
If your query is otherwise correct, all you need to do is to wrap all the strings you want to concatenate - comma separated - inside a call to CONCAT;
SELECT
CONCAT(
CASE WHEN cb_cardtype = 'Discover Credit Card' THEN 'DS' END,
';',
RIGHT(cardnumbers,4),
';',
LPAD(MONTH(planexpdate), 2, '0'),
'/',
LPAD(YEAR(planexpdate), 2, '0')
) AS account_billing_key
FROM my_table

Convert a Negative Number with Parentheses to a Minus in MYSQL

I have a MYSQL database with Negative numbers that are enclosed in parenthesis
eg. (14,500) which is supposed to be -14500.
I am storing the numbers as varchar. I am trying to convert all the numbers to a double or float format and also format the negative numbers with a minus sign.
My code:
select case
when substr(gross_d,1,1) = '(' then
ltrim('(') and rtrim(')') *-1
else
(gross_d)
end gross_d_num
from buy;
convert(gross_d_num,Double);
The problem with my current method is all the negative numbers with the parenthesis are converted to zero. Is there a different method to get my result.
edit:
I also removed the *-1 to see if the Parenthesis is removed and I get a value of zero.
Something like
convert (
case
when gross_d LIKE '(%)' THEN CONCAT('-', REPLACE(REPLACE(gross_d, ')', ''), '(', ''))
else gross_d
end, decimal(19,6))
Here, you are trimming parenthesis only. This becomes zero when you multiply by -1
ltrim('(') and rtrim(')') *-1
CONVERT(
IF( gross_d LIKE '(%)'
,CONCAT( '-', SUBSTR( gross_d, 1, LENGTH( gross_d ) - 2 ) )
,gross_d )
,DECIMAL );
At our company we don't have control over currency formatting used by external parties uploading excel sheets. We currently use this to convert the currencies and add a case whenever something new shows up :
SET #netSale := '$ (154.00)';
SELECT CONVERT (
CASE
when #netSale LIKE '$ (%)' THEN CONCAT('-', REPLACE(REPLACE(REPLACE(#netSale, '$ ', ''), ')', ''), '(', ''))
when #netSale LIKE '(%)' THEN CONCAT('-', REPLACE(REPLACE(REPLACE(#netSale, '$ ', ''), ')', ''), '(', ''))
else REPLACE(REPLACE(#netSale,'$',''),',', '')
END, DECIMAL(10,2)
)
This deals with most formatting styles we have encountered and is especially useful when loading a converted CSV file to a table.