MySQL Insert to two seperate tables with pivot - mysql

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;

Related

Selecting values from second column alongside the values from first column in the same row

I am trying to get values matching the value from the second column. For example, I want to know who is the sender for Bill Gates by only using IDs.
I have two tables,
*users* table
| user_ID | Full_name |
| -------- | -------------- |
| 1 | Steve Jobs |
| 2 | Bill Gates |
| 3 | Elon Musk |
*relationships* table (with both column foreign keys)
| user_sender | user_receiver |
| ------------ | -------------- |
| 1 | 2 |
| 3 | 1 |
| 3 | 2 |
I want to select based on "user_receiver" column the matching values in the column "user_sender"
For example, I want to know who is user_sender for 2
OUTPUT:
| | |
| ------------ | -------------- |
| 1 | 2 |
| 3 | 2 |
You need to join the tables and select the rows you want
you have access to all columns of both tables by addressing them with their alias
SELECT u.user_ID , u.Full_name,r.user_receiver
FROM users u JOIN
relationships r ON u.user_ID = r.user_sender
WHERE r.user_receiver = 2
If you want to look based on the name, then join the relationships to users.
SELECT
rel.user_sender
, rel.user_receiver
-- , sender.Full_name AS sender_name
-- , receiver.Full_name AS receiver_name
FROM relationships AS rel
JOIN users AS sender ON sender.user_ID = rel.user_sender
JOIN users As receiver ON receiver.user_ID = rel.user_receiver
WHERE receiver.Full_name = 'Bill Gates'
If you already know the user_receiver number, and you only want the ID's
SELECT *
FROM relationships
WHERE user_receiver = 2

MySQL: How to make a query between 2 tables that returns NULL to a row that isn't in the 2nd table?

I have this 2 tables
1st Table "Users"
+----+-----------+----------+
| ID | FirstName | LastName |
+----+-----------+----------+
| 1 | Jeff | Bezos |
| 2 | Bill | Gates |
| 3 | Elon | Musk |
+----+-----------+----------+
2nd Table "Records"
+----+--------+------------+
| ID | IDUser | RecordDate |
+----+--------+------------+
| 1 | 1 | 15/06/2021 |
| 2 | 2 | 05/06/2021 |
| 3 | 2 | 12/06/2021 |
| 4 | 2 | 02/06/2021 |
| 5 | 1 | 17/06/2021 |
+----+--------+------------+
So this 2 tables are linked each other by using a Foreing key Records.IDUsers -> Users.ID
I wanted to make a query that does this
+-----------+----------+----------------+--------------------+
| FirstName | LastName | Lastest Record | Numbers of Records |
+-----------+----------+----------------+--------------------+
| Jeff | Bezos | 17/06/2021 | 2 |
| Bill | Gates | 12/06/2021 | 3 |
| Elon | Musk | NULL | NULL |
+-----------+----------+----------------+--------------------+
You need to use LEFT JOIN in order to get back users without records too; then the MAX and COUNT aggregate functions.
First version: This will return 0 for the number of records instead of NULL, when there are no records for a specific user. Latest record will be NULL as expected.
SELECT
FirstName,
LastName,
MAX(RecordDate) AS LatestRecord,
COUNT(Records.ID) AS NumberOfRecords
FROM Users LEFT JOIN Records on Users.ID = Records.IDUser
GROUP BY Users.ID;
If you want NULL instead of 0 (which normally you do not want), you can use the IF function like this:
SELECT
FirstName,
LastName,
MAX(RecordDate) AS LatestRecord,
IF(COUNT(Records.ID) > 0, COUNT(Records.ID), NULL) AS NumberOfRecords
FROM Users LEFT JOIN Records on Users.ID = Records.IDUser
GROUP BY Users.ID;
Second version: It might happen that running the above query will return an error, something like:
Error: ER_WRONG_FIELD_WITH_GROUP: ...; this is incompatible with sql_mode=only_full_group_by
This happens when/if the ONLY_FULL_GROUP_BY SQL mode is enabled (which it is by default since MySQL 5.7.5). In order to get around this error, you can use the ANY_VALUE function to select the nonaggregated fields:
SELECT
ANY_VALUE(FirstName) AS FirstName,
ANY_VALUE(LastName) AS LastName,
MAX(RecordDate) AS LatestRecord,
COUNT(Records.ID) AS NumberOfRecords
FROM Users LEFT JOIN Records on Users.ID = Records.IDUser
GROUP BY Users.ID;
left join select all user even if does not have records
select * from users left join records on records.IDUser = ID;

