I just want to update automatically a column of 2 tables linked with foreign keys.
To more, these columns have to contain not just one value, but a list of values.
Here is my example :
Table Name : members
Member name : Demo User
Assigned Tasks : <List of tasks assigned to "Demo User">
Table Name : tasks
Task Name : Modeling
Assigned To : <List of users assigned to "Modeling">
In each table, I want to be able to insert value in the column, and the other table is automatically updated.
Example :
Table Name : members
Member name : Demo User
Assigned Tasks : Modeling,Mapping
Table Name : tasks
Task Name : Modeling
Assigned To : AUTOMATIC UPDATE ->Demo User
Task Name : Mapping
Assigned To : AUTOMATIC UPDATE ->Demo User
I tried with foreign keys (using innoDB), but I just succeed to insert one value in the column, and the other table was not updated automatically...
Thank you for your help !
Never, ever store delimited values in a database. Normalize your data by creating a many-to-many table (in your case let's call it assigned_tasks) instead. It will pay off big time in a long run by enabling you to normally maintain and query your data.
That being said your schema might look like
CREATE TABLE members
(
member_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
member_name VARCHAR(32)
);
CREATE TABLE tasks
(
task_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
task_name VARCHAR(32)
);
CREATE TABLE assigned_tasks
(
member_id int,
task_id int,
PRIMARY KEY (member_id, task_id),
FOREIGN KEY (member_id) REFERENCES members (member_id),
FOREIGN KEY (task_id) REFERENCES tasks (task_id)
);
Now if you need a representation with a delimited list of tasks per user you can use GROUP_CONCAT() and GROUP BY
SELECT m.member_id, m.member_name, GROUP_CONCAT(t.task_name) tasks
FROM assigned_tasks a JOIN members m
ON a.member_id = m.member_id JOIN tasks t
ON a.task_id = t.task_id
GROUP BY m.member_id, m.member_name
Output:
| MEMBER_ID | MEMBER_NAME | TASKS |
|-----------|-------------|------------------|
| 1 | Demo User | Modeling,Mapping |
Here is SQLFiddle demo
Related
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?
so, I have 3 tables:
teachers
-------
id_teacher PK
name_teacher
grades
-------
id_grade PK
name_grade
activities
-------
id_activity PK AI
id_teacher FK
id_grade FK
name_activity NULL
I want my database in mySQL to combine all the teachers with all the grades in the table "activities" and leaving the "name_activity" field as NULL to fill it out later. I want to do it automatically. Does it make sense ? How can I do it?
or should I take another approach?
You want a Cartesian product which can be achieved via a cross join
INSERT INTO `activities` (id_teacher, id_grade)
SELECT t.id_teacher, g.id_grade
FROM `teachers` t, `grades` g
How do you set up a valid auto-incrementing integer primary key on a table if you want to join it with separate files? I get data like this on a daily basis:
Interaction data:
Date | PersonID | DateTime | CustomerID | Other values...
The primary key there would be PersonID + DateTime + CustomerID. If I have an integer key, how can I get that to relate back to another table? I want to know the rows where a specific person interacted with a specific customer so I can tie back those pieces of data together into one master-file.
Survey return data:
Date | PersonID | DateTime | CustomerID | Other values...
I am normally processing all raw data first in pandas before loading it into a database. Some other files also do not have a datetime stamp and only have a date. It is rare for one person to interact with the same customer on the same day so I normally drop all rows where there are duplicates (all instances) so my sample of joins are just purely unique.
Other Data:
Date | PersonID | CustomerID | Other values...
I can't imagine how I can set it up so I know row 56,547 on 'Interaction Data' table matches with row 10,982 on 'Survey Return Data' table. Or should I keep doing it the way I am with a composite key of three columns?
(I'm assuming postgresql since you have tag-spammed this post; it's up to you to translate for other database systems).
It sounds like you're loading data with a complex natural key like (PersonID,DateTime,CustomerID) and you don't want to use the natural key in related tables, perhaps for storage space reasons.
If so, for your secondary tables you might want to CREATE UNLOGGED TABLE a table matching the original input data. COPY the data into that table. Then do an INSERT INTO ... SELECT ... into the final target table, joining on the table with the natural key mapping.
In your case, for example, you'd have table interaction:
CREATE TABLE interaction (
interaction_id serial primary key,
"PersonID" integer
"DateTime" timestamp,
"CustomerID" integer,
UNIQUE("PersonID", "DateTime", "CustomerID"),
...
);
and for table survey_return just a reference to interaction_id:
CREATE TABLE survey_return (
survey_return_id serial primary key,
interaction_id integer not null foreign key references interaction(interaction_id),
col1 integer, -- data cols
..
);
Now create:
CREATE UNLOGGED TABLE survey_return_load (
"PersonID" integer
"DateTime" timestamp,
"CustomerID" integer,
PRIMARY KEY ("PersonID","DateTime", "CustomerID")
col1 integer, -- data cols
...
);
and COPY your data into it, then do an INSERT INTO ... SELECT ... to join the loaded data against the interaction table and insert the result with the derived interaction_id instead of the original natural keys:
INSERT INTO survey_return
SELECT interaction_id, col1, ...
FROM survey_return_load l
LEFT JOIN interaction i ON ( (i."PersonID", i."DateTime", i."CustomerID") = (l."PersonID", l."DateTime", l."CustomerID") );
This will fail with a null violation if there are natural key tuples in the input survey returns that do not appear in the interaction table.
There are always many ways. Here might be one.
A potential customer (table: cust) walking into a car dealership and test driving 3 cars (table: car). An intersection/junction table between cust and car in table cust_car.
3 tables. Each with int autoinc.
Read this answer I wrote up for someone. Happy to work your tables if you need help.
SQL result table, match in second table SET type
That question had nothing to do with yours. But the solution is the same.
Three tables: users, roles and a pivot table (many to many) role_user.
user:
- id
- name
role:
- id
- name
role_user
- id
- user_id: foreign key link to user
- role_id: foreign key link to role
If I wanted to limit the amounts of maximum roles a user can have to only 1 for example, I could put the role_id foreign link on the user as a role_1 field instead of using a pivot table of many to many.
users:
- id
- name
- role_id_1
The same goes if I wanted only two roles per user.
users:
- id
- name
- role_id_1
- role_id_2
What if I wanted to limit the amount to 1, 2 or something else using a pivot table (Not using foreign role links on the user table) ? Is there an option for that in sql ?
Something like a composite unique index option including role_id and user_id in the pivot table, but instead of a constraint on the uniqueness, a custom constraint on the limit of the user_id number of appearances.
There is a way you can implement this in SQL without triggers. It is a bit complicated, but you could do it.
It starts by adding another table. Let me call it RoleNumbers. This table would consist of one row for each possible role for a user. So, you set it up with 1, 2, or however many roles you want.
Then for the junction table:
create table UserRoles (
UserRoleId int not null auto_increment primary key,
UserId int not null references users(user_id),
RoleId int not null references roles(role_id),
RoleNumber int not null references RoleNumbers(Number),
unique (UserId, RoleId),
unique (UserId, RoleNumber)
);
This uses my naming conventions. I have no problem with having a synthetic key on a junction table.
When you insert a new record, you would have to assign a value to RoleNumber that is not already being used. Hence, you get the limit. The most efficient way to do this is via triggers, but that is not strictly necessary. You could do an insert as:
insert into UserRoles(UserId, RoleId, RoleNumber)
select $UserId, $RoleId, coalesce(max(RoleNumber), 0) + 1
from UserRoles
where UserId = $UserId;
delete would require a separate query for maintaining the numbering scheme.
I am looking for the best way to prevent duplicate data in a table that is based on another data point.
Table: (a joining table between two entities, person and school)
CREATE TABLE with_school (
person_id INTEGER NOT NULL,
school_id INTEGER NOT NULL,
type_id INTEGER NOT NULL,
PRIMARY KEY (person_id,school_id)
);
person_id and school_id are also foreign keys, declared in a different statement.
What I want is something that prevents a person from adding the same school more than once.
Examples:
Row 1:
person_id = 1
school_id = 1
Row 2:
person_id = 1
school_id = 2
is okay, but:
Row 1:
person_id = 1
school_id = 1
Row 2:
person_id = 1
school_id = 1
is not.
What would be the easiest way to prevent these kinds of duplicates?
I have tried using a trigger, but I haven't been able to make it work the way I want it to:
ALTER TABLE with_school
ADD CHECK (
school_id != (
SELECT school_id
FROM with_school
WHERE person_id = person_id
)
);
(I cannot differentiate between the initial person_id and the one it is checking)
You could try creating uniqueness at the database level....like:
ALTER TABLE with_school ADD UNIQUE uniqueindex (person_id, school_id);
And...probably in addition....you could do an sql select 'check' at the application level before inserting to make sure you don't already have those two keys.