Not a group by expression Beginner - sql

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)
)

Related

SQL how to find message not responded to from DB

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;

Why is the same SQL sometimes report errors, some time it runs fine?

This is the SQL statement:
select
ua.*, uuu.user_name
from
(select
ud.dev_id,
(select uud.user_id as user_id
from user_device uud
where ud.dev_id = uud.dev_id and assigned = 1) user_id,
(select count(1)
from user_device du
where du.dev_id = ud.dev_id) user_number,
de.license
from
user_device ud
inner join
device de on ud.dev_id = de.dev_id
where ud.user_id = 'XXXXXX') ua
left join
user_info uuu on uuu.user_id = ua.user_id
Execute the same SQL, it sometimes reports this error, but sometimes it runs just fine.
The error :
and this is what I want (with another user_id yesterday)
The error is pretty self-explanatory. I'm pretty sure it is referring to this subquery:
(select uud.user_id
from user_device uud
where ud.dev_id = uud.dev_id and assigned = 1
)
Clearly, this subquery is returning multiple rows under some circumstances. A quick and dirty fix is to add and rownum = 1 to the where clause.
You can determine where the duplicates are by running:
select uud.dev_id, count(*) as cnt
from user_device uud
where uud.assigned = 1
group by uud.dev_id
having count(*) > 1;

Oracle database. How to update selected columns.

the problem is:
I have two tables(column names are in brackets):
Cars (CarColorId, CarName), CarColor (CarColorId, CarColorName);
The task is to UPDATE Cars.CarName with a string "_updated" but only if CarColor.CarColorName = 'red'. I have know idea how to do this without joins
I have tried this way:
UPDATE Cars set CarName = concat (CarName, '_updated') WHERE CarColorId = 1;
CarColorId = 1 = red;
This request works, but the task is to use both tables
You can try any one of this in Oracle
Normal Update
UPDATE
CARS
SET
CARS.CARNAME =
CONCAT ( CARS.CARNAME,
'_updated' )
WHERE
EXISTS
(SELECT
CARCOLOR.CARCOLORID
FROM
CARCOLOR
WHERE
CARS.CARCOLORID = CARCOLOR.CARCOLORID
AND CARCOLOR.CARCOLORNAME = 'RED');
Using Inline View (If it is considered updateable by Oracle)
Note: If you face a non key preserved row error add an index to resolve the same to make it update-able
UPDATE
(SELECT
CARS.CARNAME AS OLD,
CONCAT ( CARS.CARNAME,
'_updated' )
AS NEW
FROM
CARS
INNER JOIN
CARCOLOR
ON CARS.CARCOLORID = CARCOLOR.CARCOLORID
WHERE
CARCOLOR.CARCOLORNAME = 'RED') T
SET
T.OLD = T.NEW;
Using Merge
MERGE INTO
CARS
USING
(SELECT
CARS.ROWID AS RID
FROM
CARS
INNER JOIN
CARCOLOR
ON CARS.CARCOLORID = CARCOLOR.CARCOLORID
WHERE
CARCOLOR.CARCOLORNAME = 'RED')
ON
( ROWID = RID )
WHEN MATCHED
THEN
UPDATE SET CARS.CARNAME =
CONCAT ( CARS.CARNAME,
'_updated' );
You can modify your query like this:
UPDATE
Cars
set
CarName = concat (CarName, '_updated')
WHERE
CarColorId in (
select
CarColorId
from
CarColor
where
CarColorName='red'
)
;
I know you said that you did not know how to do this without any joins. I don't know if that means you would prefer to avoid joins or whether you would be open to the possibility of using them if you could. If the latter is the case then check out this piece of code:
UPDATE
(
SELECT c.CarName, cc.CarColorName
FROM Cars c
INNER JOIN CarColors cc
ON c.CarColorId = cc.CarColorId
) CarsWithColor
SET CarsWithColor.CarName = CarsWithColor.CarName || '_Updated'
WHERE CarsWithColor.CarColorName = 'red';
Hope this helps also.
T

