Select newest TOPIC for each SUBJECT in SQL Server 2008 - sql

I have a project about programming a forum.
I have 2 tables in the database:
SUBJECT (idSUB, titleSUB);
TOPIC (idTOP, titleTOP, contentTOP, idSUB, idUser [user create topic], Time);
All I want is:
+ select COUNT(*) from TOPIC as numTOPIC group by idSUB--> as Table A
+select TOP 1 titleTOP order by Time desc-> as newestTOP group by idSUB---> as Table B
+ Then JOIN 3 table A,B,SUBJECT--> C(idSUB,titleSUB,numTOPIC,newestTOP, idUser (who created the newest topic))
I've found the way to LEFT JOIN A,SUBJECT-> C(idSUB,titleSUB,numTOPIC) but I really don't know the right syntax to JOIN 3 Tables above.
SELECT
a.idSUB, a.titleSUB,
COUNT(b.idSUB) numTOPIC
FROM
SUBJECT a
LEFT JOIN
TOPIC b ON a.idSUB = b.idSUB
GROUP BY
a.idSUB, a.titleSUB
I just want do this in only one query. Help!
UPDATE:
By the code of #John Bingham below, the output table cannot display the SUBJECT which doesn't have any TOPIC. I want all the TOPIC can be display.
SELECT
s.idSUB, s.titleSUB, a.numTOPIC,
isnull(b.newestTOP, '') as [Newest Topic],
isnull(b.idUser, '')
FROM
Subject s
INNER JOIN
(SELECT
IDSub, Count(*) as NumTopic
FROM
Topic
GROUP BY IDSub) a ON s.IDSub = a.IDSub
LEFT JOIN
(SELECT
t.IDSub, t.titleTop as newestTop, t.idUser as [idUser]
FROM
Topic t
INNER JOIN
(SELECT IDSub, Max([Time]) as tm
FROM Topic
GROUP BY IDSub) x ON t.IDSub = x.IDSub
WHERE t.[Time] = x.tm) b ON s.IDSub = b.IDSub
It's the right query but I want more exactly, help!

My inclination is to convert each of the requirements (a), (b) & (c) into subqueries which deliver the virtual tables to your query, rather than attempting to combine the base tables to deliver the requirements in a singe hit. So -
SELECT s.idSUB, s.titleSUB, a.numTOPIC, isnull(b.newestTOP, '') as [Newest Topic],
isnull(b.idUser, '')
FROM Subject s
INNER JOIN (SELECT IDSub, Count(*) as NumTopic FROM Topic GROUP BY IDSub) a
ON s.IDSub = a.IDSub
LEFT JOIN (
SELECT t.IDSub, t.titleTop as newestTop, t.idUser as [idUser]
FROM Topic t
INNER JOIN (
SELECT IDSub, Max([Time]) as tm FROM Topic GROUP BY IDSub
) x ON t.IDSub = x.IDSub
WHERE t.[Time] = x.tm
) b ON s.IDSub = b.IDSub

You want to display idSub,titleSub,numTopic,newestTOP,idUser where numTopic is number of topics in any subject i.e idSub and newestTOP is latest topic for the same subject.
I am using MySQL and tested the following query and it works fine.
SELECT S.idSub, S.titleSUB, TOPIC_COUNT_TABLE.NUMTOPIC, NEWESTTOPIC_TABLE.NEWESTTOPIC,
NEWESTTOPIC_TABLE.IDUSER
FROM SUBJECT S,
(SELECT IDSUB, COUNT(*) AS NUMTOPIC FROM TOPIC
GROUP BY TOPIC.IDSUB) AS TOPIC_COUNT_TABLE,
(SELECT T.IDSUB,T.titleTOP AS NEWESTTOPIC, T.IDUSER
FROM TOPIC T,(SELECT IDTOP,IDSUB,MAX(TIME) AS MAXTIME FROM TOPIC
GROUP BY IDSUB ) AS MAXTIME_TABLE
WHERE T.TIME = MAXTIME_TABLE.MAXTIME) AS NEWESTTOPIC_TABLE
WHERE S.IDSUB = NEWESTTOPIC_TABLE.IDSUB AND S.IDSUB = TOPIC_COUNT_TABLE.IDSUB;

Related

ORDER in CTE lost after GROUP BY

I have the following SQL
WITH tally AS (
SELECT results.answer,
results.poll_id,
count(1) AS votes
FROM (
SELECT pr.poll_id,
unnest(pr.response) AS answer
FROM poll_responses pr
LEFT JOIN polls p ON pr.poll_id = p.id
LEFT JOIN poll_collections pc ON pc.id = p.poll_collection_id
WHERE pc.id = ${pollCollectionId}
) AS results
GROUP BY results.answer, results.poll_id
),
all_choices AS (SELECT unnest(pls.choices) AS choice,
pls.id AS poll_id
FROM poll_collections pcol
INNER JOIN polls pls
ON pcol.id = pls.poll_collection_id
WHERE pcol.id = ${pollCollectionId}),
unvoted_tally AS (SELECT ac.choice AS answer,
ac.poll_id,
0 AS total
FROM all_choices ac
LEFT JOIN tally t ON t.answer = ac.choice
WHERE t.answer IS NULL),
final_tally AS (SELECT *
FROM tally
UNION
ALL
SELECT *
FROM unvoted_tally),
sorted_tally AS (
SELECT ft.*
FROM final_tally ft
ORDER BY array_position(array(SELECT choice FROM all_choices), ft.answer)
)
SELECT json_agg(poll_results.polls) AS polls
FROM (
SELECT json_array_elements(json_agg(results)) -> 'poll' AS polls
FROM (
SELECT json_build_object(
'id', st.poll_id,
'question', pls.question,
'choice-type', pls.choice_type,
'results',
json_agg(json_build_object('choice', st.answer, 'votes', st.votes)),
'chosen', pr.response
) AS poll
FROM sorted_tally st
LEFT JOIN polls pls
ON
pls.id = st.poll_id
LEFT JOIN poll_responses pr
ON
pr.poll_id = st.poll_id AND
pr.email = ${email}
GROUP BY st.poll_id, pls.choice_type, pr.response, pls.question
) AS results)
AS poll_results;
I have a poll_responses table which store the user responses of a poll. I want to order the responses in exactly the same order they are stored in the polls table - as an array e.g., {Yes, No, Maybe}.
I applied the ORDER BY array_position(array(SELECT choice FROM all_choices), ft.answer) in the sorted_tally CTE.
However, in the file SELECT after applying GROUP BY the order is lost.
Is there a way to preserve the order of the choices?
Also, are there any optimizations applicable?
Much appreciated!
In json_build_object or json_agg you can set ORDER BY clause. First, have the last CTE SELECT needed order expression as a new column, then run in outermost query:
CTE
...
sorted_tally AS (
SELECT ft.votes
, ft.poll_id
, ft.answer
, array_position(array(SELECT choice FROM all_choices),
ft.answer) AS choice_order
FROM final_tally ft
ORDER BY
)
Outermost Query
...
json_build_object(
'id', st.poll_id,
'question', pls.question,
'choice-type', pls.choice_type,
'results', json_agg(json_build_object('choice', st.answer,
'votes', st.votes)
ORDER BY st.choice_order),
'chosen', pr.response
) AS poll
ORDER BY in a CTE doesn't really matter. It may work, but SQL Server is free to re-order the rows unless you specify ORDER BY in the outermost query to order all the results.

SQL query to get 10 last topics with replies

I have a simple forum database and want to write SQL query to get
10 last topics (threads) with replies from 3 or more unique posters.
Result: Topic (thread) name | Last message text | User name | Date
UPDATED:
Here is what I tried:
SELECT thread.thread_id, thread.thread_name, message.thread_id,
COUNT(message.thread_id)
FROM thread, message
WHERE message.thread_id = thread.thread_id
GROUP BY message.thread_id
HAVING COUNT(message.thread_id) >= 3
LIMIT 10
But it returns 10 topics (threads) with 3 or more replies (but not 3 or more unique posters)
UPDATE 2:
SELECT message.thread_id AS ID, thread.thread_name AS topic
FROM message INNER JOIN thread
ON message.thread_id = thread.thread_id
GROUP BY message.thread_id
HAVING COUNT(*) >= 3
LIMIT 10
This also returns 10 topics with 3 or more replies
UPDATE 3, thanks #shawnt00
SELECT thread.thread_name AS 'Topic', message_text AS 'Message', person_nickname AS 'Nickname', message_date AS 'Date'
FROM thread,
(
SELECT thread.thread_id, MAX(message_date) AS last_date
FROM thread
INNER JOIN message ON message.thread_id = thread.thread_id
GROUP BY thread.thread_id
HAVING COUNT(DISTINCT message.person_id) >= 3
) AS temp
INNER JOIN message
ON message.thread_id = temp.thread_id AND message.message_date = temp.last_date
INNER JOIN person ON person.person_id = message.person_id
WHERE thread.thread_id = temp.thread_id
ORDER BY message.message_date DESC
LIMIT 10
with data as (
select p.thread_id, max(message_date) as last_date
from thread t inner join message m on m.thread_id = t.thread_id
group by p.thread_id
having count(distinct m.person_id) >= 3
)
select *
from data d
inner join message m
on m.thread_id = d.thread_id and m.message_date = m.last_date
inner join person p on p.person_id = m.person_id;
The only assumption is that there can't be ties on dates. If you have analytic functions available then there are other approaches using those. I'm sure you can figure out how to get top 10.
i think you need below query.
Assuming that you are using mysql so i used Limitfunction as you want last 3 unique row
select distinct th.thread_name as Topic_name, m.message_text, p.person_nickname as User_name, m.message_date as Date from message m
inner join thread th on m.thread_id=th.thread_id
inner join persion p on p.persion_id=m.persion_id
order by message_date desc
Limit 3
But if the db is SQLSERVER Then use below query
select distinct TOP(3) th.thread_name as Topic_name, m.message_text, p.person_nickname as User_name, m.message_date as Date from message m
inner join thread th on m.thread_id=th.thread_id
inner join persion p on p.persion_id=m.persion_id
order by message_date desc

Sql code for distinct fields

I was wondering if anyone can help me with this query.
I have two tables that I join together (DDS2ENVR.QBO AND KCA0001.ORTS)
THE QBO Table has a field labeled NIIN AND RIC. THE KCA0001.ORTS table has a field named SERVICE and OWN_RIC.
I Join the tables by QBO.RIC and ORTS.OWN_RIC. My dilemma is that under the NIIN field multiple rows can be identical but have different values for RIC.
Example:
NIIN RIC
123455 A
122222 B
123456 C
122222 A
I want to query a distinct count for NIINS that separates by the different service where it does not overlap. So example NIIN should only find distinct values only associated with A where the same NIIN is not found in B,C,D etc.
SELECT D.SERVICE, COUNT(C.NIIN)
FROM DDS2ENVR.QBO C
JOIN KCA0001.ORTS D ON D.OWN_RIC = C.RIC
WHERE C.SITE_ID = ('HEAA')
GROUP BY D.SERVICE
HAVING COUNT(DISTINCT C.NIIN) > 1
Please ask questions if this does not make any sense.
Using Not Exists
SELECT D.SERVICE, COUNT(C.NIIN)
FROM DDS2ENVR.QBO C
JOIN KCA0001.ORTS D ON D.OWN_RIC = C.RIC
WHERE C.SITE_ID = ('HEAA')
and NOT EXISTS (Select 1 from DDS2ENVR.QBO C1 where C1.NIIN = C.NIIN and C1.RIC <> C.RIC)
GROUP BY D.SERVICE
HAVING COUNT(DISTINCT C.NIIN) > 1
Also if the table DDS2ENVR.QBO doesn't contain duplicates and your dbms supports CTE
With cte as
(Select NIIN from DDS2ENVR.QBO group by NIIN having count(*) = 1)
SELECT D.SERVICE, COUNT(C.NIIN)
FROM DDS2ENVR.QBO C
JOIN KCA0001.ORTS D ON D.OWN_RIC = C.RIC
WHERE C.SITE_ID = ('HEAA')
and C.NIIN in (Select * from cte)
GROUP BY D.SERVICE
HAVING COUNT(DISTINCT C.NIIN) > 1

PostgreSQL - how to query "result IN ALL OF"?

I am new to PostgreSQL and I have a problem with the following query:
WITH relevant_einsatz AS (
SELECT einsatz.fahrzeug,einsatz.mannschaft
FROM einsatz
INNER JOIN bergefahrzeug ON einsatz.fahrzeug = bergefahrzeug.id
),
relevant_mannschaften AS (
SELECT DISTINCT relevant_einsatz.mannschaft
FROM relevant_einsatz
WHERE relevant_einsatz.fahrzeug IN (SELECT id FROM bergefahrzeug)
)
SELECT mannschaft.id,mannschaft.rufname,person.id,person.nachname
FROM mannschaft,person,relevant_mannschaften WHERE mannschaft.leiter = person.id AND relevant_mannschaften.mannschaft=mannschaft.id;
This query is working basically - but in "relevant_mannschaften" I am currently selecting each mannschaft, which has been to an relevant_einsatz with at least 1 bergefahrzeug.
Instead of this, I want to select into "relevant_mannschaften" each mannschaft, which has been to an relevant_einsatz WITH EACH from bergefahrzeug.
Does anybody know how to formulate this change?
The information you provide is rather rudimentary. But tuning into my mentalist skills, going out on a limb, I would guess this untangled version of the query does the job much faster:
SELECT m.id, m.rufname, p.id, p.nachname
FROM person p
JOIN mannschaft m ON m.leiter = p.id
JOIN (
SELECT e.mannschaft
FROM einsatz e
JOIN bergefahrzeug b ON b.id = e.fahrzeug -- may be redundant
GROUP BY e.mannschaft
HAVING count(DISTINCT e.fahrzeug)
= (SELECT count(*) FROM bergefahrzeug)
) e ON e.mannschaft = m.id
Explain:
In the subquery e I count how many DISTINCT mountain-vehicles (bergfahrzeug) have been used by a team (mannschaft) in all their deployments (einsatz): count(DISTINCT e.fahrzeug)
If that number matches the count in table bergfahrzeug: (SELECT count(*) FROM bergefahrzeug) - the team qualifies according to your description.
The rest of the query just fetches details from matching rows in mannschaft and person.
You don't need this line at all, if there are no other vehicles in play than bergfahrzeuge:
JOIN bergefahrzeug b ON b.id = e.fahrzeug
Basically, this is a special application of relational division. A lot more on the topic under this related question:
How to filter SQL results in a has-many-through relation
Do not know how to explain it, but here is an example how I solved this problem, just in case somebody has the some question one day.
WITH dfz AS (
SELECT DISTINCT fahrzeug,mannschaft FROM einsatz WHERE einsatz.fahrzeug IN (SELECT id FROM bergefahrzeug)
), abc AS (
SELECT DISTINCT mannschaft FROM dfz
), einsatzmannschaften AS (
SELECT abc.mannschaft FROM abc WHERE (SELECT sum(dfz.fahrzeug) FROM dfz WHERE dfz.mannschaft = abc.mannschaft) = (SELECT sum(bergefahrzeug.id) FROM bergefahrzeug)
)
SELECT mannschaft.id,mannschaft.rufname,person.id,person.nachname
FROM mannschaft,person,einsatzmannschaften WHERE mannschaft.leiter = person.id AND einsatzmannschaften.mannschaft=mannschaft.id;

MySql scoping problem with correlated subqueries

I'm having this Mysql query, It works:
SELECT
nom
,prenom
,(SELECT GROUP_CONCAT(category_en) FROM
(SELECT DISTINCT category_en FROM categories c WHERE id IN
(SELECT DISTINCT category_id FROM m3allems_to_categories m2c WHERE m3allem_id = 37)
) cS
) categories
,(SELECT GROUP_CONCAT(area_en) FROM
(SELECT DISTINCT area_en FROM areas c WHERE id IN
(SELECT DISTINCT area_id FROM m3allems_to_areas m2a WHERE m3allem_id = 37)
) aSq
) areas
FROM m3allems m
WHERE m.id = 37
The result is:
nom prenom categories areas
Man Multi Carpentry,Paint,Walls Beirut,Baalbak,Saida
It works correclty, but only when i hardcode into the query the id that I want (37).
I want it to work for all entries in the m3allem table, so I try this:
SELECT
nom
,prenom
,(SELECT GROUP_CONCAT(category_en) FROM
(SELECT DISTINCT category_en FROM categories c WHERE id IN
(SELECT DISTINCT category_id FROM m3allems_to_categories m2c WHERE m3allem_id = m.id)
) cS
) categories
,(SELECT GROUP_CONCAT(area_en) FROM
(SELECT DISTINCT area_en FROM areas c WHERE id IN
(SELECT DISTINCT area_id FROM m3allems_to_areas m2a WHERE m3allem_id = m.id)
) aSq
) areas
FROM m3allems m
And I get an error:
Unknown column 'm.id' in 'where
clause'
Why?
From the MySql manual:
13.2.8.7. Correlated Subqueries
[...]
Scoping rule: MySQL evaluates from inside to outside.
So... do this not work when the subquery is in a SELECT section? I did not read anything about that.
Does anyone know? What should I do? It took me a long time to build this query... I know it's a monster query but it gets what I want in a single query, and I am so close to getting it to work!
Can anyone help?
You can only correlate one level deep.
Use:
SELECT m.nom,
m.prenom,
x.categories,
y.areas
FROM m3allens m
LEFT JOIN (SELECT m2c.m3allem_id,
GROUP_CONCAT(DISTINCT c.category_en) AS categories
FROM CATEGORIES c
JOIN m3allems_to_categories m2c ON m2c.category_id = c.id
GROUP BY m2c.m3allem_id) x ON x.m3allem_id = m.id
LEFT JOIN (SELECT m2a.m3allem_id,
GROUP_CONCAT(DISTINCT a.area_en) AS areas
FROM AREAS a
JOIN m3allems_to_areas m2a ON m2a.area_id = a.id
GROUP BY m2a.m3allem_id) y ON y.m3allem_id = m.id
WHERE m.id = ?
The reason for the error is that in the subquery m is not defined. It is defined later in the outer query.