SQL cross-reference table self-reference - sql

I am working on a project where I have a table of
all_names(
team_name TEXT,
member_name TEXT,
member_start INT,
member_end INT);
What I have been tasked with is creating a table of
participants(
ID SERIAL PRIMARY KEY,
type TEXT,
name TEXT);
which contains all team and member names as their own entries. Type may be either "team" or "member".
To compliment this table of participants I am trying to create a cross-reference table that allows a member to be referenced to a team by ID and vice versa. My table looks like this:
belongs_to(
member_id INT REFERENCES participants(ID),
group_id INT REFERENCES participants(ID),
begin_year INT,
end_year INT,
PRIMARY KEY (member_id, group_id);
I am unsure of how to proceed and populate the table properly.
The select query I have so far is:
SELECT DISTINCT ON (member_name, team_name)
id, member_name, team_name, member_begin_year, member_end_year
FROM all_names
INNER JOIN artists ON all_names.member_name = participants.name;
but I am unsure of how to proceed. What is the proper way to populate the cross reference table?

Probably the easiest solution is to use a few statements. Wrap this is a transaction to make sure you don't get concurrency issues:
BEGIN;
INSERT INTO participants (type, name)
SELECT DISTINCT 'team', team_name
FROM all_names
UNION
SELECT DISTINCT 'member', member_name
FROM all_names;
INSERT INTO belongs_to
SELECT m.id, g.id, a.member_start, a.member_end
FROM all_names a
JOIN participants m ON m.name = a.member_name
JOIN participants g ON g.name = a.team_name;
COMMIT;
Members that are part of multiple teams get all of their memberships recorded.

Related

Return everything from many-to-many relationship with only one query

I'll give an example to better clarify what I want:
Suppose I have the following classes in my programming language:
Class Person(
int id,
string name,
List<Car> cars
);
Class Car(
int id,
string name,
string brand
)
I want to save that in a PostgreSQL database, so I'll have the following tables:
CREATE TABLE person(
id SERIAL,
name TEXT
);
CREATE TABLE car(
id SERIAL,
name TEXT,
brand TEXT
)
CREATE TABLE person_car(
person_id int,
car_id int,
CONSTRAINT fk_person
FOREIGN KEY (person_id)
REFERENCES person(id),
CONSTRAINT fk_car
FOREIGN KEY (car_id)
REFERENCES car(id)
)
Then, I want to select all people with their cars from DB. I can select all people, then for each person, select their cars. But supposing I have 1000 people, I will have to query the DB 1001 times (one to select all people, and one for each person, to get their cars).
Is there an efficient way to bring all people, each with all their cars in a single query, so that I can fill my classes with the correct data without querying the DB a lot of times?
If you want to return a hierarchical dataset, you can use subqueries with COALESCE, for example :
SELECT
p.id
p.name,
COALESCE((SELECT
json_agg(json_build_object(
'id', c.id,
'name', c.name,
'brand', c.brand
))
FROM car AS c
JOIN person_car pc ON c.id = pc.car_id
WHERE pc.person_id = p.id), '[]'::json) AS cars
FROM person AS p;
You are joining person and car to person_car based on their respective ID’s.
SELECT
person.name,
person.id as person_id,
car.name,
car.brand,
car.id as car_id
FROM
person
JOIN
person_car
ON
person.id = person_car.person_id
JOIN
car
ON
car.id = person_car.car_id

SQLite update table with entries from a subset of records in another table

I have the following two tables. The 'tracks' table has no artist/track names (thery are all currently NULL). I want to update 'tracks' with the artist_name and track_name from the 'names' table. The 'names' table has 242,416,787 records and the tracks table has about 4 million. The arist/track name is associated by track_id, so I only want entries form 'names' that I have track_ids for.
CREATE TABLE tracks (
s3_url VARCHAR(128),
track_id INTEGER KEY,
cluster_id INTEGER KEY,
rank INTEGER KEY,
group_id VARCHAR(128),
artist_name VARCHAR(128),
track_name VARCHAR(128),
set_name VARCHAR(128),
file_size INTEGER KEY);
CREATE TABLE names (
artist_name VARCHAR(128),
track_name VARCHAR(128),
track_id INTEGER KEY,
album_name VARCHAR(128));
Here's what I have so far, this gets me the records in 'names' that I have track_ids for:
SELECT names.artist_name, f.track_id FROM names INNER JOIN tracks AS f ON names.track_id=f.track_id
I can't get figure out how to then stuff those results back into the 'tracks' table. I was trying something to the effect of the following:
UPDATE x SET artist_name=SELECT names.artist_name, f.track_id FROM names INNER JOIN tracks AS f ON names.track_id=f.track_id) AS x;
These threads here, and here to accomplish similar things and show that JOIN/UPDATE is not supported in SQLite.
The desired end result is to populate all entries in 'tracks' with artist_name and track_name from 'names'.
Try this:
replace into tracks
(rowid, s3_url, track_id, cluster_id, rank, group_id, artist_name, track_name, set_name, file_size)
select
t.rowid, t.s3_url, t.track_id, t.cluster_id, t.rank, t.group_id, n.artist_name, n.track_name, t.set_name, t.file_size
from names n
inner join tracks t on n.track_id = t.track_id
;
Available as a working example here: http://sqlfiddle.com/#!5/573148/1

Sqlite create view select value based on the value from another table

I have two tables right now, and I am trying to create a view with columns patient_id and drug_name that includes for each patient the set of drugs for which the patient can be allergic to. Including the drug from repostedallergies table, and also other drug which is inferred to be allergic from the inferredallergies table.
CREATE TABLE repostedallergies (
patient_id CHAR(5),
drug_name CHAR(15),
PRIMARY KEY (patient_id, drug_name)
)
CREATE TABLE inferredallergies (
alg CHAR(15),
canbe_alg CHAR(15),
PRIMARY KEY (alg, canbe_alg)
)
I have tried serval times, but it didn't work out
CREATE VIEW allergies AS
SELECT DISTINCT r.patient_id, r.drug_name
FROM reportedallergies r, inferredallergies i
WHERE r.drug_name IN (SELECT canbe_alg
FROM inferredallergies i);
Is there any other way to add the drug_name from inferredallergie table to the view.
CREATE VIEW allergies AS
SELECT r.patient_id, r.drug_name
FROM reportedallergies r
INNER JOIN inferredallergies i ON r.drug_name = i.canbe_alg
GROUP BY r.patient_id, r.drug_name
Never use commas in the FROM clause. Always use proper explicit JOIN syntax. Always!
In your case, I also think you need a UNION ALL to combine the information from both tables:
CREATE VIEW allergies AS
SELECT r.patient_id, i.canbe_alg as drug_name
FROM reportedallergies r JOIN
inferredallergies i
ON r.drug_name = i.alg
UNION ALL
SELECT r.patient_id, r.drug_name
FROM reportedallergies r;
If you need to remove duplicates, then replace the UNION ALL with UNION.

Join tables in sqlite with many to many

I have the following database schema:
create table people (
id integer primary key autoincrement,
);
create table groups (
id integer primary key autoincrement,
);
and I already have which people are members of which groups in a separate file (let's say in tuples of (person id, group id). How can I structure my database schema such that it's easy to access a person's groups, and also easy to access the members of a group? It is difficult and slow to read the tuples that I currently have, so I want this to be in database form. I can't have things like member1, member2, etc. as columns because the number of people in a group is currently unlimited.
Move your text file into a database table
CREATE TABLE groups_people (
groups_id integer,
people_id integer,
PRIMARY KEY(group_id, people_id)
);
And select all people that are a member of group 7
SELECT * FROM people p
LEFT JOIN groups_people gp ON gp.people_id = p.id
WHERE gp.groups_id = '7';
And select all the groups that person 5 is in
SELECT * FROM groups g
LEFT JOIN groups_people gp ON gp.groups_id = g.id
WHERE gp.people_id = '5';

SQL query to find rows with at least one of the specified values

Suppose you had two tables. One called MOVIES:
MovieId
MovieName
Then another called ACTORS that contains people who appear in those movies:
MovieId
ActorName
Now, I want to write a query that returns any movie that contains ONE OR MORE of the following actors: "Tom Hanks", "Russell Crowe" or "Arnold Schwarzenegger".
One way to do it would be something like:
SELECT DISTINCT A.MovieId, M.MovieName FROM ACTORS A
INNER JOIN MOVIES M USING (MovieId)
WHERE A.ActorName IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger');
Which is perfectly fine, however in my case I might have several more of these conditions on the WHERE clause so I want to find a way to make the MOVIES table the primary table I select from.
What's the best way to query for this? I'm using Oracle 11g if that matters, but I'm hoping for a standard SQL method.
You can use EXISTS or IN subqueries:
SELECT *
FROM MOVIES m
WHERE EXISTS
(
SELECT *
FROM ACTORS a
WHERE a.MovieId = m.MovieId
AND a.ActorName IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger')
)
or
SELECT *
FROM MOVIES m
WHERE m.MovieId IN
(
SELECT a.MovieId
FROM ACTORS a
WHERE a.ActorName IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger')
)
First you should have a 3rd table implementing the n:m relationship:
CREATE TABLE movie (
movie_id int primary key
,moviename text
-- more fields
);
CREATE TABLE actor (
actor_id int primary key
,actorname text
-- more fields
);
CREATE TABLE movieactor (
movie_id int references movie(movie_id)
,actor_id int references actor(actor_id)
,CONSTRAINT movieactor_pkey PRIMARY KEY (movie_id, actor_id)
);
Then you select like this:
SELECT DISTINCT m.movie_id, m.moviename
FROM movie m
JOIN movieactor ma USING (movie_id)
JOIN actor a USING (actor_id)
WHERE a.actorname IN ('Tom Hanks', 'Russell Crowe', 'Arnold Schwarzenegger');
Note, that text literals are enclose in single quotes!