Multiple Columns in one column - sql

I have a Database which manages my music library, where I store all the id3 Tags in DB tables. The data model looks as followed: http://abload.de/img/modelq5sx3.png
CREATE TABLE Tracks (
Track_ID INTEGER NOT NULL AUTO_INCREMENT,
Title VARCHAR(255),
Track_Year YEAR,
Filename VARCHAR(255),
Track_Length CHAR(5),
Folder_ID INTEGER,
Album_ID INTEGER,
CONSTRAINT PK_Tracks PRIMARY KEY (Track_ID),
UNIQUE Unique_Path (Folder_ID, Filename)
);
CREATE TABLE Artists (
Artist_ID INTEGER NOT NULL AUTO_INCREMENT,
Artist VARCHAR(50),
CONSTRAINT PK_Artists PRIMARY KEY (Artist_ID),
UNIQUE (Artist)
);
CREATE TABLE Albums (
Album_ID INTEGER NOT NULL AUTO_INCREMENT,
Album VARCHAR(100),
AlbumArtist INTEGER,
AlbumCover VARCHAR(100),
CONSTRAINT PK_Albums PRIMARY KEY (Album_ID),
UNIQUE (Album)
);
CREATE TABLE Tracks_Artists (
Artist_ID INTEGER NOT NULL,
Track_ID INTEGER NOT NULL,
CONSTRAINT PK_Tracks_Artists PRIMARY KEY (Artist_ID, Track_ID)
);
So I have tracks,albums and artists in seperate entities with a n:m relation between tracks and artists as a track can be performed by more than one artist and a artist can perform in more than one track. I have done that to search for tracks by artist.
But to make a nice track view I want to have all these artists in one line per track, which I did not manage so far. All I can do is to join over Album, which has one album artist per album:
create or replace view v_c_tracks as SELECT t.traCK_ID,t.title,ar.arTIST,a.album
FROM TRACKS t
join albums a on t.album_id=a.album_id
join artists ar on a.albumartist=ar.artist_id
order by t.title;

You need to join to the track_artists table. Then to get the results on a single row, you need to aggregate by track. The key to bringing the artists together in a list is group_concat():
SELECT t.traCK_ID, t.title, group_concat(ar.artist) as artists, a.album
FROM tracks t join
albums a
on t.album_id = a.album_id join
track_artists ta
on ta.track_id = track.track_id join
artists ar
on ta.artist_id = ar.artist_id
group by t.traCK_ID, t.title, a.album
order by t.title;

Related

Shorter way to write this SQL query with a 3-way join and a condition

