SQL Subquery using HAVING COUNT - mysql

My question is: How do I show the name of each artist which has recorded at least one title at a studio where the band: "The Bullets" have recorded?
I have formulated the following query:
select ar.artistname, ti.titleid from artists ar
where exists
(
select 0
from titles ti where ti.artistid = ar.artistid
and exists
(
select 0
from studios s
where s.studioid = ti.studioid
and ar.artistname = 'The Bullets'
)
);
However, I need to include HAVING COUNT(ti.titleid) > 0 to satisfy this part, "each artist which has recorded at least one title" in the question.
I also am unsure as to how to match the artistname, "The Bullets" who have recorded at least one studio.
The Artists table resmebles the following:
Artists
-------
ArtistID, ArtistName, City
The Tracks table resmebles the following:
Tracks
------
TitleID, ArtistID, StudioID
The Studios table resmebles the folllowing:
Studios
-------
StudioID, StudioName, Address
I also must specify that I cannot use joins, e.g., a performance preference.

Maybe like this?
select ArtistName from Artists where ArtistID in (
select ArtistID from Tracks where StudioID in (
select StudioID from Tracks where ArtistID in (
select ArtistId from Artists where ArtistName='The Bullets'
)
)
)
I don't see why do you think having is needed.

The studio(s) where the bullets recorded
SELECT StudioID
FROM Sudios S
JOIN Tracks T ON S.StudioID = S.StudioID
JOIN Artists A ON T.ArtistID = A.ArtistID AND A.ArtistName = 'The Bullets'
Every Artist who recorded there
SELECT A1.ArtistName, A1.City
FROM Artist A1
JOIN Tracks T1 ON T1.ArtistID = A2.ArtistID
WHERE T1.SudioID IN
(
SELECT StudioID
FROM Sudios S
JOIN Tracks T ON S.StudioID = S.StudioID
JOIN Artists A ON T.ArtistID = A.ArtistID AND A.ArtistName = 'The Bullets'
) T

These two requirements are quite silly - using HAVING COUNT(*) > 0 and no joins. I've never heard of choosing sub-queries over joins to enhance performance.
However, I think this query fulfills these requirements.
SELECT a.ArtistName FROM Artist a
WHERE EXISTS
(
SELECT t1.ArtistId FROM Track t1
WHERE t1.ArtistId = a.ArtistId
AND EXISTS
(
SELECT * FROM Track t2
WHERE t1.StudioId = t2.StudioID
AND t2.ArtistName = 'The Bullets'
)
GROUP BY t1.ArtistId, t1.StudioId
HAVING COUNT(*) > 0
);

Related

My sql questions about retrieving multiple values from different tables

MySQL tables:
author
(aEmail*
,fName
,lName
,bDate
,city
)
reviewer
(rEmail*
,phoneNumber
,lName
,fName
,city
)
paper
(paperId*
,title
,abstract
,submissionDate
)
author_paper
(authorId*
,paperId*
,isContact
)
paper_review
(paperId*
,reviewerId*
,score
,reviewSubmissionDate
,reviewInvitationDate
)
* = (component of) PRIMARY KEY
How would I find authors that have made more than 3 papers and return their names in a query and that the authors age is above a certain age (aEmail = authorId)
One option uses a correlated subquery for filtering: we can just count how many rows each author has in author_paper.
select a.*
from author a
where (select count(*) from author_paper ap where ap.authorid = a.id) > 3
I am unsure about the correlation clause. Maybe you want to use the author's email instead:
where ap.authorid = a.aemail

Select most popular track for a playlist in a playlists table

