Merging rows and updating related foreign key - mysql

I have a current users table. A distinct user is defined as when the email and phoneNumber together are unique. Currently the table looks like this:
And another table called giftee_info which has the foreign key on column userId to users.id:
The users table is going to be parsed out into 2 tables: users and user_metadata. Now a distinct user will be defined by the phoneNumber. So you can see in the data above, users with id's 4 and 5 are the same, and have to be merged.
The new users table will look like:
And the new user_metadata table will look like this:
Note how the 4th row has userId of 4, so users 4 and 5 have merged to one user.
And giftee_info will look like this:
See how the 3rd row in giftee_info contains userId 4, as the user with id 5 has been merged into one user.
The data I've provided is basic. In reality there are many rows, and a user with the same number may have 5 different email address (and so are currently treated as separate users in the current schema).
The part I'm having most trouble with is updating giftee_info. So any rows with userId's that have been merged down into one user need to be updated. How can I do this?

Since phonenumber can be NULL, I'm using externalid as the unique identifier below.
Start by creating the new users table from the distinct phone numbers in the old users table:
CREATE TABLE new_users (id INT PRIMARY KEY AUTO_INCREMENT, externalid VARCHAR(32), phonenumber VARCHAR(32))
SELECT DISTINCT NULL, externalid, phonenumber
FROM users
Then put all the emails into the user_metadata table, by joining the old and new users tables to get the emails along with the new user IDs.
CREATE TABLE user_metadata (id INT PRIMARY KEY AUTO_INCREMENT, userid INT, email VARCHAR(100), subscribe INT(1))
SELECT NULL, nu.id, u.email, 0
FROM new_users as nu
JOIN users AS u ON nu.externalid = u.externalid
Now you can update giftee_info by replacing the old user IDs with the new user IDs.
UPDATE giftee_info AS g
JOIN users as u ON g.userid = u.userid
JOIN new_users As nu ON nu.externalid = u.externalid
SET g.userid = nu.userid
Once this is all done you can rename the tables so new_users is now users.

Related

Insert one column DISTINCT, with corresponding other columns from one table to another

I have a problem with database query. I have three tables projects, developers and email. In developers table, there are a lot of rows with same name but different email. I have to insert the distinct names but all the emails(in the row of name to which they belong) in email table i.e
example
/////////////////////////////////////////////
developers table have records:-
id_developer project_id name email
0 1 umar umar#gmail.com
1 1 umar umar#developers.com
Now i want to inert the data in email table as:-
user_id name email_ids
0 umar umar#gmail.com
umar#developers.com
////////////////////////////////////////////
projects
----------
id_project
name
----------
developers
----------
id_developer
project_id
name
email
----------
email
----------
user_id
name
email_ids
----------
Following is my current query. Please help me. Thanks in advance
INSERT INTO email(user_id, dev_name, email_ids)
SELECT p.id_project,
d.name,
d.email
FROM projects p
INNER JOIN developers AS d
ON p.id_project = d.project_id
WHERE d.name IN (SELECT name
FROM developers
GROUP BY name HAVING(COUNT(name) > 1 ))
GROUP BY(d.name)
After some conversation in the comments what you really need is a proper data modeling here.
Having the data the way you wan't in the database is a very bad practice.
user_id name email_ids
0 umar umar#gmail.com
umar#developers.com
You will end it up having problems in the future to retrieves this data because you will have to figure out a way how to retrieve or split this data when you need then.
So, based on your current model to attend your requirement you would need just to change the table email a bit. Your model would be this way:
projects developers email
---------- ------------- ------------
id_project id_developer id
name project_id id_developer
name email
---------- ------------- ------------
So, since you already have the data in the developers table lets first drop table table email and recreate it the right way. You will need to execute:
drop table email;
create table dev_email( -- changed the name because there is a field with same name
id INTEGER AUTO_INCREMENT NOT NULL,
id_developer INTEGER NOT NULL, -- this column should be the same type
-- as id_developer in the table developers
email VARCHAR(150) NOT NULL
PRIMARY KEY pk_email (id),
CONSTRAINT uk_developer_email UNIQUE (id_developer, email), -- that will avoid duplicates,
CONSTRAINT fk_dev FOREIGN KEY (developer_id)
REFERENCES developers(id_developer)
ON UPDATE RESTRICT ON DELETE RESTRICT
);
Now lets fill this table with the right data:
INSERT INTO dev_email (id_developer, email)
SELECT min(id_developer), email
FROM developers
GROUP BY email;
After that we must delete the duplicated data from the developers table like so:
DELETE FROM developers d
WHERE NOT EXIST (SELECT 1
FROM dev_email de
WHERE de.id_developer = d.id_developer);
Then we drop the column that is no longer needed in the developers table:
ALTER TABLE developers DROP COLUMN email;
This should give you a proper normalized model.
Now if you need to retrieve the developer with all emails concatenated (which is simpler than to split it) you just do:
SELECT d.id_developer,
d.name,
GROUP_CONCAT(e.email, ', ') as emails
FROM developers d
INNER JOIN dev_email e
ON d.id_developer = e.id_developer
GROUP BY d.id_developer,
d.name
PS.: I did all of this out of my head, please run it in a test environment first (a copy of your current database to be safe). It should be ok but better safe than sorry right?

