How to convert comma separated string value into rows in MySQL? [duplicate] - mysql

This question already has answers here:
What is the opposite of GROUP_CONCAT in MySQL?
(6 answers)
Split comma separated string into rows in mysql
(3 answers)
Closed 4 months ago.
I have one column in MySQL which is return me comma separated value , I want to convert that column in to rows.
Better answer then How to convert comma separated parameters to rows in mysql?
select value from table limit 1
response
value
honda,activa,pleasure,car
I want this value to row like
value
honda
activa
pleasure
car

CREATE TABLE response (id INT, value TEXT)
SELECT 1 id, 'honda,activa,pleasure,car' value;
SELECT response.id, jsontable.value
FROM response
CROSS JOIN JSON_TABLE(CONCAT('["', REPLACE(value, ',', '","'), '"]'),
'$[*]' COLUMNS (value TEXT PATH '$')) jsontable;
id
value
1
honda
1
activa
1
pleasure
1
car
fiddle
PS. The query assumes that the value does not contain duoble quotes. If they are present then they must be quoted. See https://dbfiddle.uk/HUmPZEo1

First we need to create function which return index value of comma separated value
CREATE FUNCTION `SPLIT_STR`(
x VARCHAR(255),
delim VARCHAR(12),
pos INT
) RETURNS varchar(255) CHARSET utf8mb3
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
delim, '')
Then Create virtual recursive table from current value
with recursive new_table as (
select value ,LENGTH(t.value) - LENGTH(REPLACE(t.value, ',', '')) as n ,1 as x from table t limit 1
union all
select value, n,1+x as x from new_table where x <= n
)
select TRIM(SPLIT_STR(value,',',x)) as value from new_table
will return
value
honda
activa
pleasure
car

Related

MySQL group by comma seperated list unique [duplicate]

