Is it possible to split a word into separate lines? All the examples I found were using something to refer to as a comma or something, but I would like to separate each letter from a word, eg:
from (my table):
id
name
1
banana
to: SELECT ...
id
letter
1
b
1
a
1
n
1
a
1
n
1
a
One option is doing it with a recursive query, using the following two steps:
base step: get the letter in position 1
recursive step: get nth letter, using LEFT(RIGHT(1), n), which extracts the letter in position n.
Recursion is halted when the nth extracting element is higher than then length of the string.
WITH RECURSIVE cte AS (
SELECT id, name,
1 AS idx,
RIGHT(LEFT(name, 1),1) AS letter
FROM tab
UNION ALL
SELECT id, name,
idx + 1 AS idx,
RIGHT(LEFT(name, idx+1), 1) AS letter
FROM cte
WHERE idx < LENGTH(name)
)
SELECT id, letter FROM cte
Output:
id
letter
1
b
1
a
1
n
1
a
1
n
1
a
Check the demo here.
A simple way would be to join with a numbers table:
with n as (
select * from (values row(1),row(2),row(3),row(4),row(5),row(6),row(7),row(8),row(9))x(num)
)
select t.id, Substring(name, n.num, 1)
from t
join n on n.num <= Length(t.name);
DB Fiddle
An other way more performant is by using REGEXP_REPLACE, json_array and json_table
REGEXP_REPLACE to convert banana to b,n,a,n,a
json_array to create a json array from b,n,a,n,a
json_table will convert JSON data to tabular data.
with cte as (
select id, REGEXP_REPLACE(name, "(.)(?!$)", "$1,") as name
from _table
)
select cte.id, t.name
from cte
join json_table(
replace(json_array(cte.name), ',', '","'),
'$[*]' columns (name varchar(50) path '$')
) t;
Demo here
Related
how to split comma separated string from one column and turn it into several columns?
this is my table:
SELECT id,lik FROM `tbl_users_posts` WHERE id=1;
id lik
-------------
1 10,11,12,13,14,15
how can i split 'lik' column and get this result?
id lik
-------------
1 10
1 11
1 12
1 13
1 14
1 15
displays id 1 in the first row and split the 'lik' column into pieces in the second row and displays it one by one
Unfortunately MySQL doesn't have a split string functions. One way is create a temporary table as following with the max values of the largest row:
create temporary table numbers as (
select 1 as n
union select 2 as n
union select 3 as n
union select 4 as n
union select 5 as n
union select 6 as n
union select 7 as n
union select 8 as n
);
Then you can use substring_index to accomplish the desired result
select id,
substring_index( substring_index(lik, ',', n),',', -1) as lik
from tbl_users_posts
join numbers on char_length(lik) - char_length(replace(lik, ',', '')) >= n - 1
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=84bc1b4e60a7feea5af0d0b568bc7bcb
Edit.
Another method if you have MySQL 8+ for the lik string to split into thousands of pieces without a loop is create an temporary table using recursive cte as follows:
CREATE TEMPORARY TABLE numbers WITH RECURSIVE cte AS
( select 1 as n
union all
select n +1
from cte
limit 1000
)
SELECT * FROM cte;
And then use the same query as above:
select id,
substring_index( substring_index(lik, ',', n),',', -1) as lik
from tbl_users_posts
join numbers on char_length(lik) - char_length(replace(lik, ',', '')) >= n - 1
order by id asc;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=9231202418ce9b17aef8609ad6875fbe
If the lik is a number that can be found in the database you can do:
select p.id, t.lik_id
from table_containing_lik t
join tbl_users_posts p on find_in_set(t.lik_id, p.lik)
where p.id=1;
I have a field that includes files that have 'words' separated by an underscore, _, such as this:
`file_name`
MY_NEW_MOVIE.mov
HD_VIDEO_720p.mov
720p_DISNEY_MOVIE.mov
LG_TYLERPERRY_FEATURE_HD_8CH_EN_L9714343_16X9_235_2398_FINAL_FRSUB.srt
And I want to split on _ and get the count of each word after the split, meaining:
`word` `count`
MY 1
NEW 1
MOVIE 2
HD 1
VIDEO 1
720p 2
DISNEY 1
Would it be possible/feasible to do this in SQL? So far I have just gotten the perfunctory "remove the file extension", but not sure how I could split on the token and then count that:
select left(file_name, length(file_name) - length(substring_index(file_name, '.', -1))-1) from asset
Additionally,
The result you want can be achieved with a query derived from this answer, which uses a generated numbers table along with SUBSTRING_INDEX to split out all the words in each file_name. This is then used as a derived table to count the occurrence of each word. Note the numbers table must have sufficient values to cover the maximum number of words in a filename (12 for this sample data).
SELECT word, COUNT(*)
FROM (
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(LEFT(file_name, LENGTH(file_name)-4), '_', numbers.n), '_', -1) AS word
FROM (
select 1 n union all
select 2 union all select 3 union all select 4 union all
select 5 union all select 6 union all select 7 union all
select 8 union all select 9 union all select 10 union all
select 11 union all select 12
) numbers
JOIN asset ON LENGTH(file_name)
- LENGTH(REPLACE(file_name, '_', '')) >= numbers.n - 1
) w
GROUP BY word
Output (for your sample data):
word COUNT(*)
16X9 1
235 1
2398 1
720p 2
8CH 1
DISNEY 1
EN 1
FEATURE 1
FINAL 1
FRSUB 1
HD 2
L9714343 1
LG 1
MOVIE 2
MY 1
NEW 1
TYLERPERRY 1
VIDEO 1
Demo on dbfiddle
Assuming the filenames always have exactly three components, SUBSTRING_INDEX can get the job done here:
SELECT word, COUNT(*) AS count
FROM
(
SELECT SUBSTRING_INDEX(file_name, '_', 1) AS word FROM asset
UNION ALL
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(file_name, '_', 2), '_', -1) FROM asset
UNION ALL
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(file_name, '_', -1), '.', 1) FROM asset
) t
GROUP BY word;
Demo
Note: This answer was given based on the OP's original sample data, where all filenames had exactly three underscore-separate components. This answer will not work for the updated question.
This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 2 years ago.
I have a field: userids which contains 19,2,23 for example, which I populate based on user ids added to a room arbitrarily. I list the corresponding names based on these userids.
Right now I get Richard, Evan, James from this query (19 = Richard, 2 = Evan, 23 = James).
What I would like to do is sort the array (the field) upon query so that it is 2,19,23 and the names are returned Evan, Richard, James.
So a query that looks like this:
SELECT c.*,c.NAME as PAGE, ASORT(c.USERIDS) as USERIDS, p.*
FROM TBL_CONTENT c, TBL_PAGE p
WHERE c.PAGEID = p.ID AND FIND_IN_SET(?, c.USERIDS)
Should look like (help me with this query)?
of course, the ASORT function does not exist - I just include it as an example of what I am trying to do.
Any help here?
Thank you!
This works with mysql 8
For mysql 5.x , you need much more code to do that
SET #a = '19,2,23';
SELECT GROUP_CONCAT(ord ORDER BY ord)
FROM
(select CAST(num as UNSIGNED) ord
from (SELECT #a as names) t
join json_table(
replace(json_array(t.names), ',', '","'),
'$[*]' columns (num varchar(50) path '$')
) j) z1
RESULTS in
# GROUP_CONCAT(ord ORDER BY ord)
2,19,23
For Mysql 5.x You can use a function
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `fn_split_stringnumbers_and_sort`(_text TEXT) RETURNS text CHARSET utf8mb4
DETERMINISTIC
BEGIN
SET #text = _text;
SELECT
GROUP_CONCAT((name+0) ORDER BY (name +0)) INTO #output
FROM
(
select
SUBSTRING_INDEX(SUBSTRING_INDEX(t1.name, ',', numbers.n), ',', -1) name
from
(select 1 n union all
select 2 union all select 3 union all
select 4 union all select 5) numbers INNER JOIN (SELECT #text as name) t1
on CHAR_LENGTH(t1.name)
-CHAR_LENGTH(REPLACE(t1.name, ',', ''))>=numbers.n-1
order by n
) t2;
RETURN #output;
END$$
DELIMITER ;
SO that this query
SET #a = '19,2,23';
SELECT fn_split_stringnumbers_and_sort(#a);
has the same result
# fn_split_stringnumbers_and_sort(#a)
2,19,23
Caution
This function only would split 5 comma separated numbers if you have more than that you have to increase table with more UNION
Creating a concatenated string in SSRS with values enclosed in single quotes
Any answers to the above question?. I am struck with the same problem:
The query from SSRS side is:
select *
from xyz.test_table1
where f1 in (?)
Datasource for me in this case is a hive table. User selection on the parameter is a multivalued parameter which is what I expect to be substituted as:
where in ('value1','value2')
when query is executed. But when looked at the query execution on the hive side, it comes as:
where in ('value1,value2')
How could I solve this?
From the documentation here, it seems Hive Query Language supports Common Table Expressions.
Consequently, something similar to the following should work:
declare #str nvarchar(4000) = ?; -- String to split.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = ',')
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(',',isnull(#str,''),s),0)-s,4000) from s)
-- Return each individual value in the delimited string along with it's position.
,v as (select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
)
select *
from v
join xyz.test_table1 as t
on v.v = t.f1
If you rather understandably don't want this rigamarole in all of your datasets, you would need to encapsulate this logic into whatever the Hive equivalent of a SQL Server table-valued parameter is, perhaps a UDTF?
In SQL Server, the function would be defined as follows:
create function [dbo].[fn_StringSplit4k]
(
#str nvarchar(4000) = ' ' -- String to split.
,#delimiter as nvarchar(1) = ',' -- Delimiting value to split on.
,#num as int = null -- Which value to return.
)
returns table
as
return
-- Start tally table with 10 rows.
with n(n) as (select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1 union all select 1)
-- Select the same number of rows as characters in #str as incremental row numbers.
-- Cross joins increase exponentially to a max possible 10,000 rows to cover largest #str length.
,t(t) as (select top (select len(isnull(#str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4)
-- Return the position of every value that follows the specified delimiter.
,s(s) as (select 1 union all select t+1 from t where substring(isnull(#str,''),t,1) = #delimiter)
-- Return the start and length of every value, to use in the SUBSTRING function.
-- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string.
,l(s,l) as (select s,isnull(nullif(charindex(#delimiter,isnull(#str,''),s),0)-s,4000) from s)
select rn
,item
from(select row_number() over(order by s) as rn
,substring(#str,s,l) as item
from l
) a
where rn = #num
or #num is null;
Figured it out! Posting the answer for other users.
Provide the query(under Query in SSRS) as an expression like below:
="select * from xyz.test_table1 where f1 in ('"&Join(Parameters!param.Value,"','")&"')"
The above string manipulation translates to:
select * from xyz.test_table1 where f1 in ('value1','value2')
Note: value1, value2 here are the values from user selected multivalue parameter
When I have string list like 1, 2, 3...
I'd like to use this as one column
Ids
1
2
3
Is it possible by sql query?
ex) SELECT Ids from (1, 2, 3...) <- I know this is not working.
Use a subquery of arbitrary digits to split your string.Instead of vals you can use '1,2,3'.
SELECT
DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(vals, ',', n.digit+1), ',', -1) val
FROM
tt1
INNER JOIN
(SELECT 0 digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6) n
ON LENGTH(REPLACE(vals, ',' , '')) <= LENGTH(vals)-n.digit;
See it working
For MySQL 8.0.4+
SELECT *
FROM
JSON_TABLE(
CONCAT('[', '1,2,3,4', ']'),
"$[*]"
COLUMNS(
ids BIGINT(20) PATH "$"
)
) AS tt
Concatenate square brackets ([]) around your string to make it into a JSON array. Then use JSON_TABLE to convert it into a table. See the MySQL JSON Table Functions for more info.
You can use below stored procedure to split string delimted by any character:
CREATE PROCEDURE `split_delimited` (
IN inputstr NVARCHAR(1000),
IN delimiter CHAR(1)
)
BEGIN
DROP TEMPORARY TABLE Items;
CREATE TEMPORARY TABLE Items(item NVARCHAR(50));
WHILE LOCATE(delimiter,inputstr) > 1 DO
INSERT INTO Items SELECT SUBSTRING_INDEX(inputstr,delimiter,1);
SET inputstr = REPLACE (inputstr, (SELECT LEFT(inputstr,LOCATE(delimiter,inputstr))),'');
END WHILE;
INSERT INTO Items(item) VALUES(inputstr);
select * from Items;
END
Input: 'a,b,c'
Output: a
b
c