I am creating a summary table with the stats of all my playlists with the following fields:
PlaylistName
LatestTrackAddDate
NumberOfTracks
TracksPlayedCount
NumberOfArtists
MostPopularSong
I can get the all the fields except for the first one with the following query:
SELECT p.playlist_name AS 'Playlist Name',
MAX(plt.playlisttrack_adddate) 'Latest Track Add Date',
COUNT(DISTINCT plt.playlist_id, plt.track_id, plt.user_id, plt.playlisttrack_adddate) As 'Number of Tracks',
COUNT(DISTINCT pt.user_id, pt.track_id, playedtrack_datetime) As 'Number of Tracks Played',
COUNT(DISTINCT a.artist_id) As 'Number of Unique Artists'
FROM playlist p
JOIN playlisttrack plt ON p.playlist_id = plt.playlist_id
JOIN tracksource ts ON ts.tracksource_typeid = p.playlist_id
JOIN playedtrack pt ON pt.tracksource_id = ts.tracksource_id
JOIN track t ON plt.track_id = t.track_id
AND pt.track_id = t.track_id
JOIN trackartist ta ON t.track_id = ta.track_id
JOIN artist a ON ta.artist_id = a.artist_id
GROUP BY p.playlist_name;
I can get the last field in its own query with the following:
SELECT playlist_name, track_name, MAX(count_plays)
FROM(
SELECT p.playlist_name AS playlist_name,
t.track_name AS track_name,
COUNT(pt.playedtrack_datetime) AS count_plays
FROM playlist p
JOIN playlisttrack plt ON p.playlist_id = plt.playlist_id
JOIN tracksource ts ON ts.tracksource_typeid = p.playlist_id
JOIN playedtrack pt ON pt.tracksource_id = ts.tracksource_id
JOIN track t ON plt.track_id = t.track_id
AND pt.track_id = t.track_id
JOIN trackartist ta ON t.track_id = ta.track_id
JOIN artist a ON ta.artist_id = a.artist_id
GROUP BY p.playlist_name, t.track_name) combined
GROUP BY playlist_name;
I'm not quite sure how to combine these two results - any guidance is appreciated! Thank you!
Edit:
If there are multiple tracks that have the max play count, then they would all appear in the table.
Structure of the tables used in these queries (others are omitted):
Partial snippet of schema
Please try -
SELECT nameOfPlaylist AS "Playlist Name",
dateTrackLastAdded AS "Latest Track Add Date",
trackCount AS "Number Of Tracks",
numberOfTracksPlayed AS "Number Of Tracks Played",
uniqueArtistCount AS "Number Of Unique Artists",
playedTrackID AS "Most Popular Song(s)"
FROM
(
SELECT playlist.playlist_id AS playlist_id,
playlist.playlist_name AS nameOfPlaylist,
MAX( playlisttrack.playlisttrack_adddate ) AS dateTrackLastAdded,
COUNT( playlisttrack.track_id ) AS trackCount,
COUNT( DISTINCT playedtrack.track_id ) AS numberOfTracksPlayed,
COUNT( DISTINCT trackartist.artist_id ) AS uniqueArtistCount,
playedtrack.track_id AS playedTrackID,
COUNT( playedtrack.track_id ) AS distributedTrackPlayCount
FROM playlist
JOIN playlisttrack ON playlist.playlist_id = playlisttrack.playlist_id
JOIN tracksource ON tracksource.tracksource_typeid = playlist.playlist_id
JOIN playedtrack ON playedtrack.tracksource_id = tracksource.tracksource_id
JOIN track ON playlisttrack.track_id = track.track_id AND playedTrackID = track.track_id
JOIN trackartist ON track.track_id = trackartist.track_id
GROUP BY playlist.playlist_name,
playedTrackID
) withDistributedTrackPlayCount
JOIN
(
SELECT withConsolidatedTrackCount.playlist_id,
MAX( consolidatedTrackPlayCount ) AS maxPlayCount
FROM
(
SELECT withDistributedTrackPlayCount.playlist_id AS playlist_id,
withDistributedTrackPlayCount.playedTrackID AS playedTrackID,
SUM( distributedTrackPlayCount ) AS consolidatedTrackPlayCount
FROM withDistributedTrackPlayCount
GROUP BY withDistributedTrackPlayCount.playlist_id
) withConsolidatedTrackCount
) withMaxTrackPlayCount ON withDistributedTrackPlayCount.playlist_id = withMaxTrackPlayCount.playlist_id;
Note : I have not been able to test this as I do not have a suitably equivalent database and will have to construct one instead. If you have scripts to construct and populate the above then please modify your question to include them.
Note : I have assumed that "Number Of Tracks Played" refers to a count of those tracks belonging to a playlist that have been played as part of that playlist at least once. If it refers to the total number of times that tracks belonging to the playlist have been played as part of the playlist, or something else, then please say so and I will modify my answer.
Note : You have stated that one track can be added to a playlist by more than one user, with each user perhaps adding it more than once. I have assumed that "Most Popular Song(s)" refers to the total number of plays for a track across all of its listings within a playlist.
I welcome all constructive questions or comments related to this answer.