This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 2 years ago.
The column textfield has comma-seperated list values
ID | textfield
1 | english,russian,german
2 | german,french
3 | english
4 | null
I'm attempting to count the amount of languages in textfield. The default language is "English", so if null then "English". The correct amount of languages is 4(english,russian,german,french).
Here is my query to attempt doing this:
SELECT SUM((length(`textfield`) - length(replace(`textfield`, ',', '')) + 1)) as my
FROM yourtable;
The result i get is 6, i don't know how to group the languages.
Here is fiddle
http://sqlfiddle.com/#!9/0e532/1
The desired result is 4. How do i solve?
Identifying the source of error
What your query is doing is counting how many languages in each row, and adding them all together. Your query does not take into account duplicates. Since English shows up twice in the table, it is counted twice (and German, too), hence in your example six. Also, another issue is that your current code considers null as what null truly means, the absence of a value.
For example, if your database was
ID | textfield
---|----------
1 | null
you would also be arriving at incorrect results (more on this below).
Solution
This gets you a comma separated result of the languages, no duplicates.
SELECT
GROUP_CONCAT(DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(textfield, ',', n.digit+1), ',', -1)) textfield
FROM
yourtable
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(textfield, ',', '')) <= LENGTH(textfield)-n.digit;
This query can serve as a subquery for what you were attempting to do in the question prompt. In other words, instead of the length('textfield') ... you would provide the resulting column name from this query
Null as in English
This logic should not be implemented at the database level, IMHO. If you want to go ahead and consider null entries as English, that is fine. The downside is the example I provided for you before. When you have a query that solves for the total languages in the database, if English wasn't an explicitly stated language and instead just a null value, then the query wouldn't 'count' English (it's null). But you can't just add 1 every time you find the amount of languages because English might already be explicit.
Recommendations:
Avoid comma separated lists in databases by normalizing your data
No value makes sense for a null field
For version 5.6 (like in the fiddle)
SELECT COUNT(DISTINCT SUBSTRING_INDEX(SUBSTRING_INDEX(languages.textfield, ',', numbers.num), ',', -1)) languages_count
FROM (SELECT COALESCE(textfield, 'english') textfield
FROM yourtable) languages
JOIN (SELECT 1 num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) numbers
ON numbers.num <= LENGTH(languages.textfield) - LENGTH(REPLACE(languages.textfield, ',', '')) + 1;
fiddle
For version 8.x (as claimed in a comment)
SELECT COUNT(DISTINCT jsontable.value) languages_count
FROM yourtable
CROSS JOIN JSON_TABLE( CONCAT('["', REPLACE(COALESCE(textfield, 'english'), ',', '","'), '"]'),
"$[*]" COLUMNS( value VARCHAR(254) PATH "$" )
) AS jsontable;
fiddle

Custom SQL query, for sorting values with "-" as separators

I am trying to create an ORDER BY to sort my values properly.
The values contain a string and anywhere from zero to three sets of numbers separated by a -.
Example:
dog-2-13
dog-13-54-3
dog-25
cat-63-12
cat
I want them to be sorted firstly by the string in front and then by each of the "number sections" so that: dog-2-14 > dog-2-13 but dog-1-14 < dog-2-13.
Expected result (with more examples to make it clearer):
cat
cat-63-12
dog-2-13
dog-2-14
dog-3
dog-13-53-3
dog-13-54-3
dog-13-54-4
dog-25
I'm a SQL novice and completely lost. Thank you!
Please try...
SELECT fieldName
FROM
(
SELECT fieldName AS fieldName,
SUBSTRING_INDEX( fieldName,
'-',
1 ) AS stringComponent,
CONVERT( SUBSTRING_INDEX( SUBSTRING( fieldName,
CHAR_LENGTH( SUBSTRING_INDEX( fieldName, '-', 1 ) ) + 2 ),
'-',
1 ),
UNSIGNED ) AS firstNumber,
CONVERT( SUBSTRING_INDEX( SUBSTRING( fieldName,
CHAR_LENGTH( SUBSTRING_INDEX( fieldName, '-', 2 ) ) + 2 ),
'-',
1 ),
UNSIGNED ) AS secondNumber,
CONVERT( SUBSTRING( fieldName,
CHAR_LENGTH( SUBSTRING_INDEX( fieldName, '-', 3 ) ) + 2 ),
UNSIGNED ) AS thirdNumber
FROM table1
ORDER BY stringComponent,
firstNumber,
secondNumber,
thirdNumber
) tempTable;
The inner SELECT grabs the field name (which I am assuming is fieldName) and the three components and places each in a separate field and assigning an alias to that field. Each subfield must be included at this point for sorting purposes. The list is then sorted based upon those values.
Once this sorting is performed the outer SELECT chooses the original field from the list in a now sorted order.
The four outer instances of SUBSTRING_INDEX() are used to grab the desired fields from their first argument. As SUBSTRING_INDEX() grabs all of the string from the beginning to just before the first occurence of the delimiting character this makes finding the first field easy (Note : I am assuming that the first field shall contain no hyphens).
The first argument for the remaining occurences of SUBSTRING_INDEX() is formed by using SUBSTRING() to grab everything from just after the parsed part of fieldName and the following delimiting character. It is told where this is by using CHAR_LENGTH() to count the number of characters before the most recent delimiting character then adding 1 for the most recent delimiting character and another 1 to point SUBSTRING() to the character after the most recent delimiting character.
SUBSTRING_INDEX() will return NULL where it encounters an absent numerical field. Please note that NULL has a different sort value from zero.
The numerical fields are converted into unsigned Integers using CONVERT(). Unsigned integers were chosen as the supplied data does not contain any real numbers. If there are real values then you will need to replace UNSIGNED with DECIMAL. I have also assumed that all of the numbers will be positive.
Further reading...
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring-index
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_substring
https://dev.mysql.com/doc/refman/5.7/en/string-functions.html#function_char-length
https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
If you have any questions or comments, then please feel free to post a Comment accordingly.
You can use a Query like this:
SELECT *
FROM yourTable
ORDER BY SUBSTRING_INDEX( SUBSTRING_INDEX(cat,'-',2), '-', -1);
sample
mysql> SELECT SUBSTRING_INDEX('dog-13-54-4','-',2);
+--------------------------------------+
| SUBSTRING_INDEX('dog-13-54-4','-',2) |
+--------------------------------------+
| dog-13 |
+--------------------------------------+
1 row in set (0,00 sec)
mysql>
mysql> SELECT SUBSTRING_INDEX( SUBSTRING_INDEX( 'dog-13-54-4','-',2), '-', -1);
+------------------------------------------------------------------+
| SUBSTRING_INDEX( SUBSTRING_INDEX( 'dog-13-54-4','-',2), '-', -1) |
+------------------------------------------------------------------+
| 13 |
+------------------------------------------------------------------+
1 row in set (0,00 sec)
mysql>

How to sum a comma separated string in SQL? [duplicate]

This question already has answers here:
Summing a comma separated column in MySQL 4 (not 5)
(4 answers)
Closed 9 years ago.
id value
1 1,2,3,4
2 2,3,4
So I want to get this result:
id sum
1 10
2 9
Can I do it in SQL(MySQL)?
With great effort, you can do this. Really, though, this is a very, very bad way to store data.
In the spirit that sometimes we have to use data whose format is not under our control:
select id,
(substring_index(value, ',', 1) +
substring_index(substring_index(concat(value, ',0'), ',', 2), ',', -1) +
substring_index(substring_index(concat(value, ',0'), ',', 3), ',', -1) +
substring_index(substring_index(concat(value, ',0'), ',', 4), ',', -1) +
substring_index(substring_index(concat(value, ',0'), ',', 5), ',', -1)
) as thesum
from t;
The nested called to substring_index() fetch the nth value in the string. The concat(value, ',0') is to handle the case where there are fewer values than expressions. In this case, the nested substring_index() will return the last value for any value of n greater than the number of items in the list. Concatenating 0 to the list ensures that this doesn't affect the sum.
The SQL Fiddle is here.
you can do it more dynamically Creating a function. Please follow the following steps
create a function that give the sum of a comma separated value
CREATE FUNCTION GetToalOfCommaSeperatedVal
(
#commaSeperatedVal varchar(100)
)
RETURNS int
AS
BEGIN
declare #sum int
DECLARE #x XML
SELECT #x = CAST('<A>'+ REPLACE(#commaSeperatedVal,',','</A><A>')+ '</A>' AS XML)
SELECT #sum=sum(t.value('.', 'int'))
FROM #x.nodes('/A') AS x(t)
return #sum
END
GO
the do a just select command in the following way
select id,dbo.GetToalOfCommaSeperatedVal(value) from YOUR_TABLE

Coalesce equivalent for nth not null value - MySQL

I have been tearing my hair out over this issue. I am working with an existing data set and need to remove all the null values from the columns in table A and shunt them across so they are ordered like in table B
I need something which is equivalent to Coalesce but to retrieve the nth value so I can get the result sorted like in table B
What I have:
Table A
Name CURRENT OCT12 SEPT12 AUG12 JUL12 JUN12 MAY12 APR12
---------------------------------------------------------
A NULL NULL Aug-12 NULL NULL Jun-12 NULL Apr-12
B Nov-12 NULL Aug-12 NULL Jul-12Jun-12 NULL Apr-12
What I need:
Table B
Name Change1 Change2 Change3 Change4 Change5 Change6
----------------------------------------------------
A Aug-12 Jun-12 Apr-12 NULL NULL NULL
B Nov-12 Aug-12 Jul-12 Jun-12 Apr-12 NULL
Code-wise, it would be something like:
Select
first non-null value as Change1
,second non-null value as Change2
,third non-null value as Change3
,fourth non-null value as Change4
,fifth non-null value as Change5...etc..
from Table_A
I am using MySQL and i have no idea how to reference the nth non null value in order to call them into Table_B
Does anyone have any ideas?
I am not sure if I would reccommend using this solution... normalization of your data is always a better choice, but I wanted to answer using plain SQL with some strings functions. This query should return what you are looking for:
SELECT
Name,
Changes,
REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(Changes, ',', 1)), ',', 1)) as Change1,
REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(Changes, ',', 2)), ',', 1)) as Change2,
REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(Changes, ',', 3)), ',', 1)) as Change3,
REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(Changes, ',', 4)), ',', 1)) as Change4,
REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(Changes, ',', 5)), ',', 1)) as Change5,
REVERSE(SUBSTRING_INDEX(REVERSE(SUBSTRING_INDEX(Changes, ',', 6)), ',', 1)) as Change6
FROM (
SELECT
Name,
CONCAT_WS(',', CURRENT, OCT12, SEPT12, AUG12, JUL12, JUN12, MAY12, APR12, ',') as Changes
FROM
TableA
) s
I'm concatenating all values in a comma separated string, with two commas at the end of the string (one comma would be enough anyway, but it's easier to put two and just ignore the last one...), and since I'm using CONCAT_WS it will automatically skip null values, and the resulting string will be something like Aug-12,Jun-12,Apr-12,,.
Then in the outer query I'm extracting the n-th element of the string, using SUBSTRIG_INDEX. I would recommend to normalize your database, but if you need a quick fix this solution might be a good starting point.
See it working here.
Please notice that I am not returning NULL values where there are no changes, but I am returning empty strings instead. This can be changed if you need.
If you don't want to use strings functions you can try this sql using unpivot and row number partitioning:
CREATE TABLE #TableA
(
"Name" VARCHAR(10),
"CURRENT" VARCHAR(10),
OCT12 VARCHAR(10),
SEPT12 VARCHAR(10),
AUG12 VARCHAR(10),
JUL12 VARCHAR(10),
JUN12 VARCHAR(10),
MAY12 VARCHAR(10),
APR12 VARCHAR(10)
)
INSERT INTO #TableA
("Name", "CURRENT", OCT12, SEPT12, AUG12, JUL12, JUN12, MAY12, APR12)
VALUES
('A', NULL, NULL, 'Aug-12', NULL, NULL, 'Jun-12', NULL, 'Apr-12'),
('B', 'Nov-12', NULL, 'Aug-12', NULL, 'Jul-12', 'Jun-12', NULL, 'Apr-12')
SELECT * FROM #TableA;
Select "Name",
Min(Case row_num When 1 Then data End) Change1,
Min(Case row_num When 2 Then data End) Change2,
Min(Case row_num When 3 Then data End) Change3,
Min(Case row_num When 4 Then data End) Change4,
Min(Case row_num When 5 Then data End) Change5,
Min(Case row_num When 6 Then data End) Change6
From
(
select "Name",data,DBColumnName,
ROW_NUMBER() OVER (PARTITION BY "Name" ORDER BY "Name") row_num
From #TableA
unpivot (data for DBColumnName in ("CURRENT",OCT12,SEPT12,AUG12,JUL12,JUN12,MAY12,APR12) ) as z
) TableB
group by "Name";
References:
-- TSQL Pivot without aggregate function
-- https://www.sqlservertutorial.net/sql-server-window-functions/sql-server-row_number-function/
-- https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-ver15

