SQL - Change NULL value to 0 - sql

I have this pivot table that gives me null value on SUM(contar) and I want to change them to 0
SELECT Description AS Categoria,
[ACS],
[URO]
FROM
(SELECT GroupType.Description,
Speciality.Shortname,
SUM(1) AS contar
FROM DoctorEnterpriseDetails
INNER JOIN Speciality ON DoctorEnterpriseDetails.Speciality1 = Speciality.SpecialityId
INNER JOIN GroupType ON DoctorEnterpriseDetails.GroupId = GroupType.GroupId
WHERE (DoctorEnterpriseDetails.EnterpriseId = 48)
GROUP BY GroupType.Description,
Speciality.Shortname) AS ps PIVOT (SUM(contar)
FOR Shortname IN ([ACS],[URO])) pvt
ORDER BY description
I tried make this way but it gives me Null values again
SELECT Description AS Categoria,
[ACS],
[URO]
FROM
(SELECT GroupType.Description,
Speciality.Shortname,
GroupType.GroupId,
(CASE WHEN (SUM(1) IS NULL) THEN 0 ELSE SUM(1) END) AS contar
FROM DoctorEnterpriseDetails
INNER JOIN Speciality ON DoctorEnterpriseDetails.Speciality1 = Speciality.SpecialityId
INNER JOIN GroupType ON DoctorEnterpriseDetails.GroupId = GroupType.GroupId
WHERE (DoctorEnterpriseDetails.EnterpriseId = 48)
GROUP BY GroupType.Description,
Speciality.Shortname,
DoctorEnterpriseDetails.GroupId,
GroupType.GroupId) AS ps PIVOT (SUM(contar)
FOR Shortname IN ([ACS],[URO])) pvt
ORDER BY GroupId;
I don't understand what is wrong. Thank you

You define aliases within the CASE, which is not allowed. Additionally you mix numeric and string:
It's either
CASE WHEN SUM(1) IS NULL THEN 0 ELSE SUM(1) END AS contador
or
COALESCE(SUM(1), 0) END AS contador
Btw, SUM(1) is the same as COUNT(*)

Related

Use CTE in SQL to flag DUPLICATES and reference in sub-query

