This question already has answers here:
Search with comma-separated value mysql
(1 answer)
MySql PHP select count of distinct values from comma separated data (tags)
(5 answers)
Closed 4 years ago.
I have a table that stores data like:
userid books
ym0001 dictionary,textbooks,notebooks
ym0002 textbooks,dictionary
I want to count number of times each book occurs. I want my result to be in this format.
books Counts
dictionary 2
notebooks 1
textbooks 2
This is mysql. Please help
The following approach builds a result of 1000 integers, then uses those integers (n) to locate segments within the comma seperated string, and for each segment it creates a new row so that the derived table looks like this:
userid | book
:----- | :---------
ym0001 | dictionary
ym0002 | textbooks
ym0001 | textbooks
ym0002 | dictionary
ym0001 | notebooks
Once that exists it is a simple matter of grouping by book to arrive at the counts.
select
book, count(*) Counts
from (
select
t.userid
, SUBSTRING_INDEX(SUBSTRING_INDEX(t.books, ',', numbers.n), ',', -1) book
from (
select #rownum:=#rownum+1 AS n
from
(
select 0 union all select 1 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
) a
cross join (
select 0 union all select 1 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
) b
cross join (
select 0 union all select 1 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
) c
cross join (select #rownum:=0) r
) numbers
inner join mytable t
on CHAR_LENGTH(t.books)
-CHAR_LENGTH(REPLACE(t.books, ',', '')) >= numbers.n-1
) d
group by
book
order by
book
book | Counts
:--------- | -----:
dictionary | 2
notebooks | 1
textbooks | 2
If you already have a table of numbers, use that instead.
the cross joins of a b and c dynamically produce 1000 rows, if you need more add further cross joins similar to c. i.e. the number of numbers should exceed the maximum length of your comma seperated data
db<>fiddle here
Related
This question already has answers here:
Is storing a delimited list in a database column really that bad?
(10 answers)
Closed 3 years ago.
I am stuck in a situation where in my table I have multiple duplicate value for each row in a column, and table look like
User_Id | Color
-----------+-------------------------------
1 | Red, Blue, Red,Green
2 | Green,Green,Blue,Blue, Red
3 | Black, White
4 | Red,Red,Red
I want to remove or delete each duplicate value from column color, so that each user_id hold only unique value like.
User_Id | Color
-----------+--------------------
1 | Red, Blue,Green
2 | Green, Blue, Red
3 | Black,White
4 | Red
Is there any way to achieve the desired output? I searched a lot but got nothing.
Your valuable comment will be highly appreciated.
is there any way to achieve the desired output, I searched a lot but got nothing. your valuable comment will be highly appreciated.
Like I already said you should normalize, a delimited list can't enforce uniqueness: no way to prevent Red, Blue, Red, Green, Bluewhen inserting and updating without writing application code or a trigger. Which also means fetching the complete data.
If you don't normalize this you are going to need to be creative, with a SQL number generator and nested SUBSTRING_INDEX() functions and a CROSS JOIN to split the string. And use GROUP BY and GROUP_CONCAT(DISTINCT ..) to make the unique values
You don't want to do this, this query shows how hard the query is on a delimited list
Query
SELECT
DISTINCT
t.User_Id
, GROUP_CONCAT(DISTINCT TRIM(SUBSTRING_INDEX(
SUBSTRING_INDEX(
t.Color
, ','
, sql_number_generator.number
)
, ','
, -1
)
)) AS color
FROM (
SELECT
#row := #row + 1 AS number
FROM (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
SELECT #row := 0
) init_user_params
) AS sql_number_generator
CROSS JOIN
t
GROUP BY
t.User_Id
Result
| User_Id | color |
| ------- | -------------- |
| 1 | Blue,Green,Red |
| 2 | Blue,Green,Red |
| 3 | Black,White |
| 4 | Red |
see demo
but the problem is how to update the color column with this result, i
tried but it throw an error [ Operand should contain 1 column(s) ], i
can't understand how it is to be done
Still can't believe you are willing to continue this approach. after the warnings
UPDATE
t
INNER JOIN (
SELECT
DISTINCT
t.User_Id
, GROUP_CONCAT(DISTINCT TRIM(SUBSTRING_INDEX(
SUBSTRING_INDEX(
t.Color
, ','
, sql_number_generator.number
)
, ','
, -1
)
)) AS color
FROM (
SELECT
#row := #row + 1 AS number
FROM (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row1
CROSS JOIN (
SELECT 0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) row2
CROSS JOIN (
SELECT #row := 0
) init_user_params
) AS sql_number_generator
CROSS JOIN
t
GROUP BY
t.User_Id
) AS records_to_updated
SET t.Color = records_to_updated.color
WHERE
t.User_Id = records_to_updated.User_Id
see demo
My database is like
Name | IC | Item
--------------------------
lee | xxx | pear,bear
--------------------------
ron | xxx | apple,dog
what should I do to retrieve the 4 values contained in the column "Item" and then separate them?
Do you have only two items separated by comma in Item? Or it may vary?
LE: you can use this SQL split comma separated row
LLE: just played around with that and this what I've done:
create table myTable(name varchar(7), ic varchar(7), item varchar(200));
insert into myTable(name,ic,item) values ('lee','xxx','pear,bear');
insert into myTable(name,ic,item) values ('ron','xxx','apple,dog');
insert into myTable(name,ic,item) values ('a','xxx','gamma');
insert into myTable(name,ic,item) values ('b','xxx','a,b,c,d');
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(t.item, ',', n.n), ',', -1) value
FROM myTable t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 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) a
,(SELECT 0 AS N UNION ALL SELECT 1 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) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(t.item) - LENGTH(REPLACE(t.item, ',', '')))
ORDER BY value;
I have 2 tables as follows:-
Products
Product_name | category_id
Nutella | 1,2
Milk | 3,4
Categories
cat_id | name
1 | dessert
2 | chocolate
3 | dairy
4 | milk
I am using datatable with server side processing using SSP library to display the products table.
I need to make join statement that get me the categories for each product since the category column contains comma separated multiple values!
Is there anyway I can do it using MYSQL?
EDIT
Expected output
Product_name | category_id
Nutella | dessert,chocolate
Milk | diary,milk
Assuming as SQL Server, First, You can use the CTE to get rows from comma separated categories ids into rows, then using INNER JOIN on cat_id, you can get the desired result.
SEE DEMO SQL Server
;WITH CTEProduct (Product_name,category_id)
AS
(
SELECT A.Product_name,
Split.a.value('.', 'VARCHAR(100)') AS category_id
FROM
(
SELECT Product_name,
CAST ('<M>' + REPLACE(category_id, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM Products
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
)
Select CTEProduct.category_id,CTEProduct.Product_name,Categories.name
From CTEProduct
INNER JOIN Categories ON CTEProduct.category_id = Categories.cat_id
EDIT:- For MYSQL, we have to follow the same approach, convert comma separated string to multiple rows then INNER JOIN. I does not know much about How to separated comma separated string to rows in MYSQL so Refer Here. I utilized the same answer here.
SEE DEMO MYSQL
Select tablename.Product_name,GROUP_CONCAT(Categories.name) name
From (
SELECT Product_name,SUBSTRING_INDEX(SUBSTRING_INDEX(t.category_id, ',', n.n), ',', -1) category_id
FROM Products t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 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) a
,(SELECT 0 AS N UNION ALL SELECT 1 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) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(t.category_id) - LENGTH(REPLACE(t.category_id, ',', '')))
) tablename
INNER JOIN Categories ON tablename.category_id = Categories.cat_id
GROUP BY tablename.Product_name;
There is a table(Course Interests) which has all the values in one cell. But those values are just ids and I want to join them with another table(Course) so I can know their names.
Course Interests:
MemberID MemberName CoursesInterested
-------------- --------------------- --------------
1 Al 1,4,5,6
2 A2 3,5,6
Course Table:
CourseId Course
-------------- ---------------------
1 MBA
2 Languages
3 English
4 French
5 Fashion
6 IT
Desired Output:
MemberID MemberName CoursesInterested
-------------- --------------------- --------------
1 Al MBA,French,Fashion,IT
2 A2 English,Fashion,IT
I would like to do a SQL query in MySql that can help me to extract the desired output. I know how to do it in the opposite way(join values to one cell), but I've struggling on seek a way to separate the ids and do a cross-join into another table.
I'll appreciate any help from the community. Thanks
Use FIND_IN_SET to search for something in a comma-delimited list.
SELECT i.MemberID, i.MemberName, GROUP_CONCAT(c.Course) AS CoursesInterested
FROM CourseInterests AS i
JOIN Course AS c ON FIND_IN_SET(c.CourseId, i.CoursesInterested)
However, it would be better to create a relation table instead of storing the courses in a single column. This type of join cannot be optimized using an index, so it will be expensive for a large table.
Try this Out:
SELECT MemberID,MemberName,Group_Concat(C.Course) from
(
SELECT MemberID,MemberName,SUBSTRING_INDEX(SUBSTRING_INDEX(t.CoursesInterested, ',', n.n), ',', -1) value
FROM Table1 t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 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) a
,(SELECT 0 AS N UNION ALL SELECT 1 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) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(t.CoursesInterested) - LENGTH(REPLACE(t.CoursesInterested, ',', '')))
ORDER BY MemberID,value
) T JOIN course C ON T.value = C.CourseId
Group By MemberID,MemberName
Fiddle Demo
Output:
MemberID MemberName CoursesInterested
-------------- --------------------- --------------
1 Al MBA,French,Fashion,IT
2 A2 English,Fashion,IT
This question already has answers here:
MySQL: Split comma separated list into multiple rows
(4 answers)
Closed 9 years ago.
I have a column with a variable number of comma seperated values:
somethingA,somethingB,somethingC
somethingElseA, somethingElseB
And I want the result to take each value, and create a row:
somethingA
somethingB
somethingC
somethingElseA
somethingElseB
How can I do this in SQL (MySQL)?
(I've tried googling "implode" and "lateral view", but those don't seem to turn up related questions. All the related SO questions are trying to do much more complicated things)
You can do it with pure SQL like this
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(t.values, ',', n.n), ',', -1) value
FROM table1 t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 n
FROM
(SELECT 0 AS N UNION ALL SELECT 1 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) a
,(SELECT 0 AS N UNION ALL SELECT 1 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) b
ORDER BY n
) n
WHERE n.n <= 1 + (LENGTH(t.values) - LENGTH(REPLACE(t.values, ',', '')))
ORDER BY value
Note: The trick is to leverage tally(numbers) table and a very handy in this case MySQL function SUBSTRING_INDEX(). If you do a lot of such queries (splitting) then you might consider to populate and use a persisted tally table instead of generating it on fly with a subquery like in this example. The subquery in this example generates a sequence of numbers from 1 to 100 effectively allowing you split up to 100 delimited values per row in source table. If you need more or less you can easily adjust it.
Output:
| VALUE |
|----------------|
| somethingA |
| somethingB |
| somethingC |
| somethingElseA |
| somethingElseB |
Here is SQLFiddle demo
This is how the query might look with a persisted tally table
SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(t.values, ',', n.n), ',', -1) value
FROM table1 t CROSS JOIN tally n
WHERE n.n <= 1 + (LENGTH(t.values) - LENGTH(REPLACE(t.values, ',', '')))
ORDER BY value
Here is SQLFiddle demo