Mysql combining data from columns to row

The rough structure of the mysql table is like this
Person Name, Mobile Number1, Mob2, Mob3, Mob4.
What I am trying to do is:
For example, if the table has 4 records for the same person like:
Person Mob1 Mob2 Mob3 Mob4
John 123
John 435
John 324
John 432
I need to combine the four records into one like:
Person Mob1 Mob2 Mob3 Mob4
John 123 435 324 433
Is it possible to do this using a mysql query in phpMyAdmin?
I know it is possible using a php script, but the table is quite huge :close to 500Mb (Nearly a million records.), so a script would be extremely slow/take very long time to complete.
I think you'll need to use a stored procedure, check these other posts for (possibly) helpful info:
split keywords for post php mysql
MySQL procedure to load data from staging table to other tables. Need to split up multivalue field in the process
How to expand comma-separated field into multiple rows in MySQL
Or you could try to do it with a function and a group_concat():
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 then the select query would be:
SELECT
tmp.id,
SPLIT_STR(tmp.mobs, ',', 1) as mob1,
SPLIT_STR(tmp.mobs, ',', 2) as mob2,
SPLIT_STR(tmp.mobs, ',', 3) as mob3,
SPLIT_STR(tmp.mobs, ',', 4) as mob4
FROM (
SELECT
id,
GROUP_CONCAT(mob1) as mobs
FROM person
GROUP BY name
) tmp
Of course, then you'd have to integrate that into an UPDATE statement, but I'll let you try that on your own. (btw, got the FUNCTION from here).