So I have the following CTE:
with dupeinv AS (
select * from (
select
tci.t_idoc,
tci.t_idat,
ROW_NUMBER() OVER (partition by tci.t_idoc ORDER BY tci.t_idoc, tci.t_idat DESC) as rn
from [ln106].[dbo].tcisli305100 tci
) as t
where t.rn = 1
)
There are duplicates in the above table ([ln106].[dbo].tcisli305100) , hence the CTE to get single values. I want to format just these values in the below query (prefixed with ---)
select 'JCI' as BU,
RTRIM(LTRIM(cl.t_orno)) AS SALES_ORDER_NUMBER
, cl.t_pono AS SALES_ORDER_LINE_NUMBER
, CONCAT(cl.t_shpm, cl.t_pono, cl.t_idoc) AS SHIPPING_RECORD_ID
,CASE WHEN cl.t_dqua = 0 or cl.t_dqua is null THEN cl.t_amti ELSE
cl.t_amti / cl.t_dqua END AS AR_INVOICE_LINE_ITEM_PRICE_LOCAL
, cl.t_line AS AR_INVOICE_LINE_NUMBER
, cl.t_dqua AS AR_INVOICE_LINE_ITEM_QUANTITY
--- , concat(dupeinv.t_idoc,'|',format(dupeinv.t_idat,'MMddyyyy') ---
,ci.t_ccur AS AR_INVOICE_CURRENCY
, ci.t_idat AS AR_INVOICE_DATE
FROM [ln106].[dbo].tcisli310100 cl
LEFT JOIN [ln106].[dbo].tcisli305100 ci ON cl.t_idoc = ci.t_idoc
LEFT JOIN t di on cl.t_doc = di_t_doc
LEFT JOIN (SELECT t_orno,t_pono FROM [ln106].[dbo].ttdsls401100 WHERE t_oltp <> 1 group by t_orno,t_pono) as l --Jed 10162020 Changed the join to prevent duplicate records
ON l.t_orno=cl.t_orno COLLATE SQL_Latin1_General_CP1_CI_AS AND l.t_pono=cl.t_pono
LEFT JOIN dupeinv tci on cl.r_idoc = ci.t_doc
WHERE ci.t_idat > '2017'
Query doesn't like me referencing it in the main query. Can anyone help, or suggest a better idea?
Your final query should look something like this:
WITH dupeinv AS
(SELECT *
FROM
(SELECT tci.t_idoc,
tci.t_idat,
ROW_NUMBER() OVER (PARTITION BY tci.t_idoc
ORDER BY tci.t_idoc,
tci.t_idat DESC) AS rn
FROM [ln106].[dbo].tcisli305100 tci) AS t
WHERE t.rn = 1 )
SELECT 'JCI' AS BU,
RTRIM(LTRIM(cl.t_orno)) AS SALES_ORDER_NUMBER ,
cl.t_pono AS SALES_ORDER_LINE_NUMBER ,
CONCAT(cl.t_shpm, cl.t_pono, cl.t_idoc) AS SHIPPING_RECORD_ID ,
CASE
WHEN cl.t_dqua = 0
OR cl.t_dqua IS NULL THEN cl.t_amti
ELSE cl.t_amti / cl.t_dqua
END AS AR_INVOICE_LINE_ITEM_PRICE_LOCAL ,
cl.t_line AS AR_INVOICE_LINE_NUMBER ,
cl.t_dqua AS AR_INVOICE_LINE_ITEM_QUANTITY ,
concat(dupeinv.t_idoc,
'|',
format(dupeinv.t_idat, 'MMddyyyy')) ,
ci.t_ccur AS AR_INVOICE_CURRENCY ,
ci.t_idat AS AR_INVOICE_DATE
FROM [ln106].[dbo].tcisli310100 cl
LEFT JOIN [ln106].[dbo].tcisli305100 ci ON cl.t_idoc = ci.t_idoc
LEFT JOIN t di ON cl.t_doc = di_t_doc
LEFT JOIN
(SELECT t_orno,
t_pono
FROM [ln106].[dbo].ttdsls401100
WHERE t_oltp <> 1
GROUP BY t_orno,
t_pono) AS l --Jed 10162020 Changed the join to prevent duplicate records
ON l.t_orno=cl.t_orno COLLATE SQL_Latin1_General_CP1_CI_AS
AND l.t_pono=cl.t_pono
LEFT JOIN dupeinv tci ON cl.r_idoc = ci.t_doc
WHERE ci.t_idat > '2017'

Query for count and distinct

I should make a report in T-SQL from several table.
I can join all the table needed but after I don't know excatly how to get my information.
Explanation :
I've got the following table :
Tbl_User (UserId, Username)
Tbl_Customer (CustomeriD, CustomerName)
Tbl_DocA (DocId, CustomerID, DateCreate, DateAdd, UseriD)
Tbl_DocB (DocId, CustomerID, DateCreate, DateAdd, UseriD)
Tbl_DocC (DocId, CustomerID, DateCreate, DateAdd, UseriD)
I am trying to get a report like this :
After I can get this, the idea is to have a filter with the date in SQL reporting.
You can union all the document tables together and join users and customers on it.
SELECT Customer.CustomerID
,Customer.CustomerName
,COUNT(CASE WHEN DocType = 'A' THEN 1 END) AS doc_a_total
,COUNT(CASE WHEN DocType = 'B' THEN 1 END) AS doc_b_total
,COUNT(CASE WHEN DocType = 'C' THEN 1 END) AS doc_c_total
,COUNT(CASE WHEN DocType = 'A' AND user.username ='azerty' THEN 1 END) AS doc_a_made_by_azerty
,COUNT(CASE WHEN DocType = 'B' AND user.username ='azerty' THEN 1 END) AS doc_b_made_by_azerty
,COUNT(CASE WHEN DocType = 'C' AND user.username ='azerty' THEN 1 END) AS doc_c_made_by_azerty
FROM (
(SELECT 'A' AS DocType, * FROM Tbl_DocA)
UNION ALL
(SELECT 'B' AS DocType, * FROM Tbl_DocB)
UNION ALL
(SELECT 'C' AS DocType, * FROM Tbl_DocC)
) AS docs
JOIN Tbl_User AS user ON user.UserId = docs.UseriD
JOIN Tbl_Customer AS Customer ON Customer.CustomeriD = docs.CustomeriD
GROUP BY Customer.CustomerID , Customer.CustomerName
You can use common table expressions to get the count for each report type per customer, with conditional aggregation for reports made by a specific user, and join them to the customers table.
Something like this should get you the desired results:
DECLARE #UserId int = 1; -- or whatever the id of the user you need
WITH CTEDocA AS
(
SELECT CustomerID
, COUNT(DocId) As NumberOfReports
, COUNT(CASE WHEN UserId = #UserId THEN 1 END) As NumberOfReportsByUserAzerty
FROM Tbl_DocA
GROUP BY CustomerID
), CTEDocB AS
(
SELECT CustomerID
, COUNT(DocId) As NumberOfReports
, COUNT(CASE WHEN UserId = #UserId THEN 1 END) As NumberOfReportsByUserAzerty
FROM Tbl_DocB
GROUP BY CustomerID
), CTEDocC AS
(
SELECT CustomerID
, COUNT(DocId) As NumberOfReports
, COUNT(CASE WHEN UserId = #UserId THEN 1 END) As NumberOfReportsByUserAzerty
FROM Tbl_DocC
GROUP BY CustomerID
)
SELECT cust.CustomeriD
,cust.CustomerName
,ISNULL(a.NumberOfReports, 0) As NumberOfDocA
,ISNULL(a.NumberOfReportsByUserAzerty, 0) As NumberOfDocAByAzerty
,ISNULL(b.NumberOfReports, 0) As NumberOfDocB
,ISNULL(b.NumberOfReportsByUserAzerty, 0) As NumberOfDocBByAzerty
,ISNULL(c.NumberOfReports, 0) As NumberOfDocC
,ISNULL(c.NumberOfReportsByUserAzerty, 0) As NumberOfDocCByAzerty
FROM Tbl_Customer cust
LEFT JOIN CTEDocA As a
ON cust.CustomeriD = a.CustomerID
LEFT JOIN CTEDocA As b
ON cust.CustomeriD = b.CustomerID
LEFT JOIN CTEDocA As c
ON cust.CustomeriD = c.CustomerID
To filter by date you can add a where clause to each common table expresstion.
BTW, The fact that you have three identical tables for three document types suggest a bad database design.
If these tables are identical you should consider replacing them with a single table and add a column to that table describing the document type.
There are several ways to do this. One key feature needed is to count a particular user apart from the others. This is done with conditional aggregation. E.g.:
select
customerid,
count(*),
count(case when userid = <particular user ID here> then 1 end)
from tbl_doca
group by customerid;
Here is one possible query using a cross join to get the user in question once and cross apply to get the numbers.
select
c.customerid,
c.customername,
doca.total as doc_a_total,
doca.az as doc_a_by_azerty,
docb.total as doc_b_total,
docb.az as doc_b_by_azerty,
docc.total as doc_c_total,
docc.az as doc_c_by_azerty
from tbl_customer c
cross join
(
select userid from tbl_user where username = 'Azerty'
) azerty
cross apply
(
select
count(*) as total,
count(case when da.userid = azerty.userid then 1 end)n as az
from tbl_doca da
where da.customerid = c.customerid
) doca
cross apply
(
select
count(*) as total,
count(case when db.userid = azerty.userid then 1 end)n as az
from tbl_docb db
where db.customerid = c.customerid
) docb
cross apply
(
select
count(*) as total,
count(case when dc.userid = azerty.userid then 1 end)n as az
from tbl_docc dc
where dc.customerid = c.customerid
) docc
order by c.customerid;
Other options would be to replace the cross apply with left outer join and non-correlated subqueries or to put subqueries into the select clause.
Combining the totals for the documents is another method.
Then use conditional aggregation for the counts.
untested notepad scribble:
;WITH SPECIFICUSER AS
(
SELECT UseriD
FROM Tbl_User
WHERE UserName = 'azerty'
),
DOCTOTALS (
SELECT CustomeriD, UseriD, 'DocA' AS Src, COUNT(DocId) AS Total
FROM Tbl_DocA
GROUP BY CustomeriD, UseriD
UNION ALL
SELECT CustomeriD, UseriD, 'DocB', COUNT(DocId)
FROM Tbl_DocB
GROUP BY CustomeriD, UseriD
UNION ALL
SELECT CustomeriD, UseriD, 'DocC', COUNT(DocId)
FROM Tbl_DocC
GROUP BY CustomeriD, UseriD
)
SELECT
docs.CustomeriD,
cust.CustomerName,
SUM(CASE WHEN usrX.UseriD is not null AND docs.Src = 'DocA' THEN docs.Total ELSE 0 END) AS Total_DocA_userX,
SUM(CASE WHEN Src = 'DocA' THEN docs.Total ELSE 0 END) AS Total_DocA,
SUM(CASE WHEN usrX.UseriD is not null AND docs.Src = 'DocB' THEN docs.Total ELSE 0 END) AS Total_DocB_userX,
SUM(CASE WHEN Src = 'DocB' THEN docs.Total ELSE 0 END) AS Total_DocB,
SUM(CASE WHEN usrX.UseriD is not null AND docs.Src = 'DocC' THEN docs.Total ELSE 0 END) AS Total_DocC_userX,
SUM(CASE WHEN Src = 'DocC' THEN docs.Total ELSE 0 END) AS Total_DocC
FROM DOCTOTALS docs
LEFT JOIN Tbl_Customer cust ON cust.CustomeriD = docs.CustomeriD
LEFT JOIN Tbl_User usr ON usr.UseriD = docs.UseriD
LEFT JOIN SPECIFICUSER usrX ON usrX.UseriD = docs.UseriD
GROUP BY docs.CustomeriD, cust.CustomerName
ORDER BY docs.CustomeriD
Those long column names could be set on the report side

Subquery returned more than 1 value. in SQL Server 2005

When I query data in my application, I get the following error
Subquery returned more than 1 value.This is not permitted when the subquery follows =,!=,<,<=,>,>= or when the subquery is use as an expression.
The statement has been terminated.
Below is my stored procedure. anyone help me please?
ALTER proc [dbo].[SP_RC_DS_2]
#CustomerID varchar(10),
#ExfacDateFrom Datetime,
#ExfacDateTo DateTime
SELECT
SOD_SOLineNbr as Line_No
,IMA_LeadTimeCode as Manu_Pur
,IMA_Classification as Category
,case when IMA_ItemName is null then SOI_MiscLineDescription else IMA_ItemName end as ItemName
--,SOI_MiscLineDescription as MiscLine
,IAD_AliasName as AliasName
--,SOD_UserDef1 as PrivateRemark
,PrivateRemark =
(Case CUS_CustomerID
When 'C1175' Then isnull(convert(nvarchar(200),SOM_SpecialInst),'') + ' / ' + isnull(SOD_UserDef1,'')
When 'C1209' Then isnull(convert(nvarchar(200),SOM_SpecialInst),'') + ' / ' + isnull(SOD_UserDef1,'')
Else SOD_UserDef1
End)
,SOD_UserDef2 as RemarkLine--
,CUS_CustomerID as CustomerID
,CSA_Name as CustomerName
,SOM_Destination as Destination
,SOM_ContainerSize as ContainerSize
,CSA_RecordID as CSA_RecordID
,SOM_SalesOrderID as SalesOrderID
,SOM_CustomerPOID as CustomerPO
,SOD_RequiredDate as ExFacDate
,SOM_DefaultDockDate as ETD
,SOM_ETA as ETA
,SOD_PromiseDate as PromiseDate
,SOD_RequiredQty as Qty
,SOI_SalesConvUnitMeasure as UnitMeasure
,SOD_UnitPrice as UnitPrice
,isnull(IMA_Weight,0) as NetWeight
,isnull(IMA_GrossWeight,0)as GrossWeight
,(Case When IMA_ProdFam is null then 'Others' else IMA_ProdFam end) as ProdFamilyItem
,(Case When IMA_ProdFam is null then 'Others' else (Select PFM_PGI From PFM Where PFM_ProdFam = IMA_ProdFam) end) as ProdFamInvGroup
,UnitVol =
(Case When(Select Count(*) From RC_Special_UnitVol Where SU_CSA_RecordID =CSA_RecordID and SU_IMA_ItemID =IMA_ItemID) =1
Then (Select SU_CubicVolume From RC_Special_UnitVol Where SU_CSA_RecordID =CSA_RecordID and SU_IMA_ItemID =IMA_ItemID)
Else isnull(IMA_CubicVolume,0) End)
,IMA_CubicVolUnitMeasure as VolUnitMeasure
,isnull((SOD_RequiredQty *
(Case When(Select distinct Count(*) From RC_Special_UnitVol Where SU_CSA_RecordID =CSA_RecordID and SU_IMA_ItemID =IMA_ItemID) =1
Then (Select SU_CubicVolume From RC_Special_UnitVol Where SU_CSA_RecordID =CSA_RecordID and SU_IMA_ItemID =IMA_ItemID)
Else isnull(IMA_CubicVolume,0) End)),0) as TotalUnitVol
,SOM_DefaultVATCodeID as Vat
,SOM_SpecialInst as RemarkPO
,SOM_Buyer as OrderNumber
,isnull(EMP_MidName,'-')+' '+ isnull(EMP_LastName,'-')+' ('+ isnull(EMP_FirstName,'-')+')' as LastUpdateBy
,SOI_LineNbrTypeCode as LineTypeCode
,isnull(IMA_Kit,'') as Kit
,IMA_ItemID as ItemID--> Add
,day(SOM_DefaultRequiredDate) as [Day]
,month(SOM_DefaultRequiredDate) as [Month]
,year(SOM_DefaultRequiredDate) as [Year]
Into #OPS
FROM SalesOrder
Inner Join SalesOrderLine on SOM_RecordID = SOI_SOM_RecordID
left Join ITEM On IMA_RecordID = SOI_IMA_RecordID
left join ItemAliasDetail on IAD_RecordID = SOI_IAD_ID
inner Join SalesOrderDelivery On SOD_SOI_RecordID = SOI_RecordID
inner Join EMP On SOM_EMP_RecordID = EMP_RecordID
inner Join Customer On CUS_RecordId = SOM_CUS_RecordId
inner join CustomerShipTo on CSA_RecordID = SOM_DefaultCSA_RecordID
Where SOD_RequiredDate Between dbo.Fn_Rc_DateWithTime(#ExfacDateFrom,0) and dbo.Fn_Rc_DateWithTime(#ExfacDateTo,1)
And CUS_CustomerID = #CustomerID
And SOI_LineNbrTypeCode = 'Item'
order by SOM_SalesOrderID,SOD_SOLineNbr
--Select * From #OPS
Select
CustomerID,
CustomerName,
ItemID,
AliasName,
PartName = (Select IMA_ItemName From Item Where IMA_ItemID = ItemID ),
PrivateRemark,
Qty,
ExFacDate,
SalesOrderID,
CustomerPO,
OrderNumber,
where IMA_ItemID = ItemID)
UserDef1 = (Select WH_LOC from VW_ItemID_WH_Loc where IMA_ItemID = ItemID)
Into #TempFinish
From #OPS
order by ExFacDate
Select * From #TempFinish
Drop table #OPS
Drop table #TempFinish
This error shows when you use Sub query and =,>,< etc operators, and the sub query returning more than one record.
Try changing this:
PartName = (Select IMA_ItemName From Item Where IMA_ItemID = ItemID )
to this:
PartName = (Select Top 1 IMA_ItemName From Item Where IMA_ItemID = ItemID
This is applicable to all the places in your query where such same scenario is there

How can I optimize the SQL query?

I have a query an SQL query as follows, can anybody suggest any optimization for this; I think most of the effort is being done for the Union operation - is there anything else can be done to get the same result ?
Basically I wanna query first portion of the UNION and if for each record there is no result then the second portion need to be run. Please help.
:
SET dateformat dmy;
WITH incidentcategory
AS (
SELECT 1 ord, i.IncidentId, rl.Description Category FROM incident i
JOIN IncidentLikelihood l ON i.IncidentId = l.IncidentId
JOIN IncidentSeverity s ON i.IncidentId = s.IncidentId
JOIN LikelihoodSeverity ls ON l.LikelihoodId = ls.LikelihoodId AND s.SeverityId = ls.SeverityId
JOIN RiskLevel rl ON ls.RiskLevelId = rl.riskLevelId
UNION
SELECT 2 ord, i.incidentid,
rl.description Category
FROM incident i
JOIN incidentreportlikelihood l
ON i.incidentid = l.incidentid
JOIN incidentreportseverity s
ON i.incidentid = s.incidentid
JOIN likelihoodseverity ls
ON l.likelihoodid = ls.likelihoodid
AND s.severityid = ls.severityid
JOIN risklevel rl
ON ls.risklevelid = rl.risklevelid
) ,
ic AS (
SELECT ROW_NUMBER() OVER (PARTITION BY i.IncidentId ORDER BY (CASE WHEN incidentTime IS NULL THEN GETDATE() ELSE incidentTime END) DESC,ord ASC) rn,
i.incidentid,
dbo.Incidentdescription(i.incidentid, '',
'',
'', '')
IncidentDescription,
dbo.Dateconverttimezonecompanyid(closedtime,
i.companyid)
ClosedTime,
incidenttime,
incidentno,
Isnull(c.category, '')
Category,
opencorrectiveactions,
reportcompleted,
Isnull(classificationcompleted, 0)
ClassificationCompleted,
Cast (( CASE
WHEN closedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
IncidentClosed,
Cast (( CASE
WHEN investigatorfinishedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationFinished,
Cast (( CASE
WHEN investigationcompletetime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationComplete,
Cast (( CASE
WHEN investigatorassignedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigatorAssigned,
Cast (( CASE
WHEN (SELECT Count(*)
FROM incidentinvestigator
WHERE incidentid = i.incidentid
AND personid = 1588
AND tablename = 'AdminLevels') = 0
THEN 0
ELSE 1
END ) AS BIT)
IncidentInvestigator,
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader,
incidentstatus,
incidentstatussearch
FROM incident i
LEFT OUTER JOIN incidentcategory c
ON i.incidentid = c.incidentid
WHERE i.isdeleted = 0
AND i.companyid = 158
AND incidentno <> 0
--AND reportcompleted = 1
--AND investigatorassignedtime IS NOT NULL
--AND investigatorfinishedtime IS NULL
--AND closedtime IS NULL
),
ic2 AS (
SELECT * FROM ic WHERE rn=1
)
SELECT * FROM ic2
--WHERE rownumber >= 0
-- AND rownumber < 0 + 10
--WHERE ic2.incidentid in(53327,53538)
--WHERE ic2.incidentid = 53338
ORDER BY incidentid DESC
Following is the execution plan I got:
https://www.dropbox.com/s/50dcpelr1ag4blp/Execution_Plan.sqlplan?dl=0
There are several issues:
1) use UNION ALL instead of UNION ALL to avoid the additional operation to aggregate the data.
2) try to modify the numerous function calls (e.g. dbo.Incidentdescription() ) to be an in-lie table valued function so you can reference it using CROSS APPLY or OUTER APPLY. Especially, if those functions referencing a table again.
3) move the subqueries from the SELECT part of the query to the FROM part using CROSS APPLY or OUTER APPLY again.
4) after the above is done, check the execution plan again for any missing indexes. Also, run the query with STATISTICS TIME, IO on to verify that the number of times a table
is referenced is correct (sometimes the execution plan put you in the wrong direction, especially if function calls are involved)...
Since the first inner query produces rows with ord=1 and the second produces rows with ord=2, you should use UNION ALL instead of UNION. UNION will filter out equal rows and since you will never get equal rows it is more efficient to use UNION ALL.
Also, rewrite your query to not use the WITH construct. I've had very bad experiences with this. Just use regular derived tables instead. In the case the query is still abnormally slow, try to serialize some derived tables to a temporary table and query the temporary table instead.
Try alternate approach by removing
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader
from the SELECT. Avoid using complex functions/sub-queries in select.

How do I compare SUM and COUNT()s in SQL?

I am building a small query to find all CustomerNumbers where all of their policies are in a certain status (terminated).
Here is the query I am working on
select
a.cn
,p.pn
, tp = COUNT(p.pn)
, tp2 = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select cn, cn2
from bc
union
select cn, cn2= fn
from ic
) as a
left join p as p
on a.cn = p.cn
group by
a.cn,
pn
My issue is when I add the clause:
WHERE cn = tp
It says the columns are invalid. Am I missing something incredibly obvious?
You can't use aliases at the same level of a query. The reason is that the where clause is logically evaluated before the select, so the aliases defined in the select are not available in the where.
A typical solution is to repeat the expression (other answers) or use a subquery or cte:
with cte as (
<your query here>
)
select cte.*
from cte
where TotalPolicies = TermedPolicies;
However, in your case, you have an easier solution, because you have an aggregation query. So just use:
having TotalPolicies = TermedPolicies
You cannot use the aliased aggregate column names in the where clause. You have to use the expression itself instead. Also you cannot use it as where cluase, but use it in the having clause
HAVING COUNT(p.PolicyNumber) = SUM(case when p.status = 4 then 1 else 0 end)
You can also make the whole query as a subquery then add your where statement:
select CustomerNumber
,PolicyNumber
,TotalPolicies
,TermedPolicies
from (
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
group by
a.CustomerNumber,
PolicyNumber
) tb
where TotalPolicies = TermedPolicies
select
a.CustomerNumber
,p.PolicyNumber
, COUNT(p.PolicyNumber) as TotalPolicies
, SUM(case when p.status = 4 then 1 else 0 end) as TermedPolicies
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
WHERE COUNT(p.PolicyNumber)= SUM(case when p.status = 4 then 1 else 0 end)
group by
a.CustomerNumber,
PolicyNumber
This should work. But it is not tested.
In order to filter by an aggregate function, you must include it in the HAVING clause, rather than the WHERE clause.
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
having COUNT(p.PolicyNumber) = SUM(case when p.status = 4 then 1 else 0 end)
group by
a.CustomerNumber,
PolicyNumber
The reason for this has to do with the way SQL engines evaluate queries. The contents of the WHERE clause are used to filter out rows before the aggregate functions are applied. If you could reference aggregate functions there, the engine would have to have some way to determine which predicates to apply before aggregation and which to apply after. The HAVING clause allows the engine to have a clear demarcation between the two: WHERE applies before aggregation and HAVING applies after aggregation.
When dealing with aggregations in a query that has grouping, you will need to use HAVING. This should work:
select
a.CustomerNumber
,p.PolicyNumber
, TotalPolicies = COUNT(p.PolicyNumber)
, TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)
from
(
select CustomerNumber, CompanyName
from BusinessClients
union
select CustomerNumber, CompanyName = FullName
from IndividualClients
) as a
left join Policies as p
on a.CustomerNumber = p.CustomerNumber
group by
a.CustomerNumber,
PolicyNumber
HAVING TermedPolicies = SUM(case when p.status = 4 then 1 else 0 end)