Aggregate function not allowed in Set statment in TSQL - sql-server

I need to update one table with values from another table (msdb.dbo.sysjobhistory). Since I need to get the max values of the run_time and run_date, I kept getting 'aggregate function not allowed in set statement' error. As a workaround, I tried the following but something isn't right because ALL the values in every row are the same, indicating either an error in the join or something I can't figure out.This is what I have (which is not correct):
UPDATE inventory.dbo.books SET
auth_time = t1.at,
auth_date = t1.ad
FROM (SELECT MAX(run_time) AS at, MAX(run_date) AS ad
FROM msdb.dbo.sysjobhistory h
INNER JOIN inventory.dbo.books t
ON h.job_id = t.jobid) t1
ALSO, I need to be able to convert the run_time into decimal(10,2) format (as the auth_time field is that) and run_date into datetime (as auth_time is datetime format).

You're so close!
Just move your subquery reference from the From Clause to a subquery in the Set for each field.
Try this:
UPDATE inventory.dbo.books SET
auth_time = (SELECT MAX(run_time)
FROM msdb.dbo.sysjobhistory h
INNER JOIN inventory.dbo.books t
ON h.job_id = t.jobid)
, auth_date = (SELECT MAX(run_date)
FROM msdb.dbo.sysjobhistory h
INNER JOIN inventory.dbo.books t
ON h.job_id = t.jobid);
The subquery in the SET for each field essentially returns a single value when the subquery is executed. Therefore, the use of a subquery doesn't break any of the rules of the set operation.
If your logic starts getting too complicated and your set fields too numerous to want to repeat everything as a subquery for each field, then you can also use a CTE.
With CTE as (
Select MAX(run_time) as at
, MAX(run_date) as ad
FROM msdb.dbo.sysjobhistory h
INNER JOIN inventory.dbo.books t
ON h.job_id = t.jobid
)
Update #Temp
Set auth_time = CTE.at
, auth_date = CTE.ad
From CTE
Let me know if you have any questions!

You can join to a subquery.
And in that subquery you're allowed to use group by.
Then do the casts or converts when the destination fields are set.
For example:
UPDATE b
SET
auth_time = left(replace(cast(h.max_run_time as varchar),':',''),8),
auth_date = cast(h.max_run_date as datetime)
FROM inventory.dbo.books b
JOIN (
select
job_id,
max(run_time) as max_run_time,
max(run_date) as max_run_date
from msdb.dbo.sysjobhistory
group by job_id
) h
ON (b.job_id = h.job_id);
I didn't know what kind of number is expected in that auth_time.
So the max_run_time is just converted from a TIME to a VARCHAR.
The varchar is implicitly converted to the DECIMAL of the destination field.
For example: time '12:15:25.0000000' --> decimal(10,2) 121525.00

Related

Duplicate and incorrect output sql query

I need to select grade_name from tblgrade, subject_name from tblsubject, count (subscribe_id) from tblsubcription, count (sub_status) from tblsubcription where sub_status=1 and count (sub_status) from tblsubcription where sub_status is null.
This is what i have tried:
SELECT t2.grade_name,
t.subject_name,
(SELECT COUNT(*)
FROM tblsubcription
WHERE sub_status IS NULL
AND teacher_id = 2) AS pending,
(SELECT COUNT(*)
FROM tblsubcription
WHERE sub_status = '1'
AND teacher_id = 2) AS appoved,
COUNT(t1.subscribe_id) AS totalsub
FROM tblsubject t
INNER JOIN tblsubject_grade tg ON (t.subject_id = tg.subject_id)
INNER JOIN tblsubcription t1 ON (tg.subject_garde_id = t1.subject_garde_id)
INNER JOIN tblgrade t2 ON (tg.grade_id = t2.grade_id)
AND tg.grade_id = t2.grade_id
AND tg.subject_id = t.subject_id
AND t2.admin_id = t.admin_id
WHERE t1.teacher_id = 2
GROUP BY t.subject_name,
t2.grade_name;
See result obtained when the above query is executed and the expected result i need is in red
Looking at this subquery:
(SELECT COUNT(*)
FROM tblsubcription
WHERE sub_status IS NULL
AND teacher_id = 2) AS pending,
There is nothing here to relate (correlate) it to the specific row. You need an additional condition in the WHERE clause that tells you which Grade/Subject pair to look at. The other (approved) subquery is the same way.
Alternatively, you may be able to solve this with another join to tblsubscription and conditional aggregation.
I'd post code to fix this, but I find the images too blurry to read well, so I can't easily infer which fields to use. Next time post formatted text, and you'll get a better answer in less time.

