Merge multiple rows together MySQL - mysql

I have a table that looks more or less like this:
user is_su last_login roles_for_groups
+------+---+------------+----------------------------+
| rob | 1 | 2018-02-09 | admin, read |
+------+---+------------+----------------------------+
| gian | 0 | 2018-06-21 | prod_full_access, readOnly |
+------+---+------------+----------------------------+
| gian | 0 | 2018-06-21 | prod_full_access, CCT |
+------+---+------------+----------------------------+
| rob | 1 | 2018-02-09 | admin, write |
+------+---+------------+----------------------------+
and I would like to merge into a single row all the rows with the same user, in a way such that the table will look like this:
+------+---+------------+---------------------------------+--+
| rob | 1 | 2018-02-09 | admin, read, write | |
+------+---+------------+---------------------------------+--+
| gian | 0 | 2018-06-21 | prod_full_access, readOnly, CCT | |
+------+---+------------+---------------------------------+--+
How can I achieve this?

Why not just query out the data the way you want, using GROUP_CONCAT with DISTINCT:
SELECT
user,
is_us,
last_login,
GROUP_CONCAT(DISTINCT roles_for_groups) roles_for_groups
FROM yourTable
GROUP BY
user,
is_us,
last_login;
It isn't clear whether the is_us and last_login columns are always functionally dependent on the user. If not, then you should include some sample data which reveals this behavior.

select a.user,a.is_su,a.last_login,group_concat(distinct a.roles_for_groups separator ',' )
from (select user,is_su,last_login,substring_index(roles_for_groups,',',1) as roles_for_groups from table union select user,is_su,last_login,substring_index(roles_for_groups,',',-1) as roles_for_groups from table) a group by a.user
this query is only for roles_for_groups containing two values

Related

MySQL GROUP_CONCAT Query Excluding Records

I'm having a bit of an issue with my query below. I'm pulling data from a few tables and using the GROUP_CONCAT feature for a few rows. The GROUP_CONCAT pulls custom field data based on requests.REQUEST_ID. However, not every REQUEST_ID has an entry in TABLE_fields_data. Therefore, this element in my WHERE clause is causing an issue: data.REQUEST_ID=requests.REQUEST_ID.
I still want to pull the data from TABLE_requests even if there are no corresponding values in TABLE_fields_data. With my current query, the data is being completely omitted.
I've tried IFNULL in my GROUP_CONTACT statements but that didn't seem to make a difference. I've tried figuring out how to do an IF statement in the data.REQUEST_ID=requests.REQUEST_ID WHERE clause but haven't gotten that to work either. Based on my sample data below, the query would not return the request ID 6000002.
Really stuck on this and would be grateful for any help!
TABLE_requests
+------------+----------------+---------+---------------------+
| REQUEST_ID | REQUEST_STATUS | USER_ID | DATE_CREATE |
+------------+----------------+---------+---------------------+
| 6000001 | COMPLETE | 3000001 | 2020-06-01 11:15:16 |
+------------+----------------+---------+---------------------+
| 6000002 | COMPLETE | 3000001 | 2020-06-02 16:14:11 |
+------------+----------------+---------+---------------------+
| 6000003 | PENDING | 3000001 | 2020-06-03 12:11:11 |
+------------+----------------+---------+---------------------+
TABLE_fields
+----------+------+---------------+
| FIELD_ID | TYPE | LABEL_en |
+----------+------+---------------+
| 1 | TEXT | Student Name |
+----------+------+---------------+
| 2 | TEXT | Student Grade |
+----------+------+---------------+
| 3 | TEXT | Course |
+----------+------+---------------+
TABLE_fields_data
+---------+------------+----------+------------+
| DATA_ID | REQUEST_ID | FIELD_ID | DATA |
+---------+------------+----------+------------+
| 1 | 6000001 | 1 | John Smith |
+---------+------------+----------+------------+
| 2 | 6000001 | 2 | 1st Grade |
+---------+------------+----------+------------+
SELECT
requests.REQUEST_ID, requests.REQUEST_STATUS, requests.USER_ID, requests.DATE_CREATE,
GROUP_CONCAT(data.FIELD_ID SEPARATOR '||') AS CUSTOM_FIELD_ID,
GROUP_CONCAT(fields.TYPE SEPARATOR '||') AS CUSTOM_FIELD_TYPE,
GROUP_CONCAT(fields.LABEL_en SEPARATOR '||') AS CUSTOM_FIELD_LABEL,
GROUP_CONCAT(data.DATA SEPARATOR '||') AS CUSTOM_FIELD_DATA
FROM TABLE_requests AS requests, TABLE_fields_data AS data, TABLE_fields AS fields
WHERE requests.REQUEST_STATUS='COMPLETE' AND data.REQUEST_ID=requests.REQUEST_ID AND fields.FIELD_ID=data.FIELD_ID GROUP BY requests.REQUEST_ID;
You are using INNER joins (old style) but what you need is LEFT joins:
SELECT
requests.REQUEST_ID, requests.REQUEST_STATUS, requests.USER_ID, requests.DATE_CREATE,
GROUP_CONCAT(data.FIELD_ID SEPARATOR '||') AS CUSTOM_FIELD_ID,
GROUP_CONCAT(fields.TYPE SEPARATOR '||') AS CUSTOM_FIELD_TYPE,
GROUP_CONCAT(fields.LABEL_en SEPARATOR '||') AS CUSTOM_FIELD_LABEL,
GROUP_CONCAT(data.DATA SEPARATOR '||') AS CUSTOM_FIELD_DATA
FROM TABLE_requests AS requests
LEFT JOIN TABLE_fields_data AS data ON data.REQUEST_ID = requests.REQUEST_ID
LEFT JOIN TABLE_fields AS fields ON fields.FIELD_ID = data.FIELD_ID
WHERE requests.REQUEST_STATUS = 'COMPLETE'
GROUP BY requests.REQUEST_ID;

