Complicated SQL query - sql

Does anyone have an idea about how to structure the following query:
Tables :
TBL_GAME id, name
TBL_CATEGORY id, name
LU_GAME_TO_CATEGORY gameid, catid
LU_GAME_TO_EVENT eventid, gameid
So, basically Categories have many games.
Events have many games.
I want to generate a report that shows the Categories listed by how many of its Games were used in Events. Ordered by the amount descending.
Is this possible ?

SELECT
c.id as catId,
c.name as catName,
g.id as gameId,
g.name as gameName,
sum(ge.INT_QUANTITY) as totalQuantities
FROM
TBL_CATEGORY as c,
TBL_GAME as g,
LU_GAME_TO_CATEGORY as gc,
LU_GAME_TO_EVENT as ge
WHERE
c.id = gc.catId AND
g.id = gc.gameId AND
g.id = ge.gameId
GROUP BY
c.id,
g.id
ORDER BY
totalQuantities desc,
c.name,
g.name

SELECT A.ID, SUM(D.INT_QUANTITY) AS YourSum
FROM TBL_CATEGORY A --Adapt your selected columns
INNER JOIN LU_GAME_TO_CATEGORY B ON A.id = B.catid
INNER JOIN TBL_CATEGORY C ON B.gameid = C.id
INNER JOIN LU_GAME_TO_EVENT D ON C.gameid = D.gameid
GROUP BY A.ID
ORDER BY YourSum DESC;
Here i don't adjust amount, because columns does not exist.
You must add Amount column in target table

Related

Counting associations from multiple tables

I want to see how many association each of my records in a given table have. Some of these association have some conditions attached to them
So far I have
-- Count app associations
SELECT
distinct a.name,
COALESCE(v.count, 0) as visitors,
COALESCE(am.count, 0) AS auto_messages,
COALESCE(c.count, 0) AS conversations
FROM apps a
LEFT JOIN (SELECT app_id, count(*) AS count FROM visitors GROUP BY 1) v ON a.id = v.app_id
LEFT JOIN (SELECT app_id, count(*) AS count FROM auto_messages GROUP BY 1) am ON a.id = am.app_id
LEFT JOIN (
SELECT DISTINCT c.id, app_id, count(c) AS count
FROM conversations c LEFT JOIN messages m ON m.conversation_id = c.id
WHERE m.visitor_id IS NOT NULL
GROUP BY c.id) c ON a.id = c.app_id
WHERE a.test = false
ORDER BY visitors DESC;
I run into problem with the last join statement for conversations. I want to count the number of conversations that have at least 1 message where the visitor_id is not null. For some reason, I get multiple records for each app, ie. the conversations are not being grouped properly.
Any ideas?
My gut feeling, based on limited understanding of the big picture: in the nested query selecting from conversations,
remove DISTINCT
remove c.id from SELECT list
GROUP BY c.app_id instead of c.id
EDIT: try this
...
LEFT JOIN (
SELECT app_id, count(*) AS count
FROM conversations c1
WHERE
EXISTS (
SELECT *
FROM messages m
WHERE m.conversation_id = c1.id and
M.visitor_id IS NOT NULL
)
GROUP BY c1.app_id) c
ON a.id = c.app_id

SQL: How to save order in sql query?

I have PostgreSQL database and I try to print all my users (Person).
When I execute this query
-- show owners
-- sorted by maximum cars amount
SELECT p.id
FROM car c JOIN person p ON c.person_id = p.id
GROUP BY p.id
ORDER BY COUNT(p.name) ASC;
I get all owners sorted by cars amount
Output: 3 2 4 1
And all order goes wrong when I try to link owner id.
SELECT *
FROM person p
WHERE p.id IN (
SELECT p.id
FROM car c JOIN person p ON c.person_id = p.id
GROUP BY p.id
ORDER BY COUNT(p.name) ASC);
Output: 1 2 3 4 and other data
You see than order is wrong. So here is my question how can I save that order?
Instead Of subquery use join. Try this.
SELECT p.*
FROM person p
JOIN (SELECT p.id,
Count(p.NAME)cnt
FROM car c
JOIN person p
ON c.person_id = p.id
GROUP BY p.id) b
ON p.id = b.id
ORDER BY cnt ASC
Untangle the mess. Aggregate first, join later:
SELECT p.*
FROM person p
JOIN (
SELECT person_id, count(*) AS ct
FROM car
GROUP BY person_id
) c ON c.person_id = p.id
ORDER BY c.cnt;
No need to join to person twice. This should be fastest if you count most or all rows.
For a small selection, correlated subqueries are faster:
SELECT p.*
FROM person p
ORDER BY (SELECT count(*) FROM car c WHERE c.person_id = p.id)
WHERE p.id BETWEEN 10 AND 20; -- some very selective predicate
As for your original: IN takes a set on the right hand, order of elements is ignored, so ORDER BY is pointless in the subuery.

Is there a simpler way to write this query?