MySQL SELECT all people matching X and then all in each person's household

I have two tables I need to query to print a list - PURCHASE HISTORY and PEOPLE
PURCHASE HISTORY
----------------
purchase_txnid...purchase_date...purchase_userid...purchase_productid
PEOPLE
------
people_householdid...people_userid...people_street...people_city...people_state...(etc)
I need to get everyone in householdid where someone has purchased productid = "X" (basically a list of everyone at any house where SOMEONE has purchased the product) and then display each of their purchase histories.
PURCHASE HISTORY has seven distinct products and 320,000 records.
Right now, I'm querying all people, RIGHT JOINing the purchase history to select people who purchased "X", and then iterating through the results and, with each result, querying to SELECT everyone in that household and each of their purchase histories. It's extremely slow as there are 45,000 people and 320,000 purchases.
Any ideas as to how I can merge this into a single query or optimize it?
UPDATE
Here are the queries:
$buyers = $db->get_results( " SELECT people.*, TIMESTAMPDIFF( YEAR, birth_date, CURDATE() ) AS age FROM people
RIGHT JOIN purchase_history ON purchase_history.purchase_userid = people.userid
WHERE people.region=$region AND purchase_history.purchase_productid = 'D'
GROUP BY people.userid
ORDER BY street_name ASC, street_suffix ASC, street_num ASC, street_unit ASC, household_id DESC, birth_date ASC
" );
foreach( $buyers as $buyer ){
$in_household = $db->get_results( "SELECT *, TIMESTAMPDIFF( YEAR, birth_date, CURDATE() ) AS person_age FROM people WHERE household_id = '$buyer->household_id' ORDER BY birth_date ASC" );
foreach( $in_household as $person ){
$purchases = $db->get_results( "SELECT * FROM purchase_history WHERE purchase_userid='$person->userid'" );
}
}
SELECT DISTINCT peopleB.userid, peopleB.* FROM purchase
JOIN people AS peopleA ON peopleA.people_userid = purchase_userid
JOIN people AS peopleB ON peopleB.people_householdid = peopleA.householdid
WHERE purchase.purchase_productid = "X"
You can speed up this query by adding these indices to your tables:
CREATE INDEX productid ON purchase (purchase_productid)
CREATE INDEX householdid ON people (people_householdid)
I assume people_userid is already your primary key in people, otherwise you should make it unique:
CREATE UNIQUE INDEX userid ON people (people_userid)
EDIT - You asked for the complete purchase history:
SELECT peopleB.*, phB.* FROM purchase_history AS phA
JOIN people AS peopleA ON peopleA.people_userid = phA.purchase_userid
JOIN people AS peopleB ON peopleB.people_householdid = peopleA.householdid
LEFT JOIN purchase_history AS phB ON phB.purchase_userid = peopleB.userID
GROUP BY peopleB.id, phB.purchase_txnid
WHERE purchase.purchase_productid = "X"
Note that this will give you people's data once for every purchase they made.
Also, I don't know if that query really works the way I want it to or how fast/slow it will be. You could also try reversing the colums in the GROUP BY line and see if that is faster.
You can do this with a subquery and a join:
select p.*
from people p join
(select distinct p.people_householdid
from PurchaseHistory ph join
People p
on ph.purchase_userid = p.people_userid
where ph.purchase_productid = 'X'
) ph
on ph.people_userid = p.people_userid;
EDIT:
If you want to flag the member(s) that made the purchase:
select p.*,
max(p.people_userid = ph.people_userid) as IsPurchaser
from people p join
(select p.people_householdid, p.people_userid
from PurchaseHistory ph join
People p
on ph.purchase_userid = p.people_userid
where ph.purchase_productid = 'X'
) ph
on ph.people_userid = p.people_userid
group by p.people_userid

selecting where in multiple join tables

I have the logic worked out, just not sure how to best write this query.
the logic is
we have a deal ID of 1
a deal is linked to multiple regions
a deal is linked to multiple interests
a user is linked to multiple regions
a user is linked to multiple interests
we want all users where....
the user is linked to the same region as a deal
userRegionLink url, dealRegionLink drl
url.regionId is in drl.regionId where drl.dealId = 1
the user is linked to the same interest as a deal
userInterestLink uil, dealInterestLink dil
uil.interestId is in dil.interestId where dil.dealId = 1
this would give us a list of the users
now we need to select distinct from the list so we only end up sending each user a single email
But I have no idea what the best way to write this query would be.
We are dealing with a few tables here
We have
users which has all the user Information in it userId and other columns not important
userInterestLink which has userId and interestId
dealInterestLink which has dealId and interestId
userRegionLink which has userId and regionId
dealRegionLink which has dealId and regionId
so what we are wanting in the end is all the user info which matches.
I take RC's answer and modify it
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil ON (uil.userId = u.userId)
JOIN userRegionLink url ON (url.userId = u.userId)
WHERE interestId IN (
SELECT DISTINCT interestId FROM dealInterestLink WHERE dealId = 1
) AND regionId IN (
SELECT DISTINCT regionId FROM dealRegionLink WHERE dealId = 1
)
as there is no need for LEFT JOIN if I exclude the NULL rows afterwards.
A more "symmetric" version without subqueries and with USING would be
SELECT u.userId, uil.interestId, url.regionId FROM users u
JOIN userInterestLink uil USING (userId)
JOIN userRegionLink url USING (userId)
JOIN dealInterestLink dil USING (interestId)
JOIN dealRegionLink drl USING (regionId, dealId)
WHERE dealId = 1
Untested as well.
Something like:
SELECT u.userId, uil.interestId, url.regionId FROM users u
LEFT JOIN userInterestLink uil ON (uil.userId = u.userId)
LEFT JOIN userRegionLink url ON (url.userId = u.userId)
WHERE uil.interestId IS NOT NULL AND uil.interestId IN (
SELECT DISTINCT interestId FROM dealInterestLink WHERE dealId = 1
) AND url.regionId IS NOT NULL AND url.regionId IN (
SELECT DISTINCT regionId FROM dealRegionLink WHERE dealId = 1
)
? If result is OK, you can then SELECT DISTINCT u.userId FROM users u -- ...
(not tested)
SELECT `u`.*
FROM `users` AS `u`
JOIN `userRegionLink` `userReg` USING ( `userId` )
JOIN `userInterestLink` `userInt` USING ( `userId` )
JOIN `dealInterestLink` `dealInt` USING ( `interestId` )
JOIN `dealRegionLink` `dealReg` USING ( `regionId` )
JOIN `deal` `d` ON ( `dealInt`.`dealId` && `dealReg`.`dealId` && `d`.`dealId` = 1 )
GROUP BY `u`.`userId`
Tested locally using dummy data and presumed schema. Worked OK.

How do you do multiple inserts that have selects

How do I do an insert multiple values or records that have to get their information from select statements. This doesn't work.
INSERT INTO marriedcouples (male,female) VALUES (
(SELECT id FROM men WHERE username='brad',
SELECT id FROM women WHERE username='jennifer')
(SELECT id FROM men WHERE username='ken',
SELECT id FROM women WHERE username='barbie'))
Assuming I have tables with:
men(id,name), women(id,name), couples(id,male,female)
etc.
Thanks,
Dan
Insert marriedcouples( male, female )
Select M.id, W.id
From Men As M
Cross Join Women As W
Where ( M.username = 'brad' And W.username = 'jennifer' )
Or ( M.username = 'ken' And W.username = 'barbie' )
Addition
In comments, you asked specifically about the problems with your original query. First, you could have used your original approach like so:
Insert marriedcouples( male, female )
Select ( Select Id From men Where username = 'brad' )
, ( Select Id From women Where username = 'jennifer' )
Union All
Select ( Select Id From men Where username = 'ken' )
, ( Select Id From women Where username = 'barbie' )
Notice that each value is enclosed in parentheses as its own encapsulated subquery. Second, notice that I used the Union All directive to allow me to stack the two queries and give me two rows. Third, notice that I'm not trying to use the Values directive in combination with subqueries. You can use the Values clause to list out values or you can use a Select statement but not both in the way you did. Obviously, this approach of four subqueries will not perform well but it helps to understand the breakdown of the syntax.