MySQL inverse substr() - mysql

select now() will output 2012-11-20 09:05:38.
select substr(now(), locate(' ', now())) will output 09:05:38.
How can I obtain this result: 2012-11-20 instead? Been there, done that but can't achieve it yet. Forgive me for my lacking but please help me. Thanks!

Not an specific answer to your question about substrings, but if I wanted the DATE part of NOW() I would do:
SELECT DATE(NOW());
Or for the time portion
SELECT TIME(NOW());
For actual string purposes though you can use:
SUBSTR('SOME STRING', 0, LOCATE(' ', 'SOME STRING')) <- returns from position zero for a length of 4 characters
Or
SUBSTRING_INDEX('SOME STRING', ' ', 1) <- gives 'SOME'
SUBSTRING_INDEX('SOME STRING', ' ', -1) <- gives 'STRING'

you can use DATE_FORMAT
SELECT DATE_FORMAT('2012-11-20 09:05:38', '%Y-%m-%d')
or just DATE
SELECT DATE('2012-11-20 09:05:38')
SQLFiddle Demo (both queries)

Related

How to extract a specific word after # and before it ends? MySQL

So I have a table posts and I need to extract specific tags from the column description.
I need to extract f.e. #sunny and #lovelyday and print it out separately from the full text description. For example:
description: "today was a good day! #sunny #lovelyday" -> extract #sunny & #lovelyday
I searched for answers, but perhaps I do not understand it. (still learning)
PhpMyAdmin doesnt support, charindex() or len() -> so I tried locate() and length() but my query isn't correct.
SELECT SUBSTRING(description, locate("#",description) + length("#")) AS Tags
from posts
What am I doing wrong?
Edit: I need the "#%" from textfield and extract it
SET #string := 'today was a good day! #sunny #lovelyday';
WITH RECURSIVE
cte AS ( SELECT 0 n
UNION ALL
SELECT n + 1
FROM cte
WHERE n < (SELECT LENGTH(#string) - LENGTH(REPLACE(#string, '#', ''))))
SELECT n, SUBSTRING_INDEX(SUBSTRING_INDEX(#string, '#', -n), ' ', 1)
FROM cte
WHERE n;
n | SUBSTRING_INDEX(SUBSTRING_INDEX(#string, '#', -n), ' ', 1)
-: | :---------------------------------------------------------
1 | lovelyday
2 | sunny
db<>fiddle here

Mysql combine columns if not null

I know that I can combine multiple columns in mysql by doing:
SELECT CONCAT(zipcode, ' - ', city, ', ', state) FROM Table;
However, if one of the fields is NULL then the entire value will be NULL. So to prevent this from happening I am doing:
SELECT CONCAT(zipcode, ' - ', COALESE(city,''), ', ', COALESCE(state,'')) FROM Table;
However, there still can be situation where the result will look something like this:
zipcode-, ,
Is there a way in MySQL to only have to comma and the hyphen if the next columns are not NULL?
There is actually a native function that will do this called Concat with Separator (concat_ws)!
Specifically, it seems that what you would need is:
SELECT CONCAT_WS(' - ',zipcode, NULLIF(CONCAT_WS(', ',city,state),'')) FROM TABLE;
This should account for all of the null cases you allude to.
However, it is important to note that a blank string ('') is different than a NULL. If you want to address this in the state/city logic you would add a second NULLIF check inside the second CONCAT_ws for the case where a city or a state would be blank strings. This will depend on the database's regard for the blank field and whether you are entering true NULLS into your database or checking the integrity of the blank data before you use it. Something like the following might be slightly more robust:
SELECT CONCAT_WS(' - ', zipcode, NULLIF(CONCAT_WS(', ', NULLIF(city, ''), NULLIF(state, '')), '')) FROM TABLE;
For more, check out the native documentation on concat_ws() here:
https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_concat-ws
I think you need something like this
Set #zipcode := '12345';
Set #city := NULL;
Set #state := NULL;
SELECT CONCAT(#zipcode, ' - ', COALESCE(#city,''), ', ', COALESCE(#state,''));
Result: 12345 - ,
SELECT CONCAT(#zipcode, IF(#city is NULL,'', CONCAT(' - ', #city)), IF(#state is NULL,'', CONCAT(', ',#state)))
Result 12345
You need to make the output of the separators conditional on the following values being non-NULL. For example:
SELECT CONCAT(zipcode,
CASE WHEN city IS NOT NULL OR state IS NOT NULL THEN ' - '
ELSE ''
END,
COALESCE(city, ''),
CASE WHEN city IS NOT NULL AND state IS NOT NULL THEN ', '
ELSE ''
END,
COALESCE(state, '')
) AS address
FROM `Table``
Output (for my demo)
address
12345 - San Francisco, CA
67890 - Los Angeles
34567 - AL
87654
Demo on dbfiddle

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

mySQL extract number or string depending on value

Please take a look at a table below:
I need "the last part" of "what I have" to be number if it's all number and string if it contains character.
In Excel I've achieved this with the following function (as shown above):
=IFERROR(VALUE(TEXT(D2;"0"));TEXT(D2;"0"))
However I want to do this in mySQL in order to compute more effectively.
I've "floated" somwhere around CASE with CAST or CONVERT and also TRIM functions, but I haven't been able to put up something sensical.
A nice "bonus" would be to extract "the part part" by looking for the last "" character (so first "" from right of the string) but no idea at all how to achieve that.
Use SUBSTRING_INDEX.
Query
SELECT
CASE
WHEN SUBSTRING_INDEX(records.data, '_', -1) > 1 # is int check '00004949' returns 1
THEN SUBSTRING_INDEX(records.data, '_', -1) + 0 # converts '00004949' to 4949
ELSE SUBSTRING_INDEX(records.data, '_', -1)
END
AS word
FROM (
SELECT "TRA_PL_NWL_EMA_NWLY_DAI_000_20170610_IN1_01P002bc" AS DATA
UNION
SELECT "TRA_PL_NWL_EMA_NWLY_DAI_000_2017_0909_JET_00004949" AS DATA
) records
Result
word
----------
01P002bc
4949
Following query will somewhat achieve the task:
SELECT
case
when SUBSTRING_INDEX(value, "_", -1) REGEXP('(^[0-9]+$)')
then Trim(Leading 0 from SUBSTRING_INDEX(value, "_", -1))
else SUBSTRING_INDEX(value, "_", -1)
end as Value
From yourtable;
Click here for Demo
Hope it helps!
I hope this is what u want.
At least it does what you have asked for.
SELECT
CASE
WHEN (
CONVERT(
substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
, signed integer
)
) = 0
THEN substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
ELSE CONVERT(
substring(
txt,
LENGTH(txt) - LOCATE('_', REVERSE(txt))+2,
length(txt)
)
, signed integer
)
END as NUMBER
from test.test
This is my test Table and result of SQL:
txt NUMBER
DA_DA_ADAD_ADAD_ADAD_asd123 asd123
DA_DA_ADAD_ADAD_ADAD_000123 123
DA_DA_ADAD_ADAD_ADAD_444 444
DA_DA_ADAD_ADAD_ADAD_bsd123 bsd123
DA_DA_ADAD_ADAD_ADAD_csd123 csd123
DA_DA_ADAD_ADAD_ADAD_dsd123 dsd123

Search for text between delimiters in 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 :).