I usually don't ask for "scripts" but for mechanisms but I think that in this case if i'll see an example I would understand the principal.
I have three tables as shown below:
and I want to get the columns from all three, plus a count of the number of episodes in each series and to get a result like this:
Currently, I am opening multiple DB threads and I am afraid that as I get more visitors on my site it will eventually respond really slowly.
Any ideas?
Thanks a lot!
First join all the tables together to get the columns. Then, to get a count, use a window function:
SELECT count(*) over (partition by seriesID) as NumEpisodesInSeries,
st.SeriesId, st.SeriesName, et.episodeID, et.episodeName,
ct.createdID, ct.CreatorName
FROM series_table st join
episode_table et
ON et.ofSeries = st.seriesID join
creator_table ct
ON ct.creatorID = st.byCreator;
Do your appropriate joins between the tables and their IDs as you would expect, and also join onto the result of a subquery that determines the total episode count using the Episodes table.
SELECT SeriesCount.NumEpisodes AS #OfEpisodesInSeries,
S.id AS SeriesId,
S.name AS SeriesName,
E.id AS EpisodeId,
E.name AS EpisodeName,
C.id AS CreatorId,
C.name AS CreatorName
FROM
Series S
INNER JOIN
Episodes E
ON E.seriesId = S.id
INNER JOIN
Creators C
ON S.creatorId = C.id
INNER JOIN
(
SELECT seriesId, COUNT(id) AS NumEpisodes
FROM Episodes
GROUP BY seriesId
) SeriesCount
ON SeriesCount.seriesId = S.id
SQL Fiddle Schema:
CREATE TABLE Series (id int, name varchar(20), creatorId int)
INSERT INTO Series VALUES(1, 'Friends', 1)
INSERT INTO Series VALUES(2, 'Family Guy', 2)
INSERT INTO Series VALUES(3, 'The Tonight Show', 1)
CREATE TABLE Episodes (id int, name varchar(20), seriesId int)
INSERT INTO Episodes VALUES(1, 'Joey', 1)
INSERT INTO Episodes VALUES(2, 'Ross', 1)
INSERT INTO Episodes VALUES(3, 'Phoebe', 1)
INSERT INTO Episodes VALUES(4, 'Stewie', 2)
INSERT INTO Episodes VALUES(5, 'Kevin Kostner', 3)
INSERT INTO Episodes VALUES(6, 'Brad Pitt', 3)
INSERT INTO Episodes VALUES(7, 'Tom Hanks', 3)
INSERT INTO Episodes VALUES(8, 'Morgan Freeman', 3)
CREATE TABLE Creators (id int, name varchar(20))
INSERT INTO Creators VALUES(1, 'Some Guy')
INSERT INTO Creators VALUES(2, 'Seth McFarlane')
Try this:
http://www.sqlfiddle.com/#!3/5f938/17
select min(ec.num) as NumEpisodes,s.Id,S.Name,
Ep.ID as EpisodeID,Ep.name as EpisodeName,
C.ID as CreatorID,C.Name as CreatorName
from Episodes ep
join Series s on s.Id=ep.SeriesID
join Creators c on c.Id=s.CreatorID
join (select seriesId,count(*) as Num from Episodes
group by seriesId) ec on s.id=ec.seriesID
group by s.Id,S.Name,Ep.ID,Ep.name,C.ID,C.Name
Thanks Gordon
I would do the following:
SELECT (SELECT Count(*)
FROM episodetbl e1
WHERE e1.ofseries = s.seriesid) AS "#ofEpisodesInSeries",
s.seriesid,
s.seriesname,
e.episodeid,
e.episodename,
c.creatorid,
c.creatorname
FROM seriestbl s
INNER JOIN creatortbl c
ON s.bycreator = c.creatorid
INNER JOIN episodetbl e
ON e.ofseries = s.seriesid
Related
I am trying to figure out how to select all records that are associated with all categories on a list.
For instance take this DB setup:
create table blog (
id integer PRIMARY KEY,
url varchar(100)
);
create table blog_category (
id integer PRIMARY KEY,
name varchar(50)
);
create table blog_to_blog_category (
blog_id integer,
blog_category_id integer
);
insert into blog values
(1, 'google.com'),
(2, 'pets.com'),
(3, 'petsearch.com');
insert into blog_category values
(1, 'search'),
(2, 'pets'),
(3, 'misc');
insert into blog_to_blog_category values
(1,1),
(2,2),
(3,1),
(3,2),
(3,3);
I can query on the main table like this:
select b.*, string_agg(bc.name, ', ') from blog b
join blog_to_blog_category btbc on b.id = btbc.blog_id
join blog_category bc on btbc.blog_category_id = bc.id
where b.url like '%.com'
group by b.id
But lets say I want to only return blogs that have BOTH category 1 & 2 connected with them how do I do that?
This would return just the petsearch.com domain as it is only record to have both of those categories.
Here you go:
Added a check to count the blog_category id (HAVING Clause) and if it is 2 then it should be either 1 or 2 (IN Clause),
select b.*, string_agg(bc.name, ', ') from blog b
join blog_to_blog_category btbc on b.id = btbc.blog_id
join blog_category bc on btbc.blog_category_id = bc.id
where b.url like '%.com' and bc.id in (1,2)
group by b.id
having count(distinct bc.id ) =2
here is one way:
select * from blog where id in (
select blog_id
from blog_to_blog_category bbc
where blog_category_id in (1, 2)
group by blog_id
having count(distinct blog_category_id) = 2
)
Suppose I have the following table
DROP TABLE IF EXISTS #toy_example
CREATE TABLE #toy_example
(
Id int,
Pet varchar(10)
);
INSERT INTO #toy
VALUES (1, 'dog'),
(1, 'cat'),
(1, 'emu'),
(2, 'cat'),
(2, 'turtle'),
(2, 'lizard'),
(3, 'dog'),
(4, 'elephant'),
(5, 'cat'),
(5, 'emu')
and I want to fetch all Ids that have certain pets (for example either cat or emu, so Ids 1, 2 and 5).
DROP TABLE IF EXISTS #Pets
CREATE TABLE #Pets
(
Animal varchar(10)
);
INSERT INTO #Pets
VALUES ('cat'),
('emu')
SELECT Id
FROM #toy_example
GROUP BY Id
HAVING COUNT(
CASE
WHEN Pet IN (SELECT Animal FROM #Pets)
THEN 1
END
) > 0
The above gives me the error Cannot perform an aggregate function on an expression containing an aggregate or a subquery. I have two questions:
Why is this an error? If I instead hard code the subquery in the HAVING clause, i.e. WHEN Pet IN ('cat','emu') then this works. Is there a reason why SQL server (I've checked with SQL server 2017 and 2008) does not allow this?
What would be a nice way to do this? Note that the above is just a toy example. The real problem has many possible "Pets", which I do not want to hard code. It would be nice if the suggested method could check for multiple other similar conditions too in a single query.
If I followed you correctly, you can just join and aggregate:
select t.id, count(*) nb_of_matches
from #toy_example t
inner join #pets p on p.animal = t.pet
group by t.id
The inner join eliminates records from #toy_example that have no match in #pets. Then, we aggregate by id and count how many recors remain in each group.
If you want to retain records that have no match in #pets and display them with a count of 0, then you can left join instead:
select t.id, count(*) nb_of_records, count(p.animal) nb_of_matches
from #toy_example t
left join #pets p on p.animal = t.pet
group by t.id
How about this approach?
SELECT e.Id
FROM #toy_example e JOIN
#pets p
ON e.pet = p.animal
GROUP BY e.Id
HAVING COUNT(DISTINCT e.pet) = (SELECT COUNT(*) FROM #pets);
I'm wondering if it is possible to inner join an inner join with another inner join.
I have a database of 3 tables:
people
athletes
coaches
Every athlete or coach must exist in the people table, but there are some people who are neither coaches nor athletes.
What I am trying to do is find a list of people who are active (meaning play or coach) in at least 3 different sports. The definition of active is they are either coaches, athletes or both a coach and an athlete for that sport.
The person table would consist of (id, name, height)
the athlete table would be (id, sport)
the coaching table would be (id, sport)
I have created 3 inner joins which tell me who is both a coach and and an athlete, who is just a coach and who is just an athlete.
This is done via inner joins.
For example,
1) who is both a coach and an athlete
select
person.id,
person.name,
coach.sport as 'Coaches and plays this sport'
from coach
inner join athlete
on coach.id = athlete.id
and coach.sport = athlete.sport
inner join person
on athlete.id = person.id
That brings up a list of everyone who both coaches and plays the same sport.
2) To find out who only coaches sports, I have used inner joins as below:
select
person.id,
person.name,
coach.sport as 'Coaches this sport'
from coach
inner join person
on coach.id = person.id
3) Then to find out who only plays sports, I've got the same as 2) but just tweaked the words
select
person.id,
person.name,
athlete.sport as 'Plays this sport'
from athlete
inner join person
on athlete.id = person.id
The end result is now I've got:
1) persons who both play and coach the same sport
2) persons who coach a sport
3) persons who play a sport
What I would like to know is how to find a list of people who play or coach at least 3 different sports? I can't figure it out because if someone plays and coaches a sport like hockey in table 1, then I don't want to count them in table 2 and 3.
I tried using these 3 inner joins to make a massive join table so that I could pick the distinct values but it is not working.
Is there an easier way to go about this without making sub-sub-queries?
What I would like to know is how to find a list of people who play /
coach at least 3 different sports? I can't figure it out because if
someone plays and coaches a sport like hockey in table 1, then I don't
want to count them in table 2 and 3.
you can do something like this
select p.id,min(p.name) name
from
person p inner join
(
select id,sport from athlete
union
select id,sport from coach
)
ca
on ca.id=p.id
group by p.id
having count(ca.sport)>2
CREATE TABLE #person (Id INT, Name VARCHAR(50));
CREATE TABLE #athlete (Id INT, Sport VARCHAR(50));
CREATE TABLE #coach (Id INT, Sport VARCHAR(50));
INSERT INTO #person (Id, Name) VALUES(1, 'Bob');
INSERT INTO #person (Id, Name) VALUES(2, 'Carol');
INSERT INTO #person (Id, Name) VALUES(2, 'Sam');
INSERT INTO #athlete (Id, Sport) VALUES(1, 'Golf');
INSERT INTO #athlete (Id, Sport) VALUES(1, 'Football');
INSERT INTO #coach (Id, Sport) VALUES(1, 'Tennis');
INSERT INTO #athlete (Id, Sport) VALUES(2, 'Tennis');
INSERT INTO #coach (Id, Sport) VALUES(2, 'Tennis');
INSERT INTO #athlete (Id, Sport) VALUES(2, 'Swimming');
-- so Bob has 3 sports, Carol has only 2 (she both coaches and plays Tennis)
SELECT p.Id, p.Name
FROM
(
SELECT Id, Sport
FROM #athlete
UNION -- this has an implicit "distinct"
SELECT Id, Sport
FROM #coach
) a
INNER JOIN #person p ON a.Id = p.Id
GROUP BY p.Id, p.Name
HAVING COUNT(*) >= 3
-- returns 1, Bob
I have created a SQL with some test data - should work in your case:
Connecting the two results in the subselect with UNION:
UNION will return just non-duplicate values. So every sport will be just counted once.
Finally just grouping the resultset by person.Person_id and person.name.
Due to the HAVING clause, just persons with 3 or more sports will be returned-
CREATE TABLE person
(
Person_id int
,name varchar(50)
,height int
)
CREATE TABLE coach
(
id int
,sport varchar(50)
)
CREATE TABLE athlete
(
id int
,sport varchar(50)
)
INSERT INTO person VALUES
(1,'John', 130),
(2,'Jack', 150),
(3,'William', 170),
(4,'Averel', 190),
(5,'Lucky Luke', 180),
(6,'Jolly Jumper', 250),
(7,'Rantanplan ', 90)
INSERT INTO coach VALUES
(1,'Football'),
(1,'Hockey'),
(1,'Skiing'),
(2,'Tennis'),
(2,'Curling'),
(4,'Tennis'),
(5,'Volleyball')
INSERT INTO athlete VALUES
(1,'Football'),
(1,'Hockey'),
(2,'Tennis'),
(2,'Volleyball'),
(2,'Hockey'),
(4,'Tennis'),
(5,'Volleyball'),
(3,'Tennis'),
(6,'Volleyball'),
(6,'Tennis'),
(6,'Hockey'),
(6,'Football'),
(6,'Cricket')
SELECT person.Person_id
,person.name
FROM person
INNER JOIN (
SELECT id
,sport
FROM athlete
UNION
SELECT id
,sport
FROM coach
) sports
ON sports.id = person.Person_id
GROUP BY person.Person_id
,person.name
HAVING COUNT(*) >= 3
ORDER BY Person_id
The coaches & athletes, ie people who are coaches or athletes, are relevant to your answer. That is union (rows in one or another), not (inner) join rows in one and another). (Although outer join involves a union, so there is a complicated way to use it here.) But there's no point in getting that by unioning only-coaches, only-athletes & coach-athletes.
Idiomatic is to group & count the union of Athletes & Coaches.
select id
from (select * from Athletes union select * from Coaches) as u
group by id
having COUNT(*) >= 3
Alternatively, you want ids of people who coach or play a 1st sport and coach or play a 2nd sport and coach or play a 3rd sport where the sports are all different.
with u as (select * from Athletes union select * from Coaches)
select u1.id
from u u1
join u u2 on u1.id = u2.id
join u u3 on u2.id = u3.id
where u1.sport <> u2.sport and u2.sport <> u3.sport and u1.sport <> u3.sport
If you wanted names you would join that with People.
Is there any rule of thumb to construct SQL query from a human-readable description?](https://stackoverflow.com/a/33952141/3404097)
I'm having some trouble with summing some column in my query with aggregation.
It's a bit difficult to describe what is happening but I'll try my best:
I have 3 tables - details, extra details and places.
Places is a table that contains places in the world. Details contains details about events that happened, and extra details provides some more data on the events. Each place has an ID and a ParentID (Like New York has an ID and it's parent ID is the US. Something like that). The ID of the event(details) appears a number of times as a column in the extra details table. The extra details table also holds the ID of the place that that event occurred at.
OK after all of that, what I'm trying to achieve is, for each place, the sum of the events that happened there. I know it sounds very specific, but it's what the client asked. Anyhow, example of what I'm trying to get to:
NewYork 60, Chicago 20, Houston 10 Then the US will have 90. And it has several levels.
So this is what I was trying to do:
With C(ID, NAME, COUNTT, ROOT_ID) as
(
SELECT d.ID, d.NAME,
(SELECT COUNT(LX.ID) as COUNTT
FROM EXTRA LX
RIGHT JOIN d ON LX.PLACE_ID = d.ID -- ****
GROUP BY d.ID, d.NAME),
d.ID as ROOT_ID
FROM PLACES d
UNION ALL
SELECT d.ID, d.NAME,
(SELECT COUNT(LX.ID) as COUNTT
FROM EXTRA LX
RIGHT JOIN d ON LX.PLACE_ID = d.ID
GROUP BY d.ID, d.NAME),
C.ROOT_ID
FROM PLACES dx
INNER JOIN C ON dx.PARENT_ID = C.ID
)
SELECT p.ID, p.NAME, S.SumIncludingChildren
FROM places p
INNER JOIN (
SELECT ROOT_ID, SUM(COUNTT) as SumIncludingChildren
FROM C
GROUP BY ROOT_ID
) S
ON p.ID = S.ROOT_ID
ORDER BY p.ID;
The details table is only for showing their data. I'll add that later. It's only comparing the respective columns. To making it work I don't need that. Only for the site data.
It doesn't work because it doesn't recognizes the 'd' where the '****' is. If I'll put a 'new instance' of that table, it won't work either. So I tried to replicate what the right join by doing 'NOT EXISTS IN' on a query that gets all the places instead of the right join...on. Same problem.
Maybe I don't get something. But I'm really seeking a solution and some explanation. I know my code isn't perfect.
Thanks in advance.
EDIT: I'm using OracleSQL on Toad 10.6
create table p(id number, up number, name varchar2(100));
create table e(id number, pid number, dsc varchar2(100));
insert into p values (1, null, 'country');
insert into p values (2, 1, 'center');
insert into p values (3, 1, 'province');
insert into p values (4, 2, 'capital');
insert into p values (5, 2, 'suburb');
insert into p values (6, 3, 'forest');
insert into p values (7, 3, 'village');
insert into p values (8, 7, 'shed');
insert into p values (9, 2, 'gov');
insert into e values (1, 8, 'moo');
insert into e values (2, 8, 'clank');
insert into e values (3, 7, 'sowing');
insert into e values (4, 6, 'shot');
insert into e values (5, 6, 'felling');
insert into e values (6, 5, 'train');
insert into e values (7, 5, 'cottage');
insert into e values (8, 5, 'rest');
insert into e values (9, 4, 'president');
insert into e values (10,1, 'GNP');
commit;
with
places as
(select id,
up,
connect_by_root id as root,
level lvl
from p
connect by prior id = up),
ev_stats as
(select root as place, max(lvl) as max_lvl, count(e.id) as ev_count
from places left outer join e
on places.id = e.pid
group by root)
select max_lvl, p.name, ev_count
from ev_stats inner join p on p.id = ev_stats.place
order by max_lvl desc;
this is my sql statement i get this error. but when i use only Max to single and without displaying other results it works. can someone help me
SELECT cat.CategoryName,sb.SubCategoryName,MAX((COUNT(bs.BookID)))
FROM
Category cat,SubCategory sb, Book_Subcategory bs
WHERE cat.CategoryID = sb.CategoryID AND sb.SubCategoryID = bs.SubCategoryID
GROUP BY cat.CategoryName, sb.SubCategoryName, bs.BookID;
ERROR at line 1:
ORA-00937: not a single-group group function
Can someone help me?
SQL does not allow aggregates of aggregates directly.
However, if you write the inner aggregate in a sub-query in the FROM clause (or use a WITH clause and a Common Table Expression, CTE), you can achieve the result:
SELECT gc1.CategoryName, gc1.SubCategoryName, gc1.BookCount
FROM (SELECT cat.CategoryName, sb.SubCategoryName,
COUNT(bs.BookID) AS BookCount
FROM Category AS cat
JOIN SubCategory AS sb ON cat.CategoryID = sb.CategoryID
JOIN Book_Subcategory AS bs ON sb.SubCategoryID = bs.SubCategoryID
GROUP BY cat.CategoryName, sb.SubCategoryName
) AS gc1
WHERE gc1.BookCount = (SELECT MAX(gc2.BookCount)
FROM (SELECT cat.CategoryName, sb.SubCategoryName,
COUNT(bs.BookID) AS BookCount
FROM Category AS cat
JOIN SubCategory AS sb
ON cat.CategoryID = sb.CategoryID
JOIN Book_Subcategory AS bs
ON sb.SubCategoryID = bs.SubCategoryID
GROUP BY cat.CategoryName, sb.SubCategoryName
) AS gc2
)
This is complex because it doesn't use a CTE, and there is a common table expression that must be written out twice.
Using the CTE form (possibly with syntax errors):
WITH gc1 AS (SELECT cat.CategoryName, sb.SubCategoryName,
COUNT(bs.BookID) AS BookCount
FROM Category AS cat
JOIN SubCategory AS sb
ON cat.CategoryID = sb.CategoryID
JOIN Book_Subcategory AS bs
ON sb.SubCategoryID = bs.SubCategoryID
GROUP BY cat.CategoryName, sb.SubCategoryName
)
SELECT gc1.CategoryName, gc1.SubCategoryName, gc1.BookCount
FROM gc1
WHERE gc1.BookCount = SELECT MAX(gc1.BookCount) FROM gc1);
Much tidier!
You can simulate a CTE with a temporary table if your DBMS makes it easy to create them. For example, IBM Informix Dynamic Server could use:
SELECT cat.CategoryName, sb.SubCategoryName,
COUNT(bs.BookID) AS BookCount
FROM Category AS cat
JOIN SubCategory AS sb ON cat.CategoryID = sb.CategoryID
JOIN Book_Subcategory AS bs ON sb.SubCategoryID = bs.SubCategoryID
GROUP BY cat.CategoryName, sb.SubCategoryName
INTO TEMP gc1;
SELECT gc1.CategoryName, gc1.SubCategoryName, gc1.BookCount
FROM gc1
WHERE gc1.BookCount = (SELECT MAX(gc1.BookCount) FROM gc1);
DROP TABLE gc1; -- Optional: table will be deleted at end of session anyway
Given the following tables and data, the main query (copied and pasted from this answer) gave the result I expected when run against IBM Informix Dynamic Server 11.50.FC6 on MacOS X 10.6.4, namely:
Non-Fiction SQL Theory 4
Fiction War 4
That doesn't prove that it 'must work' when run against Oracle - I don't have Oracle and can't demonstrate either way. It does show that there is at least one SQL DBMS that handles the query without problems. (Since IDS does not support the WITH clause and CTEs, I can't show whether that formulation works.)
Schema
CREATE TABLE Category
(
CategoryID INTEGER NOT NULL PRIMARY KEY,
CategoryName VARCHAR(20) NOT NULL
);
CREATE TABLE SubCategory
(
CategoryID INTEGER NOT NULL REFERENCES Category,
SubCategoryID INTEGER NOT NULL PRIMARY KEY,
SubCategoryName VARCHAR(20) NOT NULL
);
CREATE TABLE Book_SubCategory
(
SubCategoryID INTEGER NOT NULL REFERENCES SubCategory,
BookID INTEGER NOT NULL PRIMARY KEY
);
Data
INSERT INTO Category VALUES(1, 'Fiction');
INSERT INTO Category VALUES(2, 'Non-Fiction');
INSERT INTO SubCategory VALUES(2, 1, 'SQL Theory');
INSERT INTO SubCategory VALUES(2, 2, 'Mathematics');
INSERT INTO SubCategory VALUES(1, 3, 'Romance');
INSERT INTO SubCategory VALUES(1, 4, 'War');
INSERT INTO Book_SubCategory VALUES(1, 10);
INSERT INTO Book_SubCategory VALUES(2, 11);
INSERT INTO Book_SubCategory VALUES(3, 12);
INSERT INTO Book_SubCategory VALUES(3, 13);
INSERT INTO Book_SubCategory VALUES(4, 14);
INSERT INTO Book_SubCategory VALUES(1, 15);
INSERT INTO Book_SubCategory VALUES(1, 16);
INSERT INTO Book_SubCategory VALUES(2, 17);
INSERT INTO Book_SubCategory VALUES(1, 18);
INSERT INTO Book_SubCategory VALUES(3, 19);
INSERT INTO Book_SubCategory VALUES(4, 20);
INSERT INTO Book_SubCategory VALUES(4, 21);
INSERT INTO Book_SubCategory VALUES(4, 22);
I think the error is in the GROUP BY clause (bs.BookID does not belong there):
SELECT cat.CategoryName,sb.SubCategoryName,MAX(COUNT(bs.BookID))
FROM Category cat,SubCategory sb, Book_Subcategory bs
WHERE cat.CategoryID =sb.CategoryID AND sb.SubCategoryID=bs.SubCategoryID
GROUP BY cat.CategoryName,sb.SubCategoryName;
BTW, spaces (and punctuation) are your friends. Don't be lazy about it.