Oracle SQL Selecting data from one table that relates to another - sql

I do not have that much knowledge of SQL and it is my job to create a basic social network with friendships. To do this, I have created one table (there is more columns but I have removed them for the sake of this question);
CREATE TABLE USERS
(USER_ID CHAR(8) NOT NULL,
USER_LNAME VARCHAR(20) NOT NULL,
USER_FNAME VARCHAR(20) NOT NULL,
PRIMARY KEY (USER_ID));
I have created another table in which you can insert the user_id of two people and it generates the time in which they became friends.
CREATE TABLE USERFRIEND
(USER_ID CHAR(8) NOT NULL,
FRIEND_ID CHAR(8) NOT NULL,
EST_DATE TIMESTAMP NOT NULL,
PRIMARY KEY (USER_ID, FRIEND_ID),
FOREIGN KEY (USER_ID) REFERENCES USERS (USER_ID),
FOREIGN KEY (FRIEND_ID) REFERENCES USERS (USER_ID));
I do not have any trouble inserting the data for either table. For example, here is a sample of the data I have inserted (my actual database has around 15 friends)
INSERT INTO USERS
VALUES ('10000001', 'Jones', 'Tom');
INSERT INTO USERS
VALUES ('10000002', 'Smith', 'Michael');
INSERT INTO USERS
VALUES ('10000003', 'Johnson', 'Andrew');
INSERT INTO USERS
VALUES ('10000004', 'Williams', 'David');
and the friendships
INSERT INTO USERFRIEND
VALUES ('10000001', '100000002', CURRENT_TIMESTAMP);
INSERT INTO USERFRIEND
VALUES ('10000001', '100000003', CURRENT_TIMESTAMP);
INSERT INTO USERFRIEND
VALUES ('10000002', '100000004', CURRENT_TIMESTAMP);
One of the tasks is to get a list of friends that one person has.
For example,
Michael smith would be friends with Tom Jones and David Williams (01, 04)
My issue is that in one friendship, Michael is the user_id and in the other, the friend_id
The best result I have worked out so far is this code;
SELECT user_id, friend_id
FROM USERFRIEND
WHERE USER_ID = 10000002
OR FRIEND_ID = 10000002;
This at least returns the results of all the IDs of people that Smith (10000002) is friends with but I am not sure how to make it return this with the correlating names of those people when both USER_ID and FRIEND_ID both reference the same thing.
SELECT DISTINCT a.user_fname, a.user_lname, b.USER_ID, b.FRIEND_ID
FROM USERS a, USERFRIEND b
WHERE b.USER_ID = 10000002
OR b.FRIEND_ID = 10000002;
I have tried this code and although in my database, there is only 20 friendships made, it returns 80 results and none of the names match up correctly.
Hopefully one of you will find the (probably simple) solution for me because I have no idea what I should be doing.

Your query above makes a cartesian product since there is no join between both the tables. You should have atleast n-1 joins where n is the number of tables used.
You can join both tables like this -
SELECT DISTINCT a.user_fname, a.user_lname, b.USER_ID, b.FRIEND_ID
FROM USERS a, USERFRIEND b
WHERE a.user_id=b.user_id
or a.user_d=b.friend_id

Related

Create a view that joins data from other table multiple cells into one oracle sql

