MySQL split and join the values - mysql

I have a table [mapping] with 2 columns similar to below
id | values
1 | 1,2
2 | 1,2,3
3 | 1,1
4 | 1,1,2
and another table [map] is similar to this
sno | values
1 | Test
2 | Hello
3 | Hai
My expected output is
id | values
1 | Test,Hello
2 | Test,Hello,Hai
3 | Test,Test
4 | Test,Test,Hello
Is it possible? If it is please can anybody build a query for me.

You can use MySQL FIND_IN_SET() to join the tables and GROUP_CONCAT() to concat the values :
SELECT s.sno,GROUP_CONCAT(s.values) as `values`
FROM mapping t
INNER JOIN map s ON(FIND_IN_SET(s.id,t.values))
GROUP BY s.sno
Note: You should know that this is a very bad DB structure. This may lead to a lot more complicated queries and will force you to over complicate things. You should Normalize your data, split it , and place each ID in a separate record!

SELECT
`ids`.`id`,
GROUP_CONCAT(`values`.`texts`) AS texts
FROM
`ids`
INNER JOIN `values` ON FIND_IN_SET(`values`.`id`, `ids`.`values`)
GROUP BY
`ids`.`id`
It works like this: Example

Related

How to find data based on comma separated parameter in comma separated data in my SQL query

We have below data,
plant table
----------------------------
| name | classification |
| A | 1,4,7 |
| B | 2,3,7 |
| C | 3,4,9,8 |
| D | 1,5,6,9 |
Now from front end side, they will send multiple parameter like "4,9",
and the objective output should be like this
plant table
---------------------------
| name | classification |
| A | 1,4,7 |
| C | 3,4,9,8 |
| D | 1,5,6,9 |
Already tried with FIND_IN_SET code, but only able to fetch only with 1 parameter
select * from plant o where find_in_set('4',classification ) <> 0
Another solution is by doing multiple queries, for example if the parameter is "4,9" then we do loop the query two times with parameter 4 and 9, but actually that solution will consume so much resources since the data is around 10000+ rows and the parameter itself actually can be more than 5 params
If the table design is in bad practice then OK but we are unable to change it since the table is in third party
Any solution or any insight will be appreciated,
Thank you
Schema (MySQL v8.0)
CREATE TABLE broken_table (name CHAR(12) PRIMARY KEY,classification VARCHAR(12));
INSERT INTO broken_table VALUES
('A','1,4,7'),
('B','2,3,7'),
('C','3,4,9,8'),
('D','1,5,6,9');
Query #1
WITH RECURSIVE cte (n) AS
(
SELECT 1
UNION ALL
SELECT n + 1 FROM cte WHERE n < 5
)
SELECT DISTINCT x.name, x.classification FROM broken_table x JOIN cte
WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(classification,',',n),',',-1) IN (4,9);
name
classification
A
1,4,7
C
3,4,9,8
D
1,5,6,9
View on DB Fiddle
EDIT:
or, for older versions...
SELECT DISTINCT x.name, x.classification FROM broken_table x JOIN
(
SELECT 1 n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5
) cte
WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(classification,',',n),',',-1) IN (4,9)
Let's just avoid the CSV altogether and fix your table design:
plant table
----------------------------
| name | classification |
| A | 1 |
| A | 4 |
| A | 7 |
| B | 2 |
| B | 3 |
| B | 7 |
| ... | ... |
Now with this design, you may use the following statement:
SELECT *
FROM plant
WHERE classification IN (?);
To the ? placeholder, you may bind your collection of values to match (e.g. (4,9)).
You want or so you can use regular expressions. If everything were one digit:
where classification regexp replace('4,9', ',', '|')
However, this would match 42 and 19, which I'm guessing you do not want. So, make this a little more complicated so you have comma delimiters:
where classification regexp concat('(,|^)', replace('4,9', ',', ',|,'), '(,|$)')

Select count with value from different tables

I want to count all entries in one table grouped by the user id.
This is the query I used which works fine.
select uuid_mapping_id, count(*) from t_message group by uuid_mapping_id;
and these are the results:
+-----------------+----------+
| uuid_mapping_id | count(*) |
+-----------------+----------+
| 1 | 65 |
| 4 | 277 |
Now I would like to display the actual user name, instead of the ID.
To achieve this I would need the help of two different tables.
The table t_uuid_mapping which has two columns:
uid_mapping_id, which equals uuid_mapping_id in the other table.
And f_uuid which is also unique but completely different.
f_uuid can also be found in another table t_abook which also contains the names in the column f_name.
The result I am looking for should be:
+-----------------+----------+
| f_name | count(*) |
+-----------------+----------+
| admin | 65 |
| user1 | 277 |
I am new to the database topic and understand that this could be achieved by using JOIN in the query, but to be honest I did not completely understand this yet.
if I understand you correctly:
SELECT tm.f_name, COUNT(*) as count
FROM t_message tm
LEFT JOIN t_abook ta ON (tm.uuid_mapping_id = ta.uid_mapping_id)
GROUP BY tm.f_name

How to create virtual column with multiple value using MySQL SELECT?

I can add virtual columns as
SELECT '1' as id
| id |
-------
| 1 |
But I want add multiple values, example:
SELECT ('1','2','3') as id
| id |
-------
| 1 |
| 2 |
| 3 |
But this don't work
Like Marc B said in a comment you can't have a single query split a single row into multiple rows, but you can have multiple queries, each producing one of the values, by chaining them together with union.
SELECT 1 id
UNION
SELECT 2
UNION
SELECT 3
As the answer was provided in a couple of comments I'll post it as a community wiki.

Replace nested select query by self join