I have three tables. Categories, topics, and posts. Each topic has a foreign key that references the category that it's under. Each post has a foreign key that references the topic that it's under.
The purpose of this query is to basically be the front page query. I want each category along with the number of topics and number of posts in each category. This is the query I have, and it works. Is this the simplest way of going about it?
SELECT c.*,
COUNT(t.idCategory) AS tCount,
p.pCount
FROM categories AS c
LEFT JOIN topics AS t
ON c.id = t.idCategory
LEFT JOIN (SELECT t.idCategory,
COUNT(p2.idTopic) AS pCount
FROM topics AS t
LEFT JOIN posts AS p2
ON t.id = p2.idTopic
GROUP BY t.idCategory) AS p
ON c.id = p.idCategory
GROUP BY t.idCategory
ORDER BY c.id
Thanks!
If you are talking of simplicity I guess this could be an answer:
Select
c.*,
(Select count(*) from topic t where c.id = t.idCategory) as tCount,
(Select count(*) from posts p join topics t2 on t2.id = p.idTopic where c.id = t2.idCategory) as pCount
From categories c
You can put together the topics and posts inside the derived table first before joining with the categories:
SELECT
c.id,
COUNT(tp.id) AS TotalTopics,
tp.TotalPosts
FROM categories AS c
LEFT JOIN (
SELECT
t.id,
t.idCategory,
COUNT(p.id) AS TotalPosts
FROM topics AS t
LEFT JOIN posts AS p ON t.id = p.idTopic
GROUP BY
t.id,
t.idCategory) AS tp ON c.id = tp.idCategory
GROUP BY
c.id,
tp.TotalPosts
ORDER BY c.id

Oracle SQL query count, group by with an innerjoin?

Basicly I have a bunch of Cars that belong to dealers and the dealers have a group.
The dealer table has the GROUP.ID and the Group table has the group name.
A Group has multiple dealers
So I want to count how many cars each group has.
I am using this atm
select
(select GROUP_NAME from "GROUP" where "GROUP".GROUP_ID = "DEALER"."GROUP_ID" ),
"DEALER"."GROUP_ID" as "DEALER GROUP ID",
"DEALER"."DEALER_NAME" as "DEALER DEALER NAME",
"CAR"."CAR_DEALER" as "CAR DEALER"
from
"CAR"
INNER JOIN
DEALER
ON
"DEALER"."DEALER_NAME" ="CAR"."CAR_DEALER"
I tried using group_by and count but I can't seem to get it to work
select
g.GROUP_NAME,
g.GROUP_ID,
count(*) as CAR_COUNT
from
GROUP g
inner join DEALER d on d.GROUP_ID = g.GROUP_ID
inner join CAR c on c.DEALER_ID = d.DEALERID
group by
/* Also add here all field you want to select from GROUP */
g.GROUP_NAME,
g.GROUPID

sql triple join: ambigious attribute name on a count

So I want to count a number of books, but the books are stored in 2 different tables with the same attribute name.
I want to get a result that looks like:
name1 [total number of books of 1]
name2 [total number of books of 2]
I tried this triple join;
SELECT DISTINCT name, count(book)
FROM writes w
LEFT JOIN person p on p.id = w.author
LEFT JOIN book b on b.title = w.book
LEFT JOIN controls l on l.controller=p.id
GROUP BY name
ORDER BY name DESC
but since book exists as an attribute in writes and in controls, it cant execute the query.
It can only do it if I leave out one of joins so it can identify book.
How can I tell the sql engine to count the number of both book attributes together for each person?
As a result of database design that you interested in, you should issue 2 different sql and then merge them to handle single output.
A)
SELECT DISTINCT w.name as 'Name', count(w.book) as 'Cnt'
FROM writes w
LEFT JOIN person p on p.id = w.author
LEFT JOIN book b on b.title = w.book
B)
SELECT DISTINCT l.name as 'Name', count(l.book) as 'Cnt'
FROM controls l
LEFT JOIN person p on p.id = l.controller
LEFT JOIN book b on b.title = l.book
For your purpose, you can get UNION of A and B.
or you can use them as data source on a third SQL
select A.Name, sum(A.Cnt+B.Cnt)
from A, B
where A.Name = B.Name
group by A.Name
order by A.Name
WITH T AS
(
SELECT DISTINCT 'WRITES' FROMTABLE, w.name, w.count(book)
FROM writes w
LEFT JOIN person p on p.id = w.author
LEFT JOIN book b on b.title = w.book
GROUP BY name
UNION ALL
SELECT DISTINCT 'CONTROLLS' FROMTABLE, c.name, count(c.book)
FROM controlls c
LEFT JOIN person p on p.id = c.author
LEFT JOIN book b on b.title = c.book
GROUP BY name
)
SELECT * FROM T ORDER BY NAME
Should work.
HTH
This will work on a per distinct author's ID to how many books they've written. The pre-aggregation will return one record per author with how many books by that author. THEN, join to the person table to get the name. The reason I am leaving it by ID and Name of the author is... what if you have two authors "John Smith", but they have respective IDs of 123 and 389. You wouldn't want these rolled-up to the same person (or do you).
select
P.ID,
P.Name,
PreAgg.BooksPerAuthor
from
( select
w.author,
count(*) BooksPerAuthor
from
writes w
group by
w.author ) PreAgg
JOIN Person P
on PreAgg.Author = P.id
order by
P.Name