I need to create a view that combines two tables (game and developer) together and selects gameID gameName and gameDeveloper from the game table and repaces gameDeveloper with firstname and lastname (from developers table) into one column. gameDeveloper is the same as developerID, but it is not a foreign key yet on purpose. I want to select only firstname and lastname from the row that contains the matching developerID and combine them in one column in my new view. Heres what I have:
CREATE TABLE game (
gameID varchar(3) PRIMARY KEY NOT NULL,
gamenameName varchar(30) NOT NULL,
gameDeveloper varchar(3) NOT NULL
);
INSERT INTO game VALUES (1,'RDR2',2);
INSERT INTO game VALUES (2,'GTAV',7);
INSERT INTO game VALUES (3,'PUBG',9);
/
CREATE TABLE developers (
developerID varchar(3) PRIMARY KEY NOT
NULL,
fname varchar(20) NOT NULL,
lname varchar(20) NOT NULL,
gameID varchar(3) NOT NULL,
CONSTRAINT fk_game
FOREIGN KEY (gameID)
REFERENCES game (GameID)
);
INSERT INTO developers VALUES
(1,'Patrick','Kane',1);
INSERT INTO developers VALUES
(2,'Jonathan','Toews',1);
INSERT INTO developers VALUES
(3,'Alex','Debrincat',1);
INSERT INTO developers VALUES
(4,'Andrew','Shaw',2);
INSERT INTO developers VALUES
(5,'Alex','Nylander',2);
INSERT INTO developers VALUES
(6,'Oli','Maata',2);
INSERT INTO developers VALUES
(7,'Calvin','DeHaan',2);
INSERT INTO developers VALUES
(8,'Brandon','Saad',3);
INSERT INTO developers VALUES
(9,'Corey','Crawford',3);
INSERT INTO developers VALUES
(10,'Connor','Murphy',3);
/
CREATE OR REPLACE VIEW chairs AS
SELECT firstname, lastname
FROM developer
INNER JOIN
I'd like the final table to look something like this with the mapped and combined last cell but I am so lost on what to do.. I figured an inner join would be best?
You can do something like this - JOIN is the equivalent to INNER JOIN but you can be explicit.
CREATE VIEW chairs
AS
SELECT
g.gameID
,g.gamenameName
,d.fname + ' ' + lname AS gameDeveloper
FROM game g
JOIN developers d
ON g.gameDeveloper = d. developerID

How can i make a calculator in SQL?