SQL combine two queries result into one dataset

I am trying to combine two SQL queries the first is
SELECT
EAC.Person.FirstName,
EAC.Person.Id,
EAC.Person.LastName,
EAC.Person.EmployeeId,
EAC.Person.IsDeleted,
Controller.Cards.SiteCode,
Controller.Cards.CardCode,
Controller.Cards.ActivationDate,
Controller.Cards.ExpirationDate,
Controller.Cards.Status,
EAC.[Group].Name
FROM
EAC.Person
INNER JOIN
Controller.Cards ON EAC.Person.Id = Controller.Cards.PersonId
INNER JOIN
EAC.GroupPersonMap ON EAC.Person.Id = EAC.GroupPersonMap.PersonId
INNER JOIN
EAC.[Group] ON EAC.GroupPersonMap.GroupId = EAC.[Group].Id
And the second one is
SELECT
IsActive, ActivationDateUTC, ExpirationDateUTC,
Sitecode + '-' + Cardcode AS Credential, 'Badge' AS Type,
CASE
WHEN isActive = 0
THEN 'InActive'
WHEN ActivationDateUTC > GetUTCDate()
THEN 'Pending'
WHEN ExpirationDAteUTC < GetUTCDate()
THEN 'Expired'
ELSE 'Active'
END AS Status
FROM
EAC.Credential
JOIN
EAC.WiegandCredential ON Credential.ID = WiegandCredential.CredentialId
WHERE
PersonID = '32'
Where I would like to run the second query for each user of the first query using EAC.Person.Id instead of the '32'.
I would like all the data to be returned in one Dataset so I can use it in Report Builder.
I have been fighting with this all day and am hoping one of you smart guys can give me a hand. Thanks in advance.
Based on your description in the comments, I understand that the connection between the two datasets is actually the PersonID field, which exists in both EAC.Credential and EAC.Person; however, in EAC.Credential, duplicate values exist for PersonID, and you want only the most recent one for each PersonID.
There are a few ways to do this, and it will depend on the number of rows returned, the indexes, etc., but I think maybe you're looking for something like this...?
SELECT
EAC.Person.FirstName
,EAC.Person.Id
,EAC.Person.LastName
,EAC.Person.EmployeeId
,EAC.Person.IsDeleted
,Controller.Cards.SiteCode
,Controller.Cards.CardCode
,Controller.Cards.ActivationDate
,Controller.Cards.ExpirationDate
,Controller.Cards.Status
,EAC.[Group].Name
,X.IsActive
,X.ActivationDateUTC
,X.ExpirationDateUTC
,X.Credential
,X.Type
,X.Status
FROM EAC.Person
INNER JOIN Controller.Cards
ON EAC.Person.Id = Controller.Cards.PersonId
INNER JOIN EAC.GroupPersonMap
ON EAC.Person.Id = EAC.GroupPersonMap.PersonId
INNER JOIN EAC.[Group]
ON EAC.GroupPersonMap.GroupId = EAC.[Group].Id
CROSS APPLY
(
SELECT TOP 1
IsActive
,ActivationDateUTC
,ExpirationDateUTC
,Sitecode + '-' + Cardcode AS Credential
,'Badge' AS Type
,'Status' =
CASE
WHEN isActive = 0
THEN 'InActive'
WHEN ActivationDateUTC > GETUTCDATE()
THEN 'Pending'
WHEN ExpirationDateUTC < GETUTCDATE()
THEN 'Expired'
ELSE 'Active'
END
FROM EAC.Credential
INNER JOIN EAC.WiegandCredential
ON EAC.Credential.ID = EAC.WiegandCredential.CredentialId
WHERE EAC.Credential.PersonID = EAC.Person.PersonID
ORDER BY EAC.Credential.ID DESC
) AS X
-- Optionally, you can also add conditions to return specific rows, i.e.:
-- WHERE EAC.Person.PersonID = 32
This option uses a CROSS APPLY, which means that every row of the first dataset will return additional values from the second dataset, based on the criteria that you described. In this CROSS APPLY, I'm joining the two datasets based on the fact that PersonID exists in both EAC.Person (in your first dataset) as well as in EAC.Credential. I then specify that I want only the TOP 1 row for each PersonID, with an ORDER BY specifying that we want the most recent (highest) value of ID for each PersonID.
The CROSS APPLY is aliased as "X", so in your original SELECT you now have several values prefixed with the X. alias, which just means that you're taking these fields from the second query and attaching them to your original results.
CROSS APPLY requires that a matching entry exists in both subsets of data, much like an INNER JOIN, so you'll want to check and make sure that the relevant values exist and are returned correctly.
I think this is pretty close to the direction you're trying to go. If not, let me know and I'll update the answer. Good luck!
Try like this;
select Query1.*, Query2.* from (
SELECT
EAC.Person.FirstName,
EAC.Person.Id as PersonId,
EAC.Person.LastName,
EAC.Person.EmployeeId,
EAC.Person.IsDeleted,
Controller.Cards.SiteCode,
Controller.Cards.CardCode,
Controller.Cards.ActivationDate,
Controller.Cards.ExpirationDate,
Controller.Cards.Status,
EAC.[Group].Name
FROM
EAC.Person
INNER JOIN
Controller.Cards ON EAC.Person.Id = Controller.Cards.PersonId
INNER JOIN
EAC.GroupPersonMap ON EAC.Person.Id = EAC.GroupPersonMap.PersonId
INNER JOIN
EAC.[Group] ON EAC.GroupPersonMap.GroupId = EAC.[Group].Id)
Query1 inner join (SELECT top 100
IsActive, ActivationDateUTC, ExpirationDateUTC,
Sitecode + '-' + Cardcode AS Credential, 'Badge' AS Type,
CASE
WHEN isActive = 0
THEN 'InActive'
WHEN ActivationDateUTC > GetUTCDate()
THEN 'Pending'
WHEN ExpirationDAteUTC < GetUTCDate()
THEN 'Expired'
ELSE 'Active'
END AS Status
FROM
EAC.Credential
JOIN
EAC.WiegandCredential ON Credential.ID = WiegandCredential.CredentialId
ORDER BY EAC.Credential.ID DESC) Query2 ON Query1.PersonId = Query2.PersonID
Just select two queries to join them like Query1 and Query2 by equaling PersonId data.

