Find in raw sql by join table - sql

Given 3 tables. I need to build SQL query to find two actors who cast together the most and list the titles
of those movies. Sort alphabetically
https://www.db-fiddle.com/f/r2Y9CpH8n7MHTeBaqEHe9S/0
Table film_actor
Column | Type | Modifiers
------------+-----------------------------+----------
actor_id | smallint | not null
film_id | smallint | not null
...
Table actor
Column | Type | Modifiers
------------+-----------------------------+----------
actor_id | integer | not null
first_name | character varying(45) | not null
last_name | character varying(45) | not null
...
Table film
Column | Type | Modifiers
------------+-----------------------------+----------
film_id | integer | not null
title | character varying(255) | not null
...
The desired output:
first_actor | second_actor | title
------------+--------------+--------------------
John Doe | Jane Doe | The Best Movie Ever
...

Input data and expected results would be helpful. Also, please tag DB you're using. You can try below code and see if it works:
SELECT (a.first_name || ' ' || a.last_name) AS First_Actor,
(b.first_name || ' ' || b.last_name) AS Second_Actor,
c.title
FROM actor a
JOIN
(SELECT a.actor_id AS first_actor,
b.actor_id AS second_actor,
a.film_id
FROM film_actor a
JOIN film_actor b ON a.film_id = b.film_id
AND a.actor_id < b.actor_id) ab ON a.actor_id = ab.first_actor
JOIN actor b ON b.actor_id = ab.second_actor
JOIN film c ON c.film_id = ab.film_id

Related

SQL - Using COUNT with alias shorthand

I'm learning SQL and having trouble performing a query that uses COUNT.
I have a movies database with three tables:
Table public.movies:
Column | Type | Collation | Nullable | Default
--------------+---------+-----------+----------+------------------------------------
id | integer | | not null | nextval('movies_id_seq'::regclass)
title | text | | not null |
release_year | integer | | not null |
runtime | integer | | not null |
rating | text | | not null |
studio_id | integer | | |
Table public.stars:
Column | Type | Collation | Nullable | Default
------------+---------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('stars_id_seq'::regclass)
first_name | text | | not null |
last_name | text | | |
birth_date | date | | not null |
Indexes:
"stars_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "roles" CONSTRAINT "roles_star_id_fkey" FOREIGN KEY (star_id) REFERENCES stars(id) ON DELETE CASCADE
Table public.roles:
Column | Type | Collation | Nullable | Default
----------+---------+-----------+----------+-----------------------------------
id | integer | | not null | nextval('roles_id_seq'::regclass)
movie_id | integer | | |
star_id | integer | | |
Indexes:
"roles_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"roles_movie_id_fkey" FOREIGN KEY (movie_id) REFERENCES movies(id) ON DELETE CASCADE
"roles_star_id_fkey" FOREIGN KEY (star_id) REFERENCES stars(id) ON DELETE CASCADE
I'm trying to select the first and last names of every star along with the number of movies they have been in, and group everything by first and last name (as to not have any duplicate stars). This is what I tried:
SELECT s.first_name, s.last_name, m.COUNT(*)
FROM movies m
JOIN roles r
ON m.id = r.movie_id
JOIN stars s
ON r.star_id = s.id
GROUP BY s.first_name, s.last_name;
I keep getting ERROR: schema "m" does not exist.
I'd appreciate any pointers.
The correct syntax just uses COUNT(*):
SELECT s.first_name, s.last_name, COUNT(*)
FROM movies m JOIN
roles r
ON m.id = r.movie_id JOIN
stars s
ON r.star_id = s.id
GROUP BY s.id, s.first_name, s.last_name;
To avoid duplicates, use the primary key in the GROUP BY.
Note that you don't need movies in the query:
SELECT s.first_name, s.last_name, COUNT(*)
FROM roles r JOIN
stars s
ON r.star_id = s.id
GROUP BY s.id, s.first_name, s.last_name;
Finally, if a star can play more than one role in a movie, you will want to use COUNT(DISTINCT r.movie_id) in the SELECT.

Using Two Tables to give one output