So i have this assingment in school, were i am supposed to make a tool for a DPO in a firm. The assignment is as follows: "You are required to develop an app or on-line tool where you can enter the status of the many details that the regulations states for the company. The intended user of the tool is a Data Protection Officer (DPO) or an employee responsible for data projection in general. The tool shall be able to handle input from more than one company and more than one audit for the individual company. The tool shall be able to calculate the level of compliance to the GDPR." So as for the calculator i thought about a solution, where the DPO for a given company, can give one of the 12 regulations either a 1 or 0, where 1 is that the regulation is complied and 0 is not. So i wanted to take the sum and then divide with 12, to get the compliance average number. But how do i do this in SQL?
As of now, i have already made the comany, auditor and regulation table, which looks like this:
CREATE TABLE IF NOT EXISTS`Companies` (
`idCompanies` INT NOT NULL,
`cvr_nr` INT NULL,
`Dato` DATE NULL,
PRIMARY KEY (`idCompanies`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `Auditors` (
`idAuditors` INT NOT NULL,
`Auditor_name` VARCHAR(45) NULL,
PRIMARY KEY (`idAuditors`))
ENGINE = InnoDB;
insert into auditors values (1, "Lars Larsen");
insert into auditors values (2, "Henrik Andersen");
insert into auditors values (3, "Jens Andersen");
drop table if exists regulations;
CREATE TABLE IF NOT EXISTS `Regulations` (
`idRegulations` INT NOT NULL auto_increment,
`Regulation_name` VARCHAR(100) NULL,
`Regulation_details` VARCHAR(400) NULL,
PRIMARY KEY (`idRegulations`))
ENGINE = InnoDB;
insert into regulations values (null, "xx", "xxx");
insert into regulations values (null, "xx", "xxx");
#The XX are just as examples.
CREATE TABLE IF NOT EXISTS `Companies_has_Regulations` (
`Companies_idCompanies` INT NOT NULL,
`Regulations_idRegulations` INT NOT NULL,
PRIMARY KEY (`Companies_idCompanies`, `Regulations_idRegulations`),
INDEX `fk_Companies_has_Regulations_Regulations1_idx` (`Regulations_idRegulations` ASC),
INDEX `fk_Companies_has_Regulations_Companies1_idx` (`Companies_idCompanies` ASC),
CONSTRAINT `fk_Companies_has_Regulations_Companies1`
FOREIGN KEY (`Companies_idCompanies`)
REFERENCES `Companies` (`idCompanies`)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_Companies_has_Regulations_Regulations1`
FOREIGN KEY (`Regulations_idRegulations`)
REFERENCES `Regulations` (`idRegulations`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
Select all rows from Regulations, LEFT JOIN Companies_has_Regulations ON the regulation id and filter the company in question. Then you got all regulations and for each row an entry in Companies_has_Regulations existed the values from Companies_has_Regulations in additional columns. For a row from Regulations where no entry existed in Companies_has_Regulations these columns have null values. Now you can use count() over the ID from Regulations to get the count of all regulations and count() over Regulations_idRegulations to get the count of all regulations the company complies with (count(column) doesn't count rows where column IS NULL). Put that in a division and you got your rate of compliance.
SELECT count(chr.Regulations_idRegulations)
/
count(r.idRegulations) level_of_compliance
FROM Regulations r
LEFT JOIN Companies_has_Regulations chr
ON chr.Regulations_idRegulations = r.idRegulations
WHERE chr.Companies_idCompanies = <ID for the company in question>;
Replace <ID for the company in question> with the respective company ID.

How to deal with an id that needs to be matched to multiple ids in SQLite?

I'm new to databases, so I'll start by showing what I would do if I was using a simple table in a csv file. Presently, I'm building a Shiny (R) app to keep track of people taking part in studies. The idea is to make sure no one is doing more than one study at the same time, and that enough time has passed between studies.
A single table would look like something like this:
study_title contact_person tasks first_name last_name
MX9345-3 John Doe OGTT Michael Smith
MX9345-3 John Doe PVT Michael Smith
MX9345-3 John Doe OGTT Julia Barnes
MX9345-3 John Doe PVT Julia Barnes
...
So each study has a single contact person, but multiple tasks. It is possible other studies will use the same tasks.
Each task should have a description
Each person can be connected to multiple studies (the final database has timestamps to make sure this does not happen at the same time), and consequently repeat the same tasks.
the SQLite code could look something like this
CREATE TABLE studies (study_title TEXT, contact_person TEXT);
CREATE TABLE tasks (task_name TEXT, description TEXT);
CREATE TABLE participants (first_name TEXT, last_name TEXT);
Now I'm stuck. If I add a primary key and foreign keys (say in studies an ID for each study, and foreign keys for each task and person), the primary keys will repeat, which is not possible. A Study is defined by the tasks it contains (akin to an album and music tracks).
How should I approach this situation in SQLite? And importantly, how are the INSERTs done in these situations? I've seen lots of SELECT examples, but few INSERTs that match all IDs in each table, for example when adding a new person to a running study.
What you do is use tables to map/reference/relate/associate.
The first step would be to utilise alias's of the rowid so instead of :-
CREATE TABLE studies (study_title TEXT, contact_person TEXT);
CREATE TABLE tasks (task_name TEXT, description TEXT);
CREATE TABLE participants (first_name TEXT, last_name TEXT);
you would use :-
CREATE TABLE studies (id INTEGER PRIMARY KEY,study_title TEXT, contact_person TEXT);
CREATE TABLE tasks (id INTEGER PRIMARY KEY, task_name TEXT, description TEXT);
CREATE TABLE participants (id INTEGER PRIMARY KEY, first_name TEXT, last_name TEXT);
With SQLite INTEGER PRIMARY KEY (or INTEGER PRIMARY KEY AUTOINCREMENT) makes the column (id in the above although they can have any valid column name) and alias of the rowid (max of 1 per table), which uniquely identifies the rows.
Why not to use AUTOINCREMENT plus more seeSQLite Autoincrement
Insert some data for demonstration :-
INSERT INTO studies (study_title, contact_person)
VALUES ('Maths','Mr Smith'),('English','Mrs Taylor'),('Geography','Mary White'),('Phsyics','Mr Smith');
INSERT INTO tasks (task_name,description)
VALUES ('Task1','Do task 1'),('Task2','Do task 2'),('Task3','Do task 3'),('Task4','Do task 4'),('Mark','Mark the sudies');
INSERT INTO participants (first_name,last_name)
VALUES ('Tom','Jones'),('Susan','Smythe'),('Sarah','Toms'),('Alan','Francis'),('Julian','MacDonald'),('Fred','Bloggs'),('Rory','Belcher');
First mapping/reference... Table :-
CREATE TABLE IF NOT EXISTS study_task_relationship (study_reference INTEGER, task_reference INTEGER, PRIMARY KEY (study_reference,task_reference));
Map/relate Study's with Tasks (many-many possible)
Do some mapping (INSERT some data) :-
INSERT INTO study_task_relationship
VALUES
(1,2), -- Maths Has Task2
(1,5), -- Maths has Mark Questions
(2,1), -- English has Task1
(2,4), -- English has Task4
(2,5), -- English has Mark questions
(3,3), -- Geography has Task3
(3,1), -- Geoegrapyh has Task1
(3,2), -- Geography has Task2
(3,5), -- Geography has Mark Questions
(4,4) -- Physics has Task4
;
- See comments on each line
List the Studies along with the tasks
SELECT study_title, task_name -- (just want the Study title and task name)
FROM study_task_relationship -- use the mapping table as the main table
JOIN studies ON study_reference = studies.id -- get the related studies
JOIN tasks ON task_reference = tasks.id -- get the related tasks
ORDER BY study_title -- Order by Study title
results in :-
List each study with all it's tasks
SELECT study_title, group_concat(task_name,'...') AS tasklist
FROM study_task_relationship
JOIN studies ON study_reference = studies.id
JOIN tasks ON task_reference = tasks.id
GROUP BY studies.id
ORDER by study_title;
results in :-
Add study-participants associative table and populate it :-
CREATE TABLE IF NOT EXISTS study_participants_relationship (study_reference INTEGER, participant_reference INTEGER, PRIMARY KEY (study_reference,participant_reference));
INSERT INTO study_participants_relationship
VALUES
(1,1), -- Maths has Tom Jones
(1,5), -- Maths has Julian MacDonald
(1,6), -- Maths has Fred Bloggs
(2,4), -- English has Alan Francis
(2,7), -- English has Rory Belcher
(3,3), -- Geography has Sarah Toms
(3,2) -- Susan Smythe
;
You can now, as an example, get a list of participants the tasks along with the study title :-
SELECT study_title, task_name, participants.first_name ||' '||participants.last_name AS fullname
FROM study_task_relationship
JOIN tasks ON study_task_relationship.task_reference = tasks.id
JOIN studies On study_task_relationship.study_reference = studies.id
JOIN study_participants_relationship ON study_task_relationship.study_reference = study_participants_relationship.study_reference
JOIN participants ON study_participants_relationship.participant_reference = participants.id
ORDER BY fullname, study_title
which would result in :-
FOREIGN KEYS
As you can see there is no actual need for defining FOREIGN KEYS. They are really just an aid to stop you inadvertently doing something like :-
INSERT INTO study_participants_relationship VALUES(30,25);
No such study nor no such participant
To utilise FOREIGN KEYS you have to ensure that they are enabled, the simplest is just to issue the command to turn them on (as if it were a normal SQL statment).
PRAGMA foreign_keys=1
A FOREIGN KEY is a constraint, it stops you INSERTING, UPDATING or DELETING a row that would violate the constraint/rule.
Basically the rule is that the column to which the FOREIGN key is defined (the child) must have a value that is in the referenced table/column the parent.
So assumning that FOREIGN KEYS are turned on then coding :-
CREATE TABLE IF NOT EXISTS study_participants_relationship
(
study_reference INTEGER REFERENCES studies(id), -- column foreign key constraint
participant_reference INTEGER,
FOREIGN KEY (participant_reference) REFERENCES participants(id) -- table foreign key constraint
PRIMARY KEY (study_reference,participant_reference
)
);
Would result in INSERT INTO study_participants_relationship VALUES(30,25); failing e.g.
FOREIGN KEY constraint failed: INSERT INTO study_participants_relationship VALUES(30,25);
It fails as there is no row in studies with an id who's value is 30 (the first column foreign key constraint). If the value 30 did exist then the second constraint would kick in as there is no row in participants with an id of 25.
There is no difference between a column Foreign key constraint and a table Foreign key constraint other than where and how they are coded.
However, the above wouldn't stop you deleting all rows from the study_participants_relationship table as it would stop you deleting a row from the studies or participants table if they were referenced by the study_participants_relationship table.
"deal with an id that needs to be matched to multiple ids in SQLite?"
For many-to-many couplings, make extra coupling tables, like the study_task and participent_task tables below. This is many-to-many since a task can be on many studies and a study can have many tasks.
"make sure no one is doing more than one study at the same time"
That could be handled by letting each participant only have a column for current study (no place for more than one then).
PRAGMA foreign_keys = ON;
CREATE TABLE study (id INTEGER PRIMARY KEY, study_title TEXT, contact_person TEXT);
CREATE TABLE task (id INTEGER PRIMARY KEY, task_name TEXT, description TEXT);
CREATE TABLE participant (
id INTEGER PRIMARY KEY,
first_name TEXT,
last_name TEXT,
id_current_study INTEGER references study(id),
started_current_study DATE
);
CREATE TABLE study_task (
id_study INTEGER NOT NULL references study(id),
id_task INTEGER NOT NULL references task(id),
primary key (id_study,id_task)
);
CREATE TABLE participant_task (
id_participant INTEGER NOT NULL references participant(id),
id_task INTEGER NOT NULL references task(id),
status TEXT check (status in ('STARTED', 'DELIVERED', 'PASSED', 'FAILED')),
primary key (id_participant,id_task)
);
insert into study values (1, 'MX9345-3', 'John Doe');
insert into study values (2, 'MX9300-2', 'Jane Doe');
insert into participant values (1001, 'Michael', 'Smith', 1,'2018-04-21');
insert into participant values (1002, 'Julia', 'Barnes', 1, '2018-04-10');
insert into task values (51, 'OGTT', 'Make a ...');
insert into task values (52, 'PVT', 'Inspect the ...');
insert into study_task values (1,51);
insert into study_task values (1,52);
insert into study_task values (2,51);
--insert into study_task values (2,66); --would fail since 66 doesnt exists (controlled and enforced by foreign key)
The PRAGMA on the first line is needed to make SQLite (above version 3.6 from 2009 I think) enforce foreign keys, without it it just accepts the foreign key syntax, but no controlling is done.

A simplified version of Twitter. Understanding many-to-many relationships between tables in the database

I am reading this article about a Twitter-like application. The type of storage where tweets, users, likes, etc. will be stored is a relational database. The database scheme is described here and drawn here.
As an Android developer, I coded my sample using SQLite. This is how I would code it:
create table users (_id integer primary key, username text unique, first_name text, last_name text);
create table tweets (_id integer primary key, content text, created_at integer, user_id integer, foreign key(user_id) references users(_id));
create table connections (_id integer primary key, follower_id integer, followee_id integer, created_at integer, foreign key(follower_id) references users(_id), foreign key (followee_id) references users(_id));
create table favorites (_id integer primary key, user_id integer, tweet_id integer, foreign key (user_id) references users(_id), foreign key (tweet_id) references tweets(_id));
Now let's insert some data.
users:
insert into users values (1, 'user1', 'Lorem', 'Ipsum');
insert into users values (2, 'user2', 'Dolor', 'Sit');
insert into users values (3, 'user3', 'Foo', 'Bar');
insert into users values (4, 'user4', 'Qwerty', 'Trewq');
some tweets:
insert into tweets values(10, '1 Tweet from user1', 1100, 1);
insert into tweets values(11, '2 Tweet from user1', 1101, 1);
insert into tweets values(12, '3 Tweet from user1', 1102, 1);
insert into tweets values(13, '4 Tweet from user1', 1103, 1);
insert into tweets values(14, '1 Tweet from user2', 1103, 2);
insert into tweets values(15, '2 Tweet from user2', 1103, 2);
insert into tweets values(16, '1 Tweet from user3', 1103, 3);
insert into tweets values(17, '2 Tweet from user3', 1103, 3);
insert into tweets values(18, '1 Tweet from user4', 1107, 4);
favorites (the same as likes):
insert into favorites values(1, 2, 11);
insert into favorites values(2, 3, 13);
insert into favorites values(3, 4, 15);
There is a question about the database scheme:
Do you think you could support with our database design the ability
to display a page for a given user with their latest tweets that were
favorited at least once?
Yes, this is why query:
sqlite> select favorites._id, tweets._id as tweet_row_id, tweets.content from favorites join tweets on tweets.user_id=1 and tweets._id = favorites.tweet_id order by tweets._id desc limit 1;
_id tweet_row_id content
---------- ------------ ------------------
2 13 4 Tweet from user1
Explanation:
The left dataset is the table favorites. The right dataset is the table tweets. I join the two datasets. Then tweets.user_id=1 and tweets._id = favorites.tweet_id is evaluated for each row of the resulting dataset as a boolean expression. If the result is true, the row is included. order by tweets._id desc is used to get the latest tweets (the greater tweets._id is, the newer the tweet is). limit is used to limit the number of rows. If the user has been using our Twitter-like app for years, we'll show the latest 10 or 20 tweets.
My questions.
Is there anything wrong with my database scheme? I omitted not null, unique, and other column constraints for simplicity.
Here the author of the original article says:
The first relation is addressed by sticking the user ID to each tweet.
This is possible because each tweet is created by exactly one user.
It’s a bit more complicated when it comes to following users and
favoriting tweets. The relationship there is many-to-many.
"The first relation" is users-tweets.
Why do we need a many-to-many here? In my scheme I only use a one-to-many.
Update 1
Shortly I placed an answer here, where the OP - like you in this question - was unsure about 1:n and n:m.
I assume, that your final sentence is the actual question you have:
Why do we need a many-to-many here? In my scheme I only use a one-to-many
The relation user-tweets is 1:n...
Think in objects
user (id, name, ...)
tweet (id, author (FK on user), datetime, content, ...)
The like is an object with sepecific details on its own:
like (id, userid,tweetid,datetime,...)
For this you need a mapping table (you call it favourites)
There is a 1:n-relation from users to this mapping and a 1:n-relation from tweets to this mapping.
These two 1:n-relations form the m:n-relation together.
Now each tweet can be liked by many users and each user can like many tweets, but one user should (probably) not like the same tweet twice (unique key or even a two column PK?). And you might introduce a CHECK constraint to ensure, that the liking user and the author's userid is not the same (don't like your own tweets).
As a side note:
Is there anything wrong with my database scheme
You should never create constraints wihtout naming them
CREATE TABLE Dummy
(
ID INT IDENTITY CONSTRAINT PK_Dummy PRIMARY KEY
,UserID INT NOT NULL CONSTRAINT FK_Dummy_UserID FOREIGN KEY REFERENCES User(id)
,...
)
If this database was ever installed on different systems, they'll get different (random) names and future upgrade scripts will get you in deepest pain...
UPDATE: example for the side note
In you comment you ask, what this last sentence is about... Try this
CREATE DATABASE testDB;
GO
USE testDB;
GO
CREATE TABLE testTbl1(ID INT IDENTITY PRIMARY KEY,SomeValue INT UNIQUE);
CREATE TABLE testTbl2(ID INT IDENTITY PRIMARY KEY,FKtoTbl1 INT NOT NULL FOREIGN KEY REFERENCES testTbl1(ID));
GO
CREATE TABLE testTbl3(ID INT IDENTITY CONSTRAINT PK_3 PRIMARY KEY,SomeValue INT CONSTRAINT UQ_3_SomeValue UNIQUE);
CREATE TABLE testTbl4(ID INT IDENTITY CONSTRAINT PK_4 PRIMARY KEY,FKtoTbl3 INT NOT NULL CONSTRAINT FK_4_FKtoTbl3 FOREIGN KEY REFERENCES testTbl3(ID));
GO
SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS;
GO
USE master;
GO
DROP DATABASE testDB;
GO
On column in your result looks like this:
CONSTRAINT_NAME
------------------------------
PK__testTbl1__3214EC27ABEA2C0C
UQ__testTbl1__0E5C381C04C8AF66
PK__testTbl2__3214EC272784631C
FK__testTbl2__FKtoTb__1367E606
PK_3
UQ_3_SomeValue
PK_4
FK_4_FKtoTbl3
If this script is run twice, the given names will stay as you defined them. The other names will get a random name like PK__testTbl1__3214EC27ABEA2C0C. Now imagine, you need to create an upgrade script for several installed systems where one constraint has to be dropped or modified. How would you do this, if you do not know its name?

SQL auto-validating data when adding it to row

My SQL code is as follows:
CREATE TABLE personsdb
(personID int IDENTITY(1,1) NOT NULL,
personName varchar(50) NOT NULL,
associatedWith varchar(50) NOT NULL,
CONSTRAINT pk_persondb PRIMARY KEY (personID),
CONSTRAINT uq_persondb UNIQUE (associatedWith))
INSERT INTO personsdb
(personID, personName, associatedWith)
VALUES
('John', 'Mary'),
('Jack', 'Maggie'),
('Jeff', 'Marie')
I have a UNIQUE Constraint on the 'associatedWith' column, as I want to make sure that a person in personName can only be associated with one person in associatedWith, i.e. Mary could not be associated with Jeff because Mary is already associated with John.
My query relates to inserting the next row of the table. I want to insert 'Mary' into the personName column, but need a rule that autopopulates or only allows 'John' to be populated in the corresponding 'associatedWith' field, as a person can only be associated with one other person, and as John is already associated with Mary, when Mary is entered into the table, she should automatically be associated with John.
I'm relatively new to SQL but I'd like to figure it out as much as possible myself - if you could hint at a way to do this (in layman's terms) I'd be grateful so that I can go and research and learn how to do this.
Thanks very much in advance for your help.
David
This is a unary relationship which you cannot implement using foreign keys. You'll have to use a trigger. See below for full implementation.
CREATE TABLE dbo.Person(PersonId int not null primary key identity(1, 1), PersonName varchar(20) not null, AssociatedWith varchar(20));
GO
create trigger dbo.AssociationConstraint
ON dbo.Person
FOR INSERT, UPDATE
AS
IF (EXISTS(SELECT TOP(1) 1 FROM inserted i INNER JOIN dbo.Person p on i.PersonName = p.AssociatedWith)
OR EXISTS(SELECT TOP(1) 1 FROM inserted i INNER JOIN dbo.Person p on i.AssociatedWith = p.PersonName))
BEGIN
RAISERROR('Person is already part of a relationship', 16, 1);
ROLLBACK TRANSACTION;
END
GO
insert into dbo.Person(PersonName, AssociatedWith) values('John', 'Mary'), ('Jack', 'Maggie'), ('Jeff', 'Marie');
--this will error
insert into dbo.Person(PersonName, AssociatedWith) values('Mary', 'Jack');