Conversion failed when converting the varchar value 'RN' to data type int

Im getting a conversion error when using the rownumber function on sql-server. I know plenty of similar threads have been posted but ive looked at them and they have different issues.
Conversion failed when converting the varchar value 'RN' to data type int.
It doesnt tell me where the problem is occuring. Does anyone have an idea of where it could be going wrong apart from the rownumber function?
This CTE is getting a previous transaction
, PRETRANSACTIONS AS (
SELECT DISTINCT
CONT.POH_ID AS POH_ID
,CONT.POH_POLICYNUMBER AS Contract_Number
,row_number() over (
partition by CONT.POH_ID
order by TRANS.txh_effectivedate desc
) AS 'RN'
,TXN_STAT.Txs_DESCRIPTION_I AS Transaction_Status_2
,TRANS.txh_effectivedate AS Transaction_Date_2
,TXN_DES.Txt_DESCRIPTION_I AS Transaction_Type_2
,TRX_RES.TRE_AMTPROCESSED AS Transaction_Amount_2
From Se2FAST.dbo.Cm_Opt_Poh_PolicyHdr_S CONT
JOIN BASE BASE
ON BASE.Poh_ID = CONT.POH_ID
INNER JOIN Se2FAST.dbo.Cm_Opt_Pch_PolicyCovHdr_S policyCov
ON CONT.Poh_ID = policyCov.Pch_POLICYHDRID
AND policycov.pch_sequence = 1
INNER JOIN [dbo].[Cm_Sys_Pst_PolicyStatus_I] PST
ON PST.Pst_ID_I = CONT.Poh_Status
LEFT JOIN se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRANS
ON TRANS.TXH_POLICYHDRID = CONT.POH_ID
AND TRANS.txh_effectivedate < ( SELECT TOP 1 TRAN1.txh_effectivedate
FROM se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRAN1
WHERE TRAN1.TXH_POLICYHDRID = CONT.POH_ID
ORDER BY txh_effectivedate DESC)
LEFT JOIN se2Fast.dbo.Cm_Opt_Tre_TrxRes_S AS TRX_RES
ON TRANS.Txh_ID = TRX_RES.Tre_TRXHDRID
LEFT JOIN se2Fast.dbo.Cm_Sys_Txt_TrxType_I TXN_DES
ON TRANS.Txh_TRXTYPEID = TXN_DES.Txt_ID_I
--AND TXN_DES.Txt_DESCRIPTION_I NOT IN (
--'Anniversary','Monthiversary','Quarterversary','Calendar Year End')
--AND TXN_DES.Txt_DESCRIPTION_I IS NOT NULL
LEFT JOIN se2Fast.dbo.Cm_Sys_Txs_TrxStatus_I TXN_STAT
ON Trans.Txh_TRXSTATUS = TXN_STAT.Txs_ID_I
AND TXN_STAT.Txs_DESCRIPTION_I = 'Completed'
WHERE 'RN' = 2
)
WHERE 'RN' = 2 is comparing two values, the string 'RN' and the integer 2. When SQL Server parses the expression, it is attempting to convert the literal value 'RN' to a numeric value to perform the integer comparison. Obviously, RN cannot be converted to a numeric value so you are getting an error.
In SQL Server, aliases are materialized after the WHERE/GROUP/HAVING clauses, but before the ORDER BY clause. This means you can't use the alias within the filter of the query it was assigned. You need to generate your column alias in a subquery before you can use it in a comparison.
It looks like you've mixed up your query. You've started as CTE named 'PRETRANSACTIONS', but then you've tried to alias it as a column after the closing parenthesis, AS 'RN'. A CTE must be referenced in the FROM before you can use the columns in the SELECT. If this is just a column expression, get rid of 'PRETRANSACTIONS AS' at the beginning.
WITH PRETRANSACTIONS AS (
SELECT DISTINCT
CONT.POH_ID AS POH_ID
,CONT.POH_POLICYNUMBER AS Contract_Number
,row_number() over (
partition by CONT.POH_ID
order by TRANS.txh_effectivedate desc)
AS RN)
SELECT RN,
TXN_STAT.Txs_DESCRIPTION_I AS Transaction_Status_2
,TRANS.txh_effectivedate AS Transaction_Date_2
,TXN_DES.Txt_DESCRIPTION_I AS Transaction_Type_2
,TRX_RES.TRE_AMTPROCESSED AS Transaction_Amount_2
From Se2FAST.dbo.Cm_Opt_Poh_PolicyHdr_S CONT
JOIN BASE BASE
ON BASE.Poh_ID = CONT.POH_ID
JOIN PRETRANSACTIONS
ON BASE.POH_ID = PRETRANSACTIONS.POH_ID
INNER JOIN Se2FAST.dbo.Cm_Opt_Pch_PolicyCovHdr_S policyCov
ON CONT.Poh_ID = policyCov.Pch_POLICYHDRID
AND policycov.pch_sequence = 1
INNER JOIN [dbo].[Cm_Sys_Pst_PolicyStatus_I] PST
ON PST.Pst_ID_I = CONT.Poh_Status
LEFT JOIN se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRANS
ON TRANS.TXH_POLICYHDRID = CONT.POH_ID
AND TRANS.txh_effectivedate < ( SELECT TOP 1 TRAN1.txh_effectivedate
FROM se2Fast.DBO.CM_OPT_TXH_TRXHDR_S TRAN1
WHERE TRAN1.TXH_POLICYHDRID = CONT.POH_ID
ORDER BY txh_effectivedate DESC)
LEFT JOIN se2Fast.dbo.Cm_Opt_Tre_TrxRes_S AS TRX_RES
ON TRANS.Txh_ID = TRX_RES.Tre_TRXHDRID
LEFT JOIN se2Fast.dbo.Cm_Sys_Txt_TrxType_I TXN_DES
ON TRANS.Txh_TRXTYPEID = TXN_DES.Txt_ID_I
--AND TXN_DES.Txt_DESCRIPTION_I NOT IN (
--'Anniversary','Monthiversary','Quarterversary','Calendar Year End')
--AND TXN_DES.Txt_DESCRIPTION_I IS NOT NULL
LEFT JOIN se2Fast.dbo.Cm_Sys_Txs_TrxStatus_I TXN_STAT
ON Trans.Txh_TRXSTATUS = TXN_STAT.Txs_ID_I
AND TXN_STAT.Txs_DESCRIPTION_I = 'Completed'
WHERE PRETRANSACTIONS.RN = 2
On a separate note, never use DISTINCT in the same set as ROW_NUMBER. ROW_NUMBER generates a unique number for every row, therefore every row is distinct. All that will do is add sorting overhead without eliminating any rows. You can use RANK or DENSE_RANK instead.
dont use 'RN', just RN without quotes.
You are getting this error because you are comparing the Text value 'RN' with an Integer value 2 in the where clause. But since 'RN' is a column and you want to return all the files that have a value 2 in the field 'RN' instead of
WHERE **'RN'** = 2
just type
WHERE RN = 2
RN without quotes as it is the name of the column.