How can I merge rows from two joined tables?

There are two tables, which can be joined, and the relationship is 1 to many. I wish the rows of results to be merged.
For example:
Table 1: contacts
.------------.----------.
| contact_id | username |
:------------+----------:
| 1 | user1 |
:------------+----------:
| 2 | user2 |
:------------+----------:
| 3 | user3 |
'------------'----------'
Table 2: documents
.-------------.------------.----------.
| document_id | contact_id | filename |
:-------------+------------+----------:
| 1 | 1 | abc.txt |
:-------------+------------+----------:
| 2 | 1 | bcd.txt |
:-------------+------------+----------:
| 3 | 1 | cde.txt |
:-------------+------------+----------:
| 4 | 2 | 123,txt |
:-------------+------------+----------:
| 5 | 2 | 234.txt |
:-------------+------------+----------:
| 6 | 3 | xyz.txt |
'-------------'------------'----------'
The result I wish I can get:
.------------.----------.---------------------------.
| contact_id | username | filenames |
:------------+----------+---------------------------:
| 1 | user1 | abc.txt, bcd.txt, cde.txt |
:------------+----------+---------------------------:
| 2 | user2 | 123.txt, 234.txt |
:------------+----------+---------------------------:
| 3 | user3 | xyz.txt |
'------------'----------'---------------------------'
Updated:
SELECT c.contact_id, c.username, GROUP_CONCAT(d.filename) as filenames
FROM contacts c
LEFT JOIN documents d
ON c.contact_id = d.contact_id
GROUP BY c.contact_id
You should really post your attempts with your question, so that we can see what you have tried. In that way, it will be easy to push you in the right direction, as well as give the rest of us the impression that you have put some effort into the matter before asking the question. Stackoverflow is not a coding service.
To answer your question,
What you would like to do in this case, is to perform an INNER JOIN on your two tables, and have the MYSQL function, GROUP_CONCAT();, in your SELECT statement.
When you look at your two tables, you have a coherent id (contact_id) that you should use in your INNER JOIN to link your two tables together.
You then, at the end, need to perform a GROUP BY to group your results accordingly, i.e. to group the results by contact_id.
Your SQL would look something like this:
SELECT
tbl_contacts.contact_id,
tbl_contacts.username,
GROUP_CONCAT(tbl_documents.filename) as file_name
FROM
tbl_contacts
INNER JOIN
tbl_documents ON tbl_contacts.contact_id = tbl_documents.contact_id
GROUP BY
tbl_contacts.contact_id
Working SQL fiddle