Merge multiple rows together 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

SQL: Remove rows with a duplicate field

I have duplicate entries due to a programming error.
Table Example:
id | group | userNum | username | name
---------------------------------------
1 | AA11 | D-01 | user1 | Donald
2 | AA11 | D-02 | user2 | Cruz
3 | AA11 | D-03 | user3 | Rubio
4 | AA11 | D-01 | user1 | Donald <------DUPLICATE
5 | AA11 | D-04 | user4 | Cruz
6 | AA22 | D-03 | user2 | Rubio
7 | AA22 | D-02 | user1 | Donald
userNum, username must be unique to each group, but a group can have userNum, username, name which are found in other groups.
A standard SQL approach is to use a subquery in the WHERE clause. For instance:
delete from example
where id not in (select min(id)
from example e2
group by group, userNum, username, name
);
This doesn't work in MySQL. You can do something similar using left join:
delete e
from example e left join
(select min(id) as minid
from example e2
group by group, userNum, username, name
) ee
on e.id = ee.minid
where ee.minid is null;
Add group by userNum, username, group at the end of your query..

Mysql include column with no rows returned for specific dates

I would like to ask a quick question regarding a mysql query.
I have a table named trans :
+----+---------------------+------+-------+----------+----------+
| ID | Date | User | PCNum | Customer | trans_In |
+----+---------------------+------+-------+----------+----------+
| 8 | 2013-01-23 16:24:10 | test | PC2 | George | 10 |
| 9 | 2013-01-23 16:27:22 | test | PC2 | Nick | 0 |
| 10 | 2013-01-24 16:28:48 | test | PC2 | Ted | 10 |
| 11 | 2013-01-25 16:36:40 | test | PC2 | Danny | 10 |
+----+---------------------+------+-------+----------+----------+
and another named customers :
+----+---------+-----------+
| ID | Name | Surname |
+----+---------+-----------+
| 1 | George | |
| 2 | Nick | |
| 3 | Ted | |
| 4 | Danny | |
| 5 | Alex | |
| 6 | Mike | |
.
.
.
.
+----+---------+-----------+
I want to view the sum of trans_in column for specific customers in a date range BUT ALSO include in the result set, those customers that haven't got any records in the selected date range. Their sum of trans_in could appear as NULL or 0 it doesn't matter...
I have the following query :
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
WHERE Customer IN('George','Nick','Ted','Danny')
AND `Date` >= '2013-01-24'
GROUP BY Customer
ORDER BY input DESC;
But this will only return the sum for 'Ted' and 'Danny' because they only have transactions after the 24th of January...
How can i include all the customers that are inside the WHERE IN (...) function, even those who have no transactions in the selected date range??
I suppose i'll have to join them somehow with the customers table but i cannot figure out how.
Thanks in advance!!
:)
In order to include all records from one table without matching records in another, you have to use a LEFT JOIN.
SELECT
t.`Date`,
c.name,
SUM(t.trans_in) AS 'input'
FROM customers c LEFT JOIN trans t ON (c.name = t.Customer AND t.`Date` >= '2013-01-24')
WHERE c.name IN('George','Nick','Ted','Danny')
GROUP BY c.name
ORDER BY input DESC;
Of course, I would mention that you should be referencing customer by ID, and not by name in your related table. Your current setup leads to information duplication. If the customer changes their name, you now have to update all related records in the trans table instead of just in the customer table.
try this
SELECT
`Date`,
Customer,
SUM(trans_in) AS 'input'
FROM trans
inner join customers
on customers.Name = trans.Customer
WHERE Customer IN('George','Nick','Ted','Danny')
GROUP BY Customer
ORDER BY input DESC;