MySQL Delete-Select row

I'm trying to delete a row from table subscription where there is two foreign Keys (id_user and id_journal). The information that I have is email from table user and nome from table journal. The deleted row needs to match user.email and journal.nome. I can't find a solution. How can I do it?
Table user:
id
name
email
password
Table journal:
id
name
Table Subscription:
id
id_user
id_journal
The last two queries that I tried:
DELETE FROM assinatura WHERE (
SELECT tbluser.id, journal.id
FROM tbluser, journal
WHERE email = '$email' AND nome = '$nome')
DELETE FROM assinatura
INNER JOIN tbluser on (tbluser.email = '$email')
INNER JOIN journal on (journal.nome = '$nome')
I've tried many others queries, but unsuccessful. I think it's important to say that I'm new at MySQL.
DELETE
FROM Subscription
WHERE id_user IN (
SELECT usr.id
FROM user AS usr
WHERE usr.email = INPUT_EMAIL
)
AND id_journal IN (
SELECT jrnl.id
FROM journal AS jrnl
WHERE jrnl.name = INPUT_NAME
)
On another topic ...
Try to avoid excess subscriptions for same user/journal combo by
CREATE TABLE subscription
(
id int NOT NULL AUTO_INCREMENT primary key,
id_user int not null,
id_journal int not null,
UNIQUE KEY `user_journal_intersect` (`id_user`,`id_journal`)
-- note Alan stated FK RI in place already
);
U can PK on composite instead, of course (ditching the id column), programmer pref

Create new records based on primary key of different table mysql

Let say I have two tables called users and user_profiles
user_profiles is empty without any rows, meanwhile users table has about 10-20k records.
I want to create empty records for user_profiles based upon users primary key
for question sake,
lets assume that
my users table has
id name email
my user_profiles table has
id user_id data
Is it possible to do strictly via SQL without involving any server-side script?
The end outcome, whatever amount of users I have, there should be rows of user_profiles matching this.
Try following provided that your tables are structured properly with ALLOW NULL and auto_increment for user_profiles properties.
INSERT INTO user_profiles (user_id)
SELECT id FROM users

Find row based on set of foreign rows

I've got 3 tables for a chat system:
conversation this table holds an id and timestamp
conversation_participant tells me which users are in the chat
conversation_message the messages send within this chat (also holds sender_id)
This setup works quite good for me but now I want to do a 'reverse' lookup. I know which users are going to start a new chat and I want to know if this specific group already has a conversation in the DB. Is there a way to find a row based on a dynamic set of foreign rows?
(preferable without pivot like tricks)
Or is my database design flawed and should I alter that?
CONVERSATION
id int auto_increment
start timestamp
CONVERSATION_PARTICIPANT
conversation_id int (foreign key to conversation)
participant_id int (foreign key to users table)
CONVERSATION_MESSAGE
id int auto_increment
conversation_id int (foreign key to conversation)
author_id int
time timestamp
message text
This assumes you:
have a list of comma delimited participant_id's -> $list
know the number of participants -> $qty
use one of the participants in the WHERE clause -> $participant
replace those pseudo-variable with real values
You can see the code here: http://sqlfiddle.com/#!2/e90f2/11
Code:
SELECT conversation_participant.conversation_id AS conversation_id
, SUM(IF(members.participant_id IN ($list),1,0)) AS member_count
, COUNT(*) AS total
FROM conversation_participant
JOIN conversation_participant AS members
ON members.conversation_id = conversation_participant.conversation_id
WHERE conversation_participant.participant_id = $participant
GROUP BY conversation_id
HAVING member_count = total
AND member_count = $qty;
FYI: The purpose of the WHERE clause is to limit the number of potential conversations.

MySql Join Tables with List of Indexes in one table's Column?

I have MySQL database with two tables, Users and Games. Basically each user has a unique ID and a set of games that they are playing and each game table has an ID and HasStarted flag (when HasStarted = false players can join the game). The table was set up so the users list of games a string of ID numbers separated by semi-colons (EX: 1;3;5; means they are part of game 1, 3, and 5). Create examples at bottom
What I would like to do is fetch all the games that are joinable AND that they are not already a part of.
I tried using
SELECT * FROM games INNER JOIN users ON games.GID = users.gamelist
WHERE games.status = 0;
but that only matches the games based on the first game in their list. What I'm going to do for now is just fetch all the joinable games into an array and then get the player's list of games and drop those but I was wondering if there was a way to fetch them all in one query. Does anyone know if I can do something like this?
CREATE TABLE users
(UID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(UID),
games TINYTEXT)
CREATE TABLE games
(GID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(GID),
has_started BIT)
You REALLY should alter your schema to have a users_to_games table to normalize your data. The setup could look like this:
users
------
uid - primary key
[other fields specific to a single user]
games
------
gid - primary key
has_started
[other fields specific to a single game]
users_to_games
--------------
uid - first field in compound primary key
gid - second field in compound primary key
There would be one record in users_to_games for every game a particular user is involved in.
Now in order to get your list of games that have not started but user has not joined you could do this:
SELECT gid, [any other fields from game table you want for display]
FROM games
WHERE is_started = 0
AND gid NOT IN (SELECT DISTINCT gid FROM users_to_games WHERE uid = ?)