I'm currently learning SQL in the Harvard CS50 online course. The assignment is to write various SQL queries for a database. Here is a link to the assignment. I'm talking about the 12th query there.
The schema of the database looks like this:
CREATE TABLE movies (
id INTEGER,
title TEXT NOT NULL,
year NUMERIC,
PRIMARY KEY(id)
);
CREATE TABLE stars (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE directors (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE ratings (
movie_id INTEGER NOT NULL,
rating REAL NOT NULL,
votes INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id)
);
CREATE TABLE people (
id INTEGER,
name TEXT NOT NULL,
birth NUMERIC,
PRIMARY KEY(id)
);
The goal is to write a SQL query that returns the titles of all movies in which both Johnny Depp and Helena Bonham Carter starred. The query I came up with returns the list of movies for each actor and then uses INTERSECT on both of these lists. This is the query:
SELECT
movies.title
FROM
movies
JOIN stars ON movies.id = stars .movie_id
JOIN people ON stars .person_id = people.id
WHERE
people.name = "Johnny Depp"
INTERSECT
SELECT
movies.title
FROM
movies
JOIN stars ON movies.id = stars .movie_id
JOIN people ON stars .person_id = people.id
WHERE
people.name = "Helena Bonham Carter";
The query returns the correct results, however I feel it isn't very elegant or fast. Is there a shorter, more elegant and/or faster way to write this?
You could do something like this:
with cte as (
select
movie_id,count(p.id) as cnt
from stars s
join people p on p.id = s.person_id
where p.name in (
"Johnny Depp","Helena Bonham Carter"
)
group by movie_id
)
select title
from movies
where id in (
select movie_id from cte where cnt = 2
)
Basically the cte gets the movie ids where the two people are present in the stars table. Then we count that to see where both people are present in the stars table and join it to the movies table to get the actual name. You could probably use 'having' instead also.

sqlite query for the best selling album in each genre

I am using the basic chinook database, but i can't figure out how to write a SQLite query to create a view called BestSeller for the bestselling album in each genre with sales (based on the quantity of tracks sold, named as Sales) with the columns: Genre, Album, Artist, Sales.
The schema for the album table:
[Title] NVARCHAR(160) NOT NULL,
[ArtistId] INTEGER NOT NULL,
FOREIGN KEY ([ArtistId]) REFERENCES "artists" ([ArtistId])
artists table :
[ArtistId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[Name] NVARCHAR(120)
tracks table schema:
[TrackId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[Name] NVARCHAR(200) NOT NULL,
[AlbumId] INTEGER,
[MediaTypeId] INTEGER NOT NULL,
[GenreId] INTEGER,
[Composer] NVARCHAR(220),
[Milliseconds] INTEGER NOT NULL,
[Bytes] INTEGER,
[UnitPrice] NUMERIC(10,2) NOT NULL,
FOREIGN KEY ([AlbumId]) REFERENCES "albums" ([AlbumId])
ON DELETE NO ACTION ON UPDATE NO ACTION,
FOREIGN KEY ([GenreId]) REFERENCES "genres" ([GenreId])
ON DELETE NO ACTION ON UPDATE NO ACTION,
FOREIGN KEY ([MediaTypeId]) REFERENCES "media_types" ([MediaTypeId])
ON DELETE NO ACTION ON UPDATE NO ACTION
table invoice_items:
[InvoiceLineId] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
[InvoiceId] INTEGER NOT NULL,
[TrackId] INTEGER NOT NULL,
[UnitPrice] NUMERIC(10,2) NOT NULL,
[Quantity] INTEGER NOT NULL,
FOREIGN KEY ([InvoiceId]) REFERENCES "invoices" ([InvoiceId])
ON DELETE NO ACTION ON UPDATE NO ACTION,
FOREIGN KEY ([TrackId]) REFERENCES "tracks" ([TrackId])
ON DELETE NO ACTION ON UPDATE NO ACTION
I have all these other tables as well.
albums employees invoices playlists
artists genres media_types tracks
customers invoice_items playlist_track
Any help is appreciated.
Join the relevant tables and group by genre and album to get the quantity of tracks sold of each album.
Then use FIRST_VALUE() and MAX() window functions to get the album with the most sales in each genre and the the number of sales:
CREATE VIEW BestSeller AS
SELECT DISTINCT
g.Name Genre,
FIRST_VALUE(a.Title) OVER (PARTITION BY g.GenreId ORDER BY COUNT(*) DESC) Album,
FIRST_VALUE(r.Name) OVER (PARTITION BY g.GenreId ORDER BY COUNT(*) DESC) Artist,
MAX(COUNT(*)) OVER (PARTITION BY g.GenreId) Sales
FROM genres g
INNER JOIN tracks t ON t.GenreId = g.GenreId
INNER JOIN albums a ON a.AlbumId = t.AlbumId
INNER JOIN artists r ON r.ArtistId = a.ArtistId
INNER JOIN invoice_items i ON i.TrackId = t.TrackId
GROUP BY g.GenreId, a.AlbumId;
See the demo

is it possible in SQL to call two difrent table with difrent data at the same time

so basicly what i need is to get two tables at the same time and then use a condition that reside in the first table and i want to apply it to the second one
if it not possible do i need to call SELECT Twice ?
is this code right ?
to determine the average rating of all movies released in 2012
SELECT AVG(rating),year FROM ratings , movies
WHERE year = 2012;
//these are the tables that i have
CREATE TABLE movies (
id INTEGER,
title TEXT NOT NULL,
year NUMERIC,
PRIMARY KEY(id)
);
CREATE TABLE stars (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE directors (
movie_id INTEGER NOT NULL,
person_id INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id),
FOREIGN KEY(person_id) REFERENCES people(id)
);
CREATE TABLE ratings (
movie_id INTEGER NOT NULL,
rating REAL NOT NULL,
votes INTEGER NOT NULL,
FOREIGN KEY(movie_id) REFERENCES movies(id)
);
CREATE TABLE people (
id INTEGER,
name TEXT NOT NULL,
birth NUMERIC,
PRIMARY KEY(id)
);
I think you want a JOIN and an aggregate function:
SELECT AVG(r.rating) avg_rating_2012
FROM ratings r
INNER JOIN movies m on m.id = r.movie_id
WHERE m.year = 2012;
If you want this for all years at once, then use GROUP BY:
SELECT m.year, AVG(r.rating) avg_rating
FROM ratings r
INNER JOIN movies m on m.id = r.movie_id
GROUP BY m.year;

How do I get average through ‏multiple tables

I got homework to get ‏average tags of user in album (user_id = x) in the folowing tabels:
>>> CREATE TABLE USERS (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
NAME TEXT NOT NULL);
>>> CREATE TABLE ALBUMS (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
NAME TEXT NOT NULL, CREATION_DATE TEXT NOT NULL,
USER_ID INTEGER REFERENCES USERS(USER_ID) NOT NULL);
>>> CREATE TABLE PICTURES (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
NAME TEXT NOT NULL,
LOCATION TEXT NOT NULL,
CREATION_DATE TEXT NOT NULL,
ALBUM_ID INTEGER REFERENCES ALBUMS(ALBUM_ID) NOT NULL);
>>> CREATE TABLE TAGS (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
PICTURE_ID INTEGER REFERENCES PICTURES(PICTURE_ID) NOT NULL,
USER_ID INTEGER REFERENCES USERS(USER_ID) NOT NULL);";
explenetion:
Each tag is a row in TAGS and it has picture_id, each picture has album_id and each album has user_id, basically i need to count how many times the user is tagged in each album and find the average times that the user is tagged in an album.
I can use this using only: SELECT ? FROM, AVG(), COUNT(), JOIN (INNER, LEFT, RIGHT, FULL JOIN), ON, IN, AND, OR, LIKE, , NOT, (=, != , >, <), IS, DISTINCT, ORDER BY(ASC/DESC), LIMT, OFFSET, and WHERE that means i cannot use GROUP BY
i tried this
SELECT * FROM TAGS INNER JOIN PICTURES ON tags.picture_id = PICTURES.Id where album_id IN (select id from ALBUMS where user_id = x) AND user_id = x;
but it only gives my a table that has all the tags of the user
How can i get the avg tags per album of (user_id = x), is this even possible?
First count how many times the user is tagged in each album and then get the average of these counters:
select
avg(counter) averagetags
from (
select count(t.user_id) counter
from albums a
inner join pictures p on p.album_id = a.id
inner join tags t on t.picture_id = p.id
where t.user_id = ?
group by a.id
)

Display Titles From two other tables

I have a CONTAINS table in my DB:
CREATE TABLE CONTAINS(
album_id INTEGER NOT NULL REFERENCES Album,
song_id INTEGER NOT NULL REFERENCES Song,
PRIMARY KEY(album_id , song_id)
);
I have two Other Tables ALBUM and SONG:
CREATE TABLE ALBUM(
album_id INTEGER NOT NULL,
title TEXT NOT NULL,
PRIMARY KEY(album_id)
);
CREATE TABLE SONG(
song_id INTEGER NOT NULL,
title TEXT NOT NULL,
PRIMARY KEY(album_id)
);
If I do SELECT * FROM CONTAINS, I will get Album_Id and their respective Song_Id s. Now how do I display the album names and song names instead of their IDs?
Try this query:
select a.title as album_name,s.title as song_name
from contains c join album a
on c.album_id=a.album_id
join song s
on c.song_id=s.song_id;