SQL how to find message not responded to from DB - sql

I have created a database to stored messages sent from external staff (brokers) to internal staff(Bank A) and I would like to create a query that returns a list of all messages that haven't been responded too.
I have created the below query
Select COUNT(Message_Subject.MessageSubjectID), Message_Chain.MessageSubjectID
From Message_chain
join Message_Subject on message_chain.messagesubjectid = Message_subject.messagesubjectid
Group by Message_chain.messagesubjectID
Having COUNT(Message_chain.messagesubjectID) = 1;
Which return only messages in the message chain which haven't been responded to as there are no more Messages with the same subject ID. However I wanted the query to show more data, like the message subject and the Message body but when I add this into my query I get an error message.
Failed to execute query. Error: Column 'Message_chain.MessageBody' is
invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
This is the sql ...
Select COUNT(Message_Subject.MessageSubjectID), Message_Chain.MessageSubjectID, Message_Chain.MessageBody, Message_Subject.Subject
From Message_chain
join Message_Subject on message_chain.messagesubjectid = Message_subject.messagesubjectid
Group by Message_chain.messagesubjectID
Having COUNT(Message_chain.messagesubjectID) = 1;
Can anyone tell me the best way to go about this please? I have attached an image of the tables from my db.

you need to use aggregate functions to do achieve this :
SELECT
COUNT(Message_Subject.MessageSubjectID)
, MAX(Message_Chain.MessageSubjectID)
, MAX(Message_Chain.MessageBody)
, MAX(Message_Subject.Subject)
FROM
Message_chain
JOIN Message_Subject
ON Message_Chain.messagesubjectid = Message_Subject.messagesubjectid
GROUP BY
Message_Chain.messagesubjectID
HAVING
COUNT(Message_Chain.messagesubjectID) = 1;
however here is another way of doing this :
SELECT
Message_Chain.MessageSubjectID
, Message_Chain.MessageBody
, Message_Subject.Subject
FROM
Message_Subject
JOIN
(
SELECT
Message_Chain.MessageBody
, Message_Chain.MessageSubjectID
FROM
Message_Subject
GROUP BY
Message_Chain.messagesubjectID
HAVING
COUNT(Message_Chain.messagesubjectID) = 1
) msgchain
ON msgchain.messagesubjectid = Message_Subject.messagesubjectid;

Your having clause is requiring only one row, so just use aggregation functions. For instance:
select count(*), mc.MessageSubjectID,
max(ms.brokerid) as brokerid
from Message_chain mc join
Message_Subject ms
on mc.messagesubjectid = ms.messagesubjectid
group by mc.messagesubjectID
having count(*) = 1;

Related

SQL add a column with COUNT(*) to my query

I need to add a column with the content of this query :
SELECT COUNT(*) FROM account_subscriptiongroups WHERE account_subscriptiongroups.active = true AND account_subscriptiongroups.user_id = account_user.id
to this query :
SELECT
account_user.id as user_id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id as sub_group_id,
account_adminaction.description,
account_adminaction.id as admin_action_id,
account_adminaction.created_on as subscription_ended_on
FROM
account_adminaction
LEFT JOIN
account_user ON account_user.id = account_adminaction.user_id
LEFT JOIN
account_subscriptiongroup ON account_adminaction.sub_group_id = account_subscriptiongroup.id
WHERE
account_adminaction.created_on >= '2021-04-07' AND account_adminaction.created_on <= '2021-04-13' AND
((account_adminaction.description LIKE 'Arrêt de l''abonnement%') OR (account_adminaction.description LIKE 'L''utilisateur a arrêté%'))
ORDER BY
subscription_ended_on
I tried adding a LEFT JOIN like that:
LEFT JOIN
account_subscriptiongroup all_sg ON account_user.id = account_subscriptiongroup.user_id
with this line in my WHERE statement :
AND all_sg.active = true
and this line in my SELECT :
COUNT(all_sg.id)
but I get an error :
ERROR: column "account_user.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 2: account_user.id as user_id, account_user.email, account_us...
^
I don't understand how I could perform this action properly
To count something, you need to specify a group where that count applies.
So every column that you select (and is not used in an aggregate function, like COUNT or SUM), you need to mention in the GROUP BY clause.
Or to put it the other way around: the non-aggregate columns must apply to all rows that are contained in that particular COUNT.
So between the WHERE and ORDER BY clauses, add a GROUP BY clause:
GROUP BY account_user.id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id,
account_adminaction.description,
account_adminaction.id,
account_adminaction.created_on
If, on the other hand, you want a count from a different table, you can add a sub-select:
SELECT
account_user.id as user_id, account_user.email, account_user.first_name, account_user.last_name, account_user.phone,
account_subscriptiongroup.id as sub_group_id,
account_adminaction.description,
account_adminaction.id as admin_action_id,
account_adminaction.created_on as subscription_ended_on,
(SELECT COUNT(*)
FROM account_subscriptiongroups
WHERE account_subscriptiongroups.active = true
AND account_subscriptiongroups.user_id = account_user.id) AS groupcount
FROM
account_adminaction
LEFT JOIN
account_user ON account_user.id = account_adminaction.user_id
You can left join to to a derived table that does the grouping and counting:
SELECT au.id as user_id, au.email, au.first_name, au.last_name, au.phone,
asg.id as sub_group_id,
ad.description,
ad.id as admin_action_id,
ad.created_on as subscription_ended_on,
asgc.num_groups
FROM account_adminaction ad
LEFT JOIN account_user au ON au.id = ad.user_id
LEFT JOIN account_subscriptiongroups asg on ON ad.sub_group_id = asg.id
LEFT JOIN (
SELECT user_id, count(*) as num_groups
FROM account_subscriptiongroups ag
WHERE ag.active
GROUP by user_id
) asgc on asgc.user_id = au.id
WHERE ad.created_on >= '2021-04-07'
AND ad.created_on <= '2021-04-13'
AND ((ad.description LIKE 'Arrêt de l''abonnement%') OR (ad.description LIKE 'L''utilisateur a arrêté%'))
ORDER BY subscription_ended_on
It's not entirely clear to me, what you are trying to count, but another option (most probably slower) could be to use a window function combined with a filter clause:
count(*) filter (where asg.active) over (partition by asg.user_id) as num_groups
EDIT: my answer is the same as submitted by a_horse_with_no_name
Two answers, a literal one just solving the problem you posed, and then another one questioning whether what you asked for is really what you want.
Simple answer: modify your desired query to add user_id to the Select and remove user_id from the Where clause. Now you have a table that can be left-joined to the rest of your larger query.
...
Left Join (Select user_id, count(*) as total_count
From account_subscriptiongroup
Where account_subscriptiongroups.active = true
Group By user_id) Z
On Z.user_id=account_user.id
I question whether this count is what you really want here. This counts every account_subscriptiongroup entry for all time but only the active ones. Your larger query brings back inactive as well as active records, so if your goal is to create a denominator for a percentage, you are mixing 'apples and oranges' here.
If you decide you want a total by user of the records in your query instead, then you can add one more element to your larger query without adding any more tables. Use a windowing function like this:
Select ..., Sum(Case When account_subscriptiongroup.active Then 1 else 0 End) Over (Group By account_user.id) as total count
This just counts the records within the date range and having the desired actions.

Use count and group by in a joins table?

Here is my query and I want to add the "count of SalID group by OFID" and store the result in the same table.
SELECT
T_OF.OFID,
T_OF.OFDateDPrev, T_OF.OFDateFPrev,
T_OF_User.OFUserID,
T_OF_User.SalID
INTO T_tracing
FROM T_OF
INNER JOIN T_OF_User
ON T_OF_User.OFID = T_OF.OFID
I tried this:
SELECT
T_OF.OFID,
T_OF.OFDateDPrev, T_OF.OFDateFPrev,
T_OF_User.OFUserID,
Count (SalID) FROM T_OF_User GROUP BY OFID
INTO T_tracing
FROM T_OF
INNER JOIN T_OF_User
ON T_OF_User.OFID = T_OF.OFID
But I have an error message. Any help please?
I think you want a window function:
SELECT T_OF.OFID, T_OF.OFDateDPrev, T_OF.OFDateFPrev, T_OF_User.OFUserID,
Count(SalID) OVER (PARTITION BY T_OF.OFID) as cnt
INTO T_tracing
FROM T_OF JOIN
T_OF_User
ON T_OF_User.OFID = T_OF.OFID;
You also need to give the result of the expression a name for T_Tracing.

Why can't I access a field defined as "Select 1" from a subquery in the outer query?

I have this subquery:
LEFT JOIN (SELECT 1 as exist
, MAX (ev.EventDate) as eventdate
, evt.EventCode
, CCaseID
FROM stg.Event ev
JOIN stg.EventTemplate evt
ON ev.EventTemplateID = evt.ID
WHERE evt.EventCode = 'UN002'
Group by CCaseID, evt.EventCode) as un002
ON un002.CCaseID = ev.CCaseID
WHERE evt.EventCode = 'UN001'
AND (un002.eventdate < ev.eventdate OR un002.eventdate IS NULL)
Group by ev.CCaseID, evt.EventCode) as un001
ON cc.ID = un001.CCaseID
I am now trying to access the exist field in the outer query as per un001.exist but SQL Server tells me that it is an invalid field. What am I missing?
un001 doesnt have exist that field belong to un002 subquery.
Also you have a GROUP BY and the and ON so there is some missing code there.
You should simplify the code and use CTE to make it easy to read and debug.
Something like this :
WITH un001 as ( SELECT ... ),
un002 as ( SELECT ...)
SELECT *
FROM un001
JOIN un002
ON un001 .CCaseID = un002.CCaseID

Multi level GROUP BY clause not allowed

I have the following CrossTab query
TRANSFORM Max(VWDRSSTA.DATUM_ZEIT) AS MaxOfDATUM_ZEIT
SELECT VWDRSSTA.ANTRAGSNUMMER
,IIF(VWDRSSTA.SYSTEM = 'VS', (
SELECT (Max(VWDRSSTA.DUNKEL)) AS d
FROM VWDRSSTA
), NULL) AS Dunkel
,Max(VWDRSSTA.VERS_NR_INT) AS Versicherungsnummer
FROM VWDRSSTA
INNER JOIN V_NAMES ON (VWDRSSTA.SYSTEM = V_NAMES.SYSTEM_CODE)
AND (VWDRSSTA.EREIGNIS = V_NAMES.EREIGNIS)
GROUP BY VWDRSSTA.ANTRAGSNUMMER
ORDER BY VWDRSSTA.ANTRAGSNUMMER
PIVOT V_NAMES.MAPPED_NAME;
which gives me the error "Multi-level GROUP BY clause is not allowed in a subquery". Where am I going wrong with the code?
Try with VWDRSSTA.SYSTEM in the GROUP BY clause.
It should do the trick.
Edit to detail my answer :
TRANSFORM Max(VWDRSSTA.DATUM_ZEIT) AS MaxOfDATUM_ZEIT
SELECT VWDRSSTA.ANTRAGSNUMMER
,IIF(VWDRSSTA.SYSTEM = 'VS', (
SELECT (Max(VWDRSSTA.DUNKEL)) AS d
FROM VWDRSSTA
), NULL) AS Dunkel
,Max(VWDRSSTA.VERS_NR_INT) AS Versicherungsnummer
FROM VWDRSSTA
INNER JOIN V_NAMES ON (VWDRSSTA.SYSTEM = V_NAMES.SYSTEM_CODE)
AND (VWDRSSTA.EREIGNIS = V_NAMES.EREIGNIS)
GROUP BY VWDRSSTA.ANTRAGSNUMMER, VWDRSSTA.SYSTEM
ORDER BY VWDRSSTA.ANTRAGSNUMMER
PIVOT V_NAMES.MAPPED_NAME;
You cross-tab query contains a secondary "aggregated" SQL and this is not allowed in ACCESS Cross-tab query.
change the Select max(vwdrssta.dunkel) ... part to DMax("dunkel", "vwdrssta") which will get rid of that error message.

Not a group by expression Beginner

Here I have three tables
PLAYS {ID_athlete, ID_sport, best_result}
PARTICIPATE {ID_athlete, ID_competition, result}
ISAT {ID_sport, ID_competition}
I would like to update the best_result attribute IN PLAYS.
I wrote this but I have the "not a group expression" error.
UPDATE PLAYS PL
SET BEST_RESULT =
(
SELECT MAX(RESULT)
FROM PARTICIPATE P
GROUP BY P.ID_ATHLETE, P.ID_COMPETITION
HAVING PL.ID_ATHLETE = P.ID_ATHLETE
AND P.ID_COMPETITION IN
(
SELECT ID_COMPETITION
FROM ISAT
WHERE ID_SPORT = PL.ID_SPORT
)
)
I don't know where my error comes from. I want to get the max result of a certain athlete in a certain sport and put it in best_result.
I think you are looking for this. Use a where clause to limit it and take the max. HAVING is for aggregate filters.
UPDATE PLAYS PL
SET BEST_RESULT =
(
SELECT MAX(RESULT)
FROM PARTICIPATE P
WHERE PL.ID_ATHLETE = P.ID_ATHLETE
AND P.ID_COMPETITION IN
(
SELECT ID_COMPETITION
FROM ISAT
WHERE ID_SPORT = PL.ID_SPORT
)
)
You have got your WHERE and HAVING caluses mixed up. For this SQL (getting the max result), you don't even need GROUP BY or HAVING. Just plain a condition should do the job.
UPDATE PLAYS PL
SET BEST_RESULT =
(
SELECT MAX(RESULT)
FROM PARTICIPATE P
WHERE PL.ID_ATHLETE = P.ID_ATHLETE
AND P.ID_COMPETITION IN
(SELECT ID_COMPETITION
FROM ISAT
WHERE ID_SPORT = PL.ID_SPORT)
)