Group by "outer reference" error in SQL server - sql-server

I am using a simple group by statement in the code below:
SELECT 'bcg-measles1' AS vaccgroup,
e0.providerid,
( SELECT count(DISTINCT e1.baseentityid) AS count
FROM bcgmeasles1 e1
WHERE e1.measles1_ = 'Vaccinated' AND e0.providerid = e1.providerid) AS numerator,
( SELECT count(DISTINCT e2.baseentityid) AS count
FROM bcgmeasles1 e2
WHERE e2.measles1_ <> 'Not Eligible' AND e0.providerid = e2.providerid) AS denominator
FROM bcgmeasles1 e0
GROUP BY 'bcg-measles1' , e0.providerid
I am facing an error which says:
Each GROUP BY expression must contain at least one column that is not an outer reference.
I do not understand why this error is showing up, as I am only doing a simple group by. Please help!

The error message is
Each GROUP BY expression must contain at least one column that is not
an outer reference.
You have
GROUP BY 'bcg-measles1' , e0.providerid
This contains an expression 'bcg-measles1' that is just a constant string literal.
Remove that to resolve the issue (as noted by #Charlieface).
But in any event a simpler way of producing these results would be to use the below
SELECT 'bcg-measles1' AS vaccgroup,
providerid,
COUNT(DISTINCT CASE WHEN measles1_ = 'Vaccinated' THEN baseentityid END) AS numerator,
COUNT(DISTINCT CASE WHEN measles1_ <> 'Not Eligible' THEN baseentityid END) AS denominator
FROM bcgmeasles1
GROUP BY providerid

Related

"not contained in either an aggregate function or the GROUP BY clause" error

I am having problems figuring out how to solve the following error when trying to combine two queries:
Msg 8120, Level 16, State 1, Line 3
Column 'dbo.tbl_Person.perMailingLabel' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any help in understanding why this is happening and a solution would be greatly appreciated!
WITH AgeData AS
(
SELECT p.perMailingLabel,
p.perDOB,
DATEDIFF(YEAR, p.perDOB, GETDATE()) AS [AGE],
sr.strRegion AS 'State Region',
COUNT (*) AS 'State Region Total'
FROM dbo.fnAllEmails(1) AS e
INNER JOIN tbl_Subscription AS s
ON s.subsubscription_ID = e.MembershipId
INNER JOIN dbo.tbl_Person AS P
ON s.perPerson_ID = p.perPerson_ID
INNER JOIN dbo.tbl_Address AS a
ON s.perPerson_ID = a.perPerson_ID
INNER JOIN dbo.val_StateRegion AS sr
ON sr.strState_ID = a.strState_ID
WHERE s.subsubscription_ID
IN (000001,
000007,
000016,
000150,
000287,
000305,
000337,
000535,
000541,
000651
)
AND a.addPrimaryAdd = 1),
GroupAge AS
(
SELECT p.perMailingLabel,
p.perDOB,
Age,
CASE
WHEN AGE < 30 THEN 'Under 30'
WHEN AGE BETWEEN 31 AND 40 THEN '31 - 40'
WHEN AGE BETWEEN 41 AND 50 THEN '41 - 50'
WHEN AGE > 50 THEN 'Over 50'
ELSE 'Invalid Birthdate'
END AS [Age Groups]
FROM AgeData
)
SELECT COUNT(*) AS [AgeGrpCount],
[Age Groups]
FROM GroupAge
GROUP BY [Age Groups]
ORDER BY AgeGrpCount DESC;
As mentioned in a comment to your question, you're missing a group by clause to correspond with your count(*) in the AgeData CTE. If you still don't understand why this is required, just read up further onit.
If you do see the issue and it was a goof that you just couldn't see, then the deeper issue causing you to miss it may be your code organization. I made the following changes to your code that may help you catch things like this in the future:
I added a group by clause in the AgeData CTE with the relevant columns.
I moved your calculation of 'Age' into a cross apply statement of the first CTE. This allows elimination of the second CTE entirely. I then pointed your base query to the first CTE.
Case statements operate in sequential order, so the 'between' approach can be converted to a simple '<=' approach. This is very optional and you may find 'between' more readable. However, unless an age of 30 is an invalid birth date, you'll want to reform your logic to account for it. I did in my revision.
Your in statement has numbers with leading 0's in them. I imagine in this case the values are really stored as strings. So I converted your numbers to strings to make this clear.
You mix quoted identifiers with bracketed identifiers. I chose one for consistency. I also changed from value as fieldName to fieldName = value, but this is just preference. I just like to see the fieldNames first. But not all SQL dialects allow this.
I changed your inner join statements to simple join statements. Again, just a matter of preference but just demonstrating that it's the same thing.
Of course, the code is untested, so it may have something off here or there, but you get the idea. And it would be much easier to catch some of the issues identified with this or some other better organized structure. Here's the code:
with AgeData as (
select p.perMailingLabel,
p.perDOB,
ap.AGE,
[Age Groups] =
case
when age <= 30 then '30 or under'
when age <= 40 then '31 - 40'
when age <= 50 then '41 - 50'
when age > 50 then 'Over 50'
else 'Invalid Birthdate'
end,
[State Region] = sr.strRegion,
[State Region Total] = count(*)
from dbo.fnAllEmails(1) e
join tbl_Subscription s on s.subsubscription_ID = e.MembershipId
join tbl_Person p on s.perPerson_ID = p.perPerson_ID
join tbl_Address a on s.perPerson_ID = a.perPerson_ID
join val_StateRegion sr on sr.strState_ID = a.strState_ID
cross apply (select age = datediff(year, p.perDOB, getdate())) ap
where a.addPrimaryAdd = 1
and s.subsubscription_ID in (
'000001', '000007', '000016', '000150', '000287',
'000305', '000337', '000535', '000541', '000651'
)
group by p.perMailingLabel,
p.perDOB,
ap.AGE,
sr.strRegion
)
select AgeGrpCount = count(*),
[Age Groups]
from ageData
group by [Age Groups]
order by AgeGrpCount desc;

SQL Server : Subselect where IS NULL THEN 'VALUE'

I am trying to return a string result when a value IS NULL in SQL Server.
An example of what I am doing is below. This is a sub select query...
SELECT
stockitems.code AS StockCode,
stockitems.description AS
ShortDescription,
(SELECT
CASE remaining
WHEN NULL
THEN 'NO LEVEL'
ELSE remaining
END
FROM StockItemAlertLevels
WHERE StockItems.ID = StockItemAlertLevels.StockItemID) AS RemainingLevel
FROM
stockitems
WHERE
StockItems.Attribute1 = '1'
ORDER BY
StockItems.Code
No matter what I do, I cannot get the output result of the subselect to change if the result is NULL. Not all returned results from the SELECT statement will have returned results in the subselect - this is where I need the set the output to "NO LEVEL". The subselect will never return more than one result.
Hope this makes sense.
I think you want COALESCE():
SELECT si.code As StockCode, si.description AS ShortDescription,
(SELECT COALESCE(remaining, 'NO LEVEL')
FROM StockItemAlertLevels sal
WHERE si.ID = sal.StockItemID
) as RemainingLevel
FROM stockitems si
WHERE si.Attribute1 = '1'
ORDER BY si.Code;
I suspect, though, that your problem is not that remaining is NULL, but that there are no matches. You can move the COALESCE() outside the subquery, but I would propose a LEFT JOIN:
SELECT si.code As StockCode, si.description AS ShortDescription,
COALESCE(remaining, 'NO LEVEL') as RemainingLevel
FROM stockitems si LEFT JOIN
StockItemAlertLevels sal
ON si.ID = sal.StockItemID
WHERE si.Attribute1 = '1'
ORDER BY si.Code;

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.

GROUP BY error in SQL Server 2012

I'm getting this error
Column 'tbl_user.u_id' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
When I'm doing this query
SELECT *
FROM
tbl_user
JOIN
tbl_assign_role ON tbl_user.u_id = tbl_assign_role.tar_owner_id
WHERE
is_active = 1
AND u_id != 1
AND tar_is_deleted = 0
GROUP BY
tbl_assign_role.tar_owner_id
ORDER BY
tbl_user.u_updated_date DESC
Strongly agreed with #Dai
According to me, you should read first the role of "Group By". Here in your query there is no any aggregate function used, so there is no case here to use "Group By".
Do this, to continue removing duplicates while not needing the group by:
SELECT DISTINCT --[INSERT NEEDED COLUMNS HERE]
FROM
tbl_user
JOIN
tbl_assign_role ON tbl_user.u_id = tbl_assign_role.tar_owner_id
WHERE
is_active = 1
AND u_id != 1
AND tar_is_deleted = 0
ORDER BY
tbl_user.u_updated_date DESC

SELECT most recent date out of group

I have a T-SQL query that is designed to weed out duplicate entries of a certain product training, grabbing only the one with the most recent DateTaken. For example, if someone has taken a certain training course 3 times, we only want to display one row, that row being the one that contains the most recent DateTaken. Here is what I have so far, however I am receiving the following error:
An expression of non-boolean type specified in a context where a condition is expected, near 'ORDER'.
The ORDER BY is necessary since we want to group all the results of this query by the expiration date. Below is the full query:
SELECT DISTINCT
p.ProductDescription as ProductDesc,
c.CourseDescription as CourseDesc,
c.Partner, a.DateTaken, a.DateExpired, p.Status
FROM
sNumberToAgentId u, AgentProductTraining a, Course c, Product p
WHERE
#agentId = u.AgentId
and u.sNumber = a.sNumber
and a.CourseCode = c.CourseCode
and (a.DateExpired >= #date or a.DateExpired IS NULL)
and a.ProductCode = p.ProductCode
and (p.status != 'D' or p.status IS NULL)
GROUP BY
(p.ProductDescription)
HAVING
MIN(a.DateTaken)
ORDER BY
DateExpired ASC
EDIT
I've made the following changes to the GROUP BY and HAVING clauses, however I am still receiving errors:
GROUP BY
(p.ProductDescription, c.CourseDescription)
HAVING
MIN(a.DateTaken) > GETUTCDATE()
In SQL Management Studio, and red line error marker appears under the ',' after p.ProductDescription, the ')' after c.CourseDescription, the 'a' in a.DateTaken, and the closing parenthesis ')' of GETUTCDATE(). If I simply leave the GROUP BY statement to include only p.ProductDescription I get this error message:
Column 'Product.ProductDescription' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I'm relatively new to SQL, could someone explain what's going on? Thank you!
My suggestion since you are using sql server is to implement row_number() and partition by the ProductDescription and CourseDescription. This will go in a subquery and then you apply a filter to return only those where the row number is equal to one or the most recent record:
select *
from
(
SELECT p.ProductDescription as ProductDesc,
c.CourseDescription as CourseDesc,
c.Partner, a.DateTaken, a.DateExpired, p.Status
row_number() over(partition by p.ProductDescription, c.CourseDescription order by a.DateTaken desc) rn
FROM sNumberToAgentId u
INNER JOIN AgentProductTraining a
ON u.sNumber = a.sNumber
AND (a.DateExpired >= #date or a.DateExpired IS NULL)
INNER JOIN Course c
ON a.CourseCode = c.CourseCode
INNER JOIN Product p
ON a.ProductCode = p.ProductCode
AND (p.status != 'D' or p.status IS NULL)
WHERE u.AgentId = #agentId
) src
where rn = 1
order by DateExpired
Its this line
HAVING MIN(a.DateTaken)
Should be a boolean type such as
HAVING MIN(a.DateTaken) > GETUTCDATE()
Have to return True or a False (Boolean)
Here is the final query I wound up using. It is similar to the suggestions above:
SELECT ProductDesc, CourseDesc, Partner, DateTaken, DateExpired, Status
FROM(
SELECT
p.ProductDescription as ProductDesc,
c.CourseDescription as CourseDesc,
c.Partner, a.DateTaken, a.DateExpired, p.Status,
row_number() OVER (PARTITION BY p.ProductDescription, c.CourseDescription ORDER BY abs(datediff(dd, DateTaken, GETDATE()))) as Ranking
FROM
sNumberToAgentId u, AgentProductTraining a, Course c, Product p
WHERE
#agentId = u.AgentId
and u.sNumber = a.sNumber
and a.CourseCode = c.CourseCode
and (a.DateExpired >= #date or a.DateExpired IS NULL)
and a.ProductCode = p.ProductCode
and (p.status != 'D' or p.status IS NULL)
) aa
WHERE Ranking = '1'

Resources