TSQL Select Statement using Case or Join

I am a little stuck on a situation that I have been trying to fight through. I have a page that allows a user to select all the filter options they want to search by and then it runs the query on that data.
Every field requires something to be picked but on a new field I am introducing, it's going to be optional.
It allows you to provide a list of supervisors and it will then provide all records where the agents supervisor is in the list provided; pretty straight forward. However, I am trying to make this optional as I don't want to always search by users. If I don't provide a name in the UI to pass to the stored procedure, then I want to ignore this part of the statement and get me everything regardless of the manager.
Here is the query I am working with:
SELECT a.[escID],
a.[escReasonID],
b.[ArchibusLocationName],
c.[ArchibusLocationName],
b.[DepartmentDesc],
c.[DepartmentDesc],
a.[escCreatedBy],
a.[escWorkedBy],
a.[escNotes],
a.[preventable],
a.[escalationCreated],
a.[escalationTracked],
a.[feedbackID],
typ.[EscalationType],
typ.[EscalationTypeText] AS escalationType,
d.reasonText AS reasonText
FROM [red].[dbo].[TFS_Escalations] AS a
LEFT OUTER JOIN
red.dbo.EmployeeTable AS b
ON a.escCreatedBy = b.QID
LEFT OUTER JOIN
red.dbo.EmployeeTable AS c
ON a.escWorkedBy = c.QID
LEFT OUTER JOIN
red.dbo.TFS_Escalation_Reasons AS d
ON a.escReasonID = d.ReasonID
INNER JOIN
dbo.TFS_EscalationTypes AS typ
ON d.escType = typ.EscalationType
WHERE B.[ArchibusLocationName] IN (SELECT location
FROM #tmLocations)
AND C.[ArchibusLocationName] IN (SELECT location
FROM #subLocations)
AND B.[DepartmentDesc] IN (SELECT department
FROM #tmDepartments)
AND C.[DepartmentDesc] IN (SELECT department
FROM #subDepartments)
AND DATEDIFF(second, '19700101', CAST (CONVERT (DATETIME, A.[escalationCreated], 121) AS INT)) >= #startDate
AND DATEDIFF(second, '19700101', CAST (CONVERT (DATETIME, A.[escalationCreated], 121) AS INT)) <= #endDate
AND a.[PREVENTABLE] IN (SELECT PREVENTABLE FROM #preventable)
AND b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders)
The part that I am trying to make option is the very last line of the query:
AND b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders)
Essentially, if there is no data in the temp table #sourceLeaders then it should ignore that piece of the query.
In all of the other instances of the WHERE clause, something is always required for those fields which is why that all works fine. I just cant figure out the best way to make this piece optional depending on if the temp table has data in it (the temp table is populated by the names entered in the UI that a user COULD search by).
So this line should be TRUE if something matches data in the table variable OR there is nothing in the table variable
AND
(
b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders)
OR
NOT EXISTS (SELECT 1 FROM #sourceLeaders)
)
Similar to Nick.McDermaid's, but uses a case statement instead :
AND
(
1 = CASE WHEN NOT EXISTS(SELECT 1 FROM #sourceLeaders) THEN 1
WHEN b.MgrQID IN (SELECT leaderQID FROM #sourceLeaders) THEN 1
ELSE 0
END
)
Maybe at the top so you have a single check
DECLARE #EmptySourceLeaders CHAR(1)
IF EXISTS (SELECT 1 FROM #sourceLeaders)
SET #EmptySourceLeaders = 'N'
ELSE
SET #EmptySourceLeaders = 'Y'
Then in the joins
LEFT OUTER JOIN #SourceLeaders SL
ON b.MgrQID = SL.leaderQID
Then in the WHERE
AND (#EmptySourceLeaders = 'Y' OR SL.leaderQID IS NOT NULL)
lots of ways to do it.

SQL query distict count using inner join

Need help ensuring the below query doesn't return inaccurate results.
select #billed = count(a.[counter]) from [dbo].cxitems a with (nolock)
inner join [dbo].cxitemhist b with (nolock) on a.[counter] = b.cxlink
where b.[eventtype] in ('BILLED','REBILLED')
and b.[datetime] between #begdate and #enddate
The query is "mostly" accurate as is, however there is a slight possibility that cxitemhist table could contain more than 1 "billed" record for given date range. I only need to count item as "Billed" once during given date range.
You can join on a sub query the limits you to one row for each combination of fields used for the join:
select #billed = count(a.[counter])
from [dbo].cxitems a
inner join (
select distinct cxlink
from [dbo].cxitemhist
where [eventtype] in ('BILLED','REBILLED')
and [datetime] between #begdate and #enddate
) b on a.[counter] = b.cxlink
You can also use the APPLY operator instead of a join here, but you'll have to check against your data to see which gives better performance.
If you only need to count records from the cxitems table, that have any corresponding records from the cxitemhist table, you can use the exists clause with a subquery.
select #billed = count(a.[counter]) from [dbo].cxitems a
where exists(select * from [dbo].cxitemhist b
where a.[counter] = b.cxlink
and b.[eventtype] in ('BILLED','REBILLED')
and b.[datetime] between #begdate and #enddate)
Cannot really say how this will affect performance, without specific data, though, but it should be comparably fast with your code.

Resources