I recently asked a question here concerning an SQL query: Trouble wrapping head around complex SQL delete query
I now understand that what I'm trying to do is too complex to pull off with a single query or even multiple queries without some way to keep results in between. Therefore I decided to create a bash script (the end result will be something to do with a cronjob so bash is the most straightforward choice).
Consider the following table:
AssociatedClient:
+-----------+-----------------+
| Client_id | Registration_id |
+-----------+-----------------+
| 2 | 2 |
| 3 | 2 |
| 3 | 4 |
| 4 | 5 |
| 3 | 6 |
| 5 | 6 |
| 3 | 8 |
| 8 | 9 |
| 7 | 10 |
+-----------------------------+
What I want to do is select all Registration_ids where the Client_id is in the list of Client_ids associated with a specific Registration_id.
Although I'm pretty noob with SQL, I found this query relatively easy:
SELECT `Registration_id` FROM `AssociatedClient` ac1
WHERE ac1.`Client_id` IN
(SELECT `Client_id` FROM `AssociatedClient` ac2
WHERE ac2.`Registration_id` = $reg_id);
where $reg_id is just a bash variable.
This works but I would like to see it done with a self join, because it looks nicer, especially within a bash script where a lot of character clutter occurs. I'm afraid my SQL skills just don't reach that far.
If I've understood correctly, you should just be able to do a simple self join like so:
SELECT ac1.registration_id
FROM associatedclient ac1
JOIN associatedclient ac2 ON ac2.client_id = ac1.client_id
WHERE ac2.registration_id = $reg_id
So what you are doing is scanning the table once, joining it to itself where the client_id matches. Then you are restricting the joined rows to ones where the 2nd version of the table has a specific id, leaving you with the different permutations of the join on the 1st table, and then just picking the registration_id from those rows.
So, given the example of a variable value of 6, try running the following statement:
SELECT
ac1.client_id AS client_id_1
, ac1.registration_id AS reg_id_1
, ac2.client_id AS client_id_2
, ac2.registration_id AS reg_id_2
FROM associatedclient ac1
JOIN associatedclient ac2 ON ac1.client_id = ac2.client_id
and you'll notice the full set of joins. Then try adding the WHERE restriction and notice which rows come back. Then finally just pick the column you want.
You can check out a SQLFiddle I set up which tests it with a value of 6

Chaining results from multiple tables using SQL

I have a set of tables with following structures
**EntityFields**
fid | pid
1 | 1
2 | 1
3 | 2
4 | 2
5 | 1
**Language**
id | type | value
1 | Entity | FirstEntity
2 | Entity | SecondEntity
1 | Field | Name
2 | Field | Age
3 | Field | Name
4 | Field | Age
5 | Field | Location
Now as you may have understood, the first table gives the EntityField assignment to each Entity. The second table gives out the names for those IDs. What I want to output is something like the following
1 | FirstEntity / Name (i.e. a concat of the Entity and the EntityField name)
2 | FirstEntity / Age
3 | FirstEntity / Location
4 | SecondEntity / Name
5 | SecondEntity / Age
Is this possible?
Thank you for the answers, unfortunately the table structure is something that I cannot change. The table structure it self belongs to another data directory system which is quite flexible and which I am using to pull out data. I know that without providing the necessary background, this table structure looks quite weird, but it is something that works quite well (except in this scenario).
I will try out the examples here and will let you know.
For your current table structure, I think the following will work
SELECT EntityFields.fid, CONCAT(L1.value, ' / ' L2.value)
FROM EntityFields INNER JOIN Language as L1 ON EntityFields.pid=L1.id and L1.type='Entity'
INNER JOIN Language as L2 ON EntityFields.fid=L2.id and L2.type='Field'
ORDER BY EntityFields.fid
However, this query could be made much easier by having a better table structure. For example, with the following structure:
**EntityFields**
fid | pid | uid
1 | 1 | 1
2 | 1 | 2
1 | 2 | 3
2 | 2 | 4
3 | 1 | 5
**Entities**
id | value
1 | FirstEntity
2 | SecondEntity
**Fields**
id | value
1 | Name
2 | Age
3 | Location
you can use the somewhat simpler query:
SELECT uid, CONCAT(Entities.value, Fields.value)
FROM EntityFields INNER JOIN Entities ON EntityFields.pid=Entities.id
INNER JOIN Fields ON EntityFields.fid=Fields.id
ORDER BY uid
Well, I have no idea what you're trying to accomplish here. The fact that you label some records "Entity" and others "Field" and then try to connect them to each other makes it look to me like you are mixing two totally different things in the same table. Why not have an Entity table and a Field table?
You could get the results you seem to want by writing
select fid, le.value, lf.value
from entittyfields e
join language le on e.pid=le.id and type='Entity'
join language lf on e.fid=lf.id and type='Field'
order by fid
But I think you'd be wise to rethink your table design. Perhaps you could explain what you're trying to accomplish.
SELECT ef.fid AS id
, COALESCE(e.value, '-', ef.pid, ' / ', f.value)
AS entity_field
FROM EntityFields ef
JOIN Language AS e
ON e.id = ef.id
AND e.type = 'Entity'
JOIN Language AS f
ON f.id = ef.id
AND f.type = 'Field'
ORDER BY ef.pid
, ef.fid
If I understand your question, which I don't think I do, this is simple. It appears to be a set of very poorly designed tables (Language doing more than one thing, for example). And it appears that the Language table has two types of records: a) The Entity records, which have type='Entity' and b) Field records, which have type='Field'.
At any rate, the way I would approach it is to treat the Language table as if it were two tables:
select ef.fid, Entities.value, Fields.value
from entityfields ef
inner join language Entities
on Entities.id = ef.id
and Entities.type = 'Entity'
inner join language Fields
on Fields.id = ef.id
and Fields.Type = 'Field'
order by 2, 3
First stab, anyway. That should help you get the answer.