MySQL Insert to two seperate tables with pivot

Starting with a single table (user_import) brought in from .csv
| Name | Login | Email | CustomA | CustomB |
+------------+-------+----------------------+---------+---------+
| John Smith | johns | john_smith#gmail.com | Blarg | Narx |
| Max Power | maxp | max_power#gmail.com | Jarg | Lipdo |
+------------+-------+----------------------+---------+---------+
Attempting to have it populate a Joomla! users table
| id | name | username | email | ...
| 514 | Super User | admin | admin#gmail.com | ...
| 515 | John Smith | johns | john_smith#gmail.com | ...
| 516 | Max Power | maxp | max_power#gmail.com | ...
and insert any custom custom fields to the user_profiles table
+---------+-------------------------------------+---------------+----------+
| user_id | profile_key | profile_value | ordering |
+---------+-------------------------------------+---------------+----------+
| 515 | customprofile.custom_a | "Blarg" | 1 |
| 515 | customprofile.custom_b | "Jarg" | 2 |
| 516 | customprofile.custom_a | "Narx" | 1 |
| 516 | customprofile.custom_b | "Lipdo" | 2 |
+---------+-------------------------------------+---------------+----------+
I don't think there is a way to do this in a single call as the user_id has to auto_increment
First query is pretty strait forward
INSERT INTO prknc_users (name, username, email, params, password)
SELECT Name, Login, Email, '{}', 'tuChaSw-tEte72_!eSW#muc3#trew8steZacra2e7a7R6yuqAyeSAXUy=Stu'
FROM user_import;`
The second one is the one I need some help with, tried with this for one:
INSERT INTO user_profiles (user_id, profile_key, profile_value, ordering)
SELECT (SELECT users.id FROM users, user_import WHERE users.email = user_import.Email), 'customprofile.custom_a',user_import.CustomA, '1'
FROM user_import;
Failing hard. Please help me out if you can.
You can try an application like this
https://gist.github.com/elinw/5b579e18b9613f08330d
Just make sure to make the changes that make sense in your use case .
Inner select can return multiple values and not insync with other colums in the outer select. You should move the last three columns into the first select.
INSERT INTO user_profiles (user_id, profile_key, profile_value, ordering)
(SELECT users.id, 'customprofile.custom_a',user_import.CustomA, '1'
FROM users, user_import
WHERE users.email = user_import.Email)
INSERT INTO user_profiles (user_id, profile_key, profile_value, ordering)
SELECT users.id,'customprofile.custom_b',user_import.CustomB, '2'
FROM users, user_import
WHERE users.email = user_import.Email
UNION
SELECT users.id,'customprofile.custom_a',user_import.CustomA, '1'
FROM users, user_import
WHERE users.email = user_import.Email;

How to get count of matching strings in a large table

I have a table with following structure:
+-----+-------------------+
| ID | Name |
+-----+-------------------+
| 1 | abc |
+-----+-------------------+
| 2 | abc (duplicate) |
+-----+-------------------+
| 3 | bcd |
+-----+-------------------+
| 4 | bcd (duplicate) |
+-----+-------------------+
| 5 | bcd (duplicate) |
+-----+-------------------+
| 6 | efg |
+-----+-------------------+
| 7 | hij |
+-----+-------------------+
I need to count each Name occurance (with (duplicate) included), i.e.:
+-------------------+--------+
| Name | Count |
+-------------------+--------+
| abc | 2 |
+-------------------+--------+
| bcd | 3 |
+-------------------+--------+
| efg | 1 |
+-------------------+--------+
| hij | 1 |
+-------------------+--------+
I want to mention, that Name column is actually have type TINYTEXT. And there will be very lot of rows in it: 5396 in test mode already. I tried to make self join of table by TRIM(REPLACE(Name, '(duplicate)', '')) with grouping:
SELECT
DISTINCT TRIM(REPLACE(`t`.`Name`, '(duplicate)', '')) as `name`,
COUNT(`s`.`ID`) as `count`
FROM
`Table` as `t` INNER JOIN `Table` as `s` ON
TRIM(REPLACE(`t`.`Name`, '(duplicate)', '')) LIKE TRIM(REPLACE(`s`.`Name`, '(duplicate)', ''))
GROUP BY 1;
And... Well, it took 122.62 sec (?!) with result of 4846 rows on my development machine.
Q1: Was it a correct approach?
Q2: Is there any way to make it faster?
Any help would be appreciated.
Just remove the "duplicate" text:
select replace(name, ' (duplicate)', ''), count(*)
from mytable
group by 1
This should be quicker, although with that many rows you're basically storing a growing array of objects that you're counting and since it's a TINYTEXT field it can be immense.
select Name,count(ID) from Table group by Name
I see what you're saying now. Here's an updated SQL:
select DISTINCT TRIM(REPLACE(Name, ' (duplicate)', ''))
as name, count(ID) from Table group by name

How to combine SUM values of different tables? (Good performance?)

I have following table structure, with many tables like this:
data_1:
+-------+--------+-------+
| views | clicks | date |
+-------+--------+-------+
| 29 | 1 | ..... |
| 458 | 9 | ..... |
+-------+--------+-------+
....
data_43:
+-------+--------+-------+
| views | clicks | date |
+-------+--------+-------+
| 0 | 0 | ..... |
| 0 | 0 | ..... |
+-------+--------+-------+
...
My question here is, how can I get all the sum values in one query?
I tried it with a simple join:
mysql> SELECT SUM(t1.views) data_1_views,
SUM(t1.clicks) data_1_clicks,
SUM(t2.views) data_43_views,
SUM(t2.clicks) data_43_clicks
FROM data_1 t1, data_43 t2;
But my result isn't what I expected:
+--------------+---------------+---------------+----------------+
| data_1_views | data_1_clicks | data_43_views | data_43_clicks |
+--------------+---------------+---------------+----------------+
| NULL | NULL | NULL | NULL |
+--------------+---------------+---------------+----------------+
I expected it to be:
+--------------+---------------+---------------+----------------+
| data_1_views | data_1_clicks | data_43_views | data_43_clicks |
+--------------+---------------+---------------+----------------+
| 487 | 10 | 0 | 0 |
+--------------+---------------+---------------+----------------+
Anyone here who can help me with this query?
I am also interested if this is more performant than doing all this stuff in multiple queries
In this case multiple queries are fine especially if you are grouping many rows. There is virtually no benefit (and actually potential bugs) lumping these two tables in one query.
I would just make two queries
SELECT SUM(t1.views) data_1_views, SUM(t1.clicks) data_1_clicks
FROM data_1 t1
SELECT SUM(t2.views) data_43_views, SUM(t2.clicks) data_43_clicks
FROM data_43 t2;
Use Union Query.
Like below:
SELECT sum(t.data_1_views) as data_1_views,SUM(t.data_1_clicks) as data_1_clicks,
SUM(t.data_43_views) as data_43_views,SUM(t.data_43_views) as data_43_views
FROM (
SELECT SUM(t1.views) data_1_views,SUM(t1.clicks) data_1_clicks,
0 as data_43_views,0 as data_43_clicks
FROM FROM data_1 as t1
UNION
SELECT 0 as data_1_views,0 as data_1_clicks,SUM(t1.clicks) as data_43_views, SUM(t2.clicks)
FROM data_43_clicks as t2
) as t GROUP BY 1