Table: Person
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| PersonId | int |
| FirstName | varchar |
| LastName | varchar |
+-------------+---------+
PersonId is the primary key column for this table.
Table: Address
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| AddressId | int |
| PersonId | int |
| City | varchar |
| State | varchar |
+-------------+---------+
AddressId is the primary key column for this table.
Write a SQL query for a report that provides the following information for each person in the Person table, regardless if there is an address for each of those people:
FirstName, LastName, City, State
So this is a pretty straight forward question and my answer is
select p.FirstName,p.LastName, a.City, a.State
from Person p , Address a
where p.PersonId = a.PersonId
And I'm not getting anything in the output and yes I filled up the tables with some simple lines of data.
Sample data would help, but - the way you described it, it looks as if you need outer join. Something like this:
select p.FirstName, p.LastName, a.City, a.State
from Person p left join Address a on a.personid = p.personid
MySQL query
SELECT Person.FirstName, Person.LastName, Address.City, Address.State
FROM Person
LEFT JOIN Address
ON Person.PersonId=Address.PersonId;

How to make this sql with query array_agg?

I want to make a query
select * from projects where user_id = 3;
and depending on it's result r, I need to make n queries, where n is the length l of r. eg:
| id | project_name | description | user_id |
| 1 | Project A | lorem ipsu | 3 |
| 4 | Project B | lorem ipsu | 3 |
l => 2 then:
select * from images where project_id = 1;
select * from images where project_id = 4;
Ok, you can see where this is going if l is too big. Too many selects, too many access to the database. Is there a better way to achieve an end result like so:
| id | project_name | description | user_id | images |
| 1 | Project A | lorem ipsu | 3 | {imgX,imgY,imgZ} |
| 4 | Project B | lorem ipsu | 3 | {imgA,imgB} |
I heard about array_agg function on postgres. Maybe that's the answer? Anyways, these are my table descriptions:
Table "public.projects"
Column | Type | Modifiers
-------------+--------------------------+-------------------------------------------------------
id | integer | not null default nextval('projects_id_seq'::regclass)
name | character varying(255) |
description | character varying(255) |
user_id | integer |
created_at | timestamp with time zone |
updated_at | timestamp with time zone |
Table "public.images"
Column | Type | Modifiers
------------+--------------------------+-----------------------------------------------------
id | integer | not null default nextval('images_id_seq'::regclass)
name | character varying(255) |
url | character varying(255) |
project_id | integer |
created_at | timestamp with time zone |
updated_at | timestamp with time zone |
And thank you in advance :D
array_agg is like any other aggregate function (count, sum), but returns an array instead of a scalar value. What you need can be achieved simply by joining and grouping the 2 tables.
SELECT p.id, p.name, p.description, p.user_id, array_agg(i.name) images
FROM projects p
LEFT JOIN images i ON p.id = i.project_id
GROUP BY p.id, p.name, p.description, p.user_id
You want records from projects plus records in image names as array matching by project_id :
SELECT *
FROM projects
LEFT JOIN LATERAL (SELECT array_agg(name) AS images FROM images WHERE project_id = projects.project_id) x ON true
WHERE user_id = '3'
sqlfiddle
Probably the easiest solution for you is a sub-select. This comes closest to the individual SELECT statements that you mention earlier:
SELECT * FROM images
WHERE project_id IN (
SELECT project_id FROM projects
WHERE user_id = 3);

POSTGRES - Combine same columns with different text in one row

I have three tables that i want to combine. I have a table doctor, patient and operation. Doctor represents all cind of doctors. Patient represents a patient and operation holds primaryKeys from patient and doctor.
How do i write a query which will show me chiefDoctor and assistantDocotor and Patient? What did i reach so far?
Show firstname and lastname from one either Chiefdoctor or Assistantdoctor. How do i write to show a table with both Chief and Assistant in it?
select a.vorname|| ' ' || a.nachname AS leitenderArzt, p.firstname || ' ' || p.lastname AS patient
from angestellter a inner join operation o on a.id = o.leitenderarzt
inner join patient p on o.patientident=p.ident
| id | firstname | lastname | patient |
| 1 | ImA | ChiefDoctor1 | p.firstname |
| 3 | ImA | ChiefDoctor3 | p.firstname |
The underlying structure of my database with a representation.
CREATE TABLE doctor
(
id serial NOT NULL,
firstname character varying(255) NOT NULL,
lastname character varying(255) NOT NULL,
CONSTRAINT angestellter_pkey PRIMARY KEY (id),
}
Table doctor
|id | firstname | lastname |
| 1 | ImA | ChiefDoctor1 |
| 2 | ImA | AssistantDoctor |
| 3 | ImA | ChiefDoctor2 |
CREATE TABLE patient
(
ident serial NOT NULL,
firstname character varying(255) NOT NULL,
lastname character varying(255) NOT NULL,
CONSTRAINT patient_pkey PRIMARY KEY (ident),
}
Table patient
| ident | firstname | lastname |
| 1 | Operated | ME |
CREATE TABLE operation
(
id serial NOT NULL,
chiefDoctor integer NOT NULL,
AssistantDoctor integer NOT NULL,
patientident integer NOT NULL,
CONSTRAINT operation_pkey PRIMARY KEY (id),
CONSTRAINT fkoperation539608 FOREIGN KEY (patientident)
REFERENCES patient (ident) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkoperation745809 FOREIGN KEY (assistantDoctor)
REFERENCES angestellter (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fkoperation949671 FOREIGN KEY (chiefDoctor)
REFERENCES angestellter (id) MATCH SIMPLE
}
Table operation
| id | doctorID | doctorID | patientID |
| 1 | 1 | 2 | 1 |
How do i write a query which will show show who operated a patient with full name? It should look like this.
| id | chiefdoctor | assistantdoctor | patient |
|----| ImA + ChiefDoctor |ImA + AssistantDoctor | Operated + ME|
The first and perhaps most intuitive way of doing this is to join doctor twice:
select o.id
, d1.firstname || ' + ' || d1.lastname as chiefdoctor
, d2.firstname || ' + ' || d2.lastname as assistantdoctor
, p.firstname || ' + ' || p.lastname as patient
from operation o
join doctor d1
on o.chiefDoctor = d1.id
join doctor d2
on o.AssistantDoctor = d2.id
join patient p
on o.patientident = p.ident
You probably would like to trim names as in:
trim(both from d1.firstname) || ' + ' || trim(both from d1.lastname)
but I got the feeling that this was not the major concern you had so inorder to keep the solution shorter, I left that out

MySQL Query: get all matches from the matches table along with the name of the team from the teams table given that when …

I have the following table structures:
matches:
+-------------------+---------------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+---------------------------+------+-----+-------------------+-----------------------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| creator_id | bigint(20) | NO | | NULL | |
| mode | enum('versus','freeplay') | NO | | NULL | |
| name | varchar(100) | NO | | NULL | |
| team_1_id | varchar(100) | NO | | NULL | |
| team_2_id | varchar(100) | NO | | NULL | |
+-------------------+---------------------------+------+-----+-------------------+-----------------------------+
teams:
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| creator_id | bigint(20) | NO | MUL | NULL | |
| name | varchar(100) | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
I need a query where we get all matches from the matches table along with the name of the team from the teams table given that when mode is "versus" the name of the team is taken from the teams table but when the mode is "freeplay" the name of the team is team_1_id or team_2_id themselves (they can hold strings, that is why they are varchar instead of int) without going to the teams table.
Use:
SELECT m.id,
m.creator_id,
m.mode,
m.name,
m.team_1_id,
m.team_2_id
FROM MATCHES m
WHERE m.mode = 'freeplay'
UNION ALL
SELECT m.id,
m.creator_id,
m.mode,
m.name,
t1.name,
t2.name
FROM MATCHES m
LEFT JOIN TEAMS t1 ON t1.id = m.team_1_id
LEFT JOIN TEAMS t2 ON t2.id = m.team_2_id
WHERE m.mode = 'versus'
SELECT CASE mode WHEN 'versus' THEN t1.name
ELSE team_1_id END AS name FROM matches
LEFT JOIN teams t1 ON t1.id=team_1_id
Do the same for team 2 and add where clause to suit your need
First, I would suggest you change your table structure slightly. Instead of using team_X_id for a name or an id, use it only for an id. Add an additional column for team_X_name that you can put the string in. That way you can define foreign keys and have the correct datatype. Set the team_X_id field to null and team_X_name to the team name when in freeplay mode, and set the team_X_id to the team id in versus mode.
That said, this should do what you want:
SELECT mode,
IF(team_1.id IS NULL, team_1_id, team_1.name),
IF(team_2.id IS NULL, team_2_id, team_2.name),
FROM matches
LEFT JOIN teams AS team_1 ON (matches.team_id_1=team_1.id)
LEFT JOIN teams AS team_2 ON (matches.team_id_2=team_2.id);
edit:
Actually, perhaps I misunderstood the design. If you are saying mode 'freeplay' flag means neither team_X_id will be an actual team id then you need a slightly different query:
SELECT mode,
IF(mode = 'freeplay' OR team_1.id IS NULL, team_1_id, team_1.name),
IF(mode = 'freeplay' OR team_2.id IS NULL, team_2_id, team_2.name),
FROM matches
LEFT JOIN teams AS team_1 ON (matches.team_id_1=team_1.id)
LEFT JOIN teams AS team_2 ON (matches.team_id_2=team_2.id);
But I would strongly suggest improving your DB design.