Using Count() and Sum() correctly in SQL?

Ok, so I hope I can explain this question well enough, because I feel like this is going to be a tough one.
I have two tables I'm working with today. These look like:
#pset table (PersonID int, SystemID int, EntitlementID int, TargetID int)
#Connector table (TargetName varchar(10), fConnector bit)
The first table stores records that tell me, oh this person has this system, which is composed of these entitlements, whom have these targets. A little complicated, but stay with me. The second stores the TargetName and then whether or not that target has a connector in my not-so-theoretical system.
What I'm trying to do is merge these two tables so that I can see the target flag for each row in #pset. This will help me later as you'll see.
If each entitlement in a system has a connector to the target (the flag is true for all of them), then I'd like to know.
All the others should go into a different table.
This is what I tried to do, but it didn't work. I need to know where I went wrong. Hopefully someone with more experience than me will be able to answer.
-- If the count(123) = 10 (ten rows with SystemID = 123) and the sum = 10, cool.
select pset.*, conn.fConnector from #pset pset
inner join vuTargets vt
on vt.TargetID = pset.TargetID
inner join #conn conn
on conn.TargetName = vt.TargetName
group by ProfileID, SystemRoleID, EntitlementID, TargetID, fConnector
having count(SystemID) = sum(cast(fConnector as int))
order by ProfileID
and
-- If the count(123) = 10 (ten rows with SystemID = 123) and the sum <> 10
select pset.*, conn.fConnector from #pset pset
inner join vuTargets vt
on vt.TargetID = pset.TargetID
inner join #conn conn
on conn.TargetName = vt.TargetName
group by ProfileID, SystemRoleID, EntitlementID, TargetID, fConnector
having count(SystemID) <> sum(cast(fConnector as int))
order by ProfileID
Unfortunately, these do not work :(
Edit
Here is a screenshot showing the problem. Notice ProfileID 1599 has a SystemID of 1126567, but one of the entitlements doesn't have a connector! How can I get both of these rows into the second query? (above)
Your basic problem is that you're trying to roll up to two different record sets.
The initial set (the SELECT and GROUP BY clauses) is saying that you want one record for every difference in the set [ProfileId, SystemId, EntitlementId, TargetId, fConnector].
The second set (the HAVING clause) is saying that you want, for every row in the inital set, to compare it's COUNT of records with the SUM of the connections. However, because you've asked for grouping down to the individual flag, this has the effect of getting a single row for each flag (assuming 1-to-1 relationships). Effectively, you're saying - 'Hey, if this target has a connection? Yeah, I want it'.
What you appear to want is a roll up to the SystemId value. To do that, you will need to change your SELECT and GROUP BY clauses to only include the set [ProfileId, SystemId]. This will return only those rows (keyed from profile and system) who has all targets 'connected'. You will not be able to see the individual entitlements, targets, and whether they are connected (you will be able to infer that they will all be/not be connected, however).
EDIT:
In the interests of full disclosure, here is how you'd get something similar to your original results set, where it lists all EntitlementIds and TargetIds:
WITH all_connections as (SELECT pset.ProfileId, pset.SystemRoleId
FROM #pset pset
INNER JOIN vuTargets vt
ON vt.TargetId = pset.TargetId
INNER JOIN #conn conn
ON conn.TargetName = vt.TargetName
GROUP BY pset.ProfileId, pset.SystemRoleId
HAVING COUNT(pset.SystemRoleId)
= SUM(CAST(fConnector as INT)))
SELECT pset.*
FROM #pset pset
JOIN all_connections conn
ON conn.ProfileId = pset.ProfileId
AND conn.SystemRoleId = pset.SystemRoleId
This should get you a listing, down to the TargetId, of ProfileId/SystemRoleId keys where all EntitlementIds and TargetIds have a connection (or, flip the CTE = to <> for those where not all do).
Edit: fixed my original queries, updated the description as well
You can split this up: first find the TargetIDs that have an fConnector of 0. Then find the PersonID, SystemID pairs that have any target equal to the ones you found. Then select the relevant data: (this finds the PersonID, SystemID pair where at least one entitlement does not have a connector to the target)
with abc as (
select PersonID, SystemID
from pset P
where TargetID in (
select TargetID
from vuTargets V join connector C on V.TargetName = C.TargetName
where C.fConnector = 0
)
)
select P.PersonID, P.SystemID, P.EntitlementID, P.TargetID, C.fConnector
from pset P
join abc on ((P.PersonID = abc.PersonID) and (P.SystemID = abc.SystemID))
join vuTargets V on P.TargetID = V.TargetID
join connector C on V.TargetName = C.TargetName
The query to find the PersonID, SystemID pairs where all entitlements have a connector to the target is similar:
with abc as (
select PersonID, SystemID
from pset P
where TargetID in (
select TargetID
from vuTargets V join connector C on V.TargetName = C.TargetName
where C.fConnector = 0
)
)
select P.PersonID, P.SystemID, P.EntitlementID, P.TargetID, C.fConnector
from
pset P
join abc on ((P.PersonID <> abc.PersonID) or (P.SystemID <> abc.SystemID))
join vuTargets V on P.TargetID = V.TargetID
join connector C on V.TargetName = C.TargetName
The difference is in the join with the temp table (<> vs =). This is very similar to zero's answer, but doesn't use counts or sums.

issue with MAX statement in t-sql code

This is the code:
Select BCPP.*
from ViewPBCPP BCPP
inner join
(
Select MBC.PC PC
,MRT.Name CT
,Max(dbo.CalcDatefromUTC(MBC.CreatedDate)) as LRDate
from TableBACC MBC
inner join TableSC.RT MRT
ON MBC.RTid = MRT.id
where MBC.Isdeleted = 'False'
and MBC.PC <> 'NULL'
Group by MBC.PC
,MRT.Name
) MBCR
ON BCPP.P_id = MBCR.PC
and BCPP.CreatedDate = MBCR.LRDate
and BCPP.CT = MBCR.CT
Now Max(dbo.CalcDatefromUTC(MBC.CreatedDate)) is actually a function
Query above works fine with Max(dbo.CalcDatefromUTC(MBC.CreatedDate))
Now when I write
Max(dbo.CalcDatefromUTC(MBC.CreatedDate)) + Min(dbo.CalcDatefromUTC(MBC.CreatedDate))
I cannot extract any values at all from this query written above
If I write just
(dbo.CalcDatefromUTC(MBC.CreatedDate))
it gives me error that it does not contained aggregate function or the group by function
I actually want this
(dbo.CalcDatefromUTC(MBC.CreatedDate))
so that I can use all the values of this function rather than just MAX values of it
How can I change this code written above to achieve my objective??
Anyone??
You can't have dbo.CalcDatefromUTC(MBC.CreatedDate) in the SELECT list as neither you can have MBC.CreatedDate because it's not in the GROUP BY list.
You can have MAX(MBC.CreatedDate) though because it uses an aggregate function (MAX) on thta column. You can also have:
dbo.CalcDatefromUTC(MAX(MBC.CreatedDate)) as LRDate
which is the same actually (although maybe a bit faster), as:
MAX(dbo.CalcDatefromUTC(MBC.CreatedDate)) as LRDate
From your comments, I assume the above is not very helpful. Perhaps grouping by MBC.CreatedDate as well is what you want:
Select BCPP.*
from ViewPBCPP BCPP
inner join
(
Select MBC.PC PC
,MRT.Name CT
,dbo.CalcDatefromUTC(MBC.CreatedDate) as LRDate
from TableBACC MBC
inner join TableSC.RT MRT
ON MBC.RTid = MRT.id
where MBC.Isdeleted = 'False'
and MBC.PC <> 'NULL'
Group by MBC.PC
,MBC.CreatedDate --- added
,MRT.Name
) MBCR
ON BCPP.P_id = MBCR.PC
and BCPP.CreatedDate = MBCR.LRDate
and BCPP.CT = MBCR.CT