i have two query please tell me how to merge these two queries and also how to refine these query.Merge query on base on GroupID.
(SELECT count(idlee.ObjectId) AS 'Count', idlee.GroupId, idlee.Name
FROM (SELECT CONVERT(int, Sum(idle.distance)) AS distance, idle.ObjectId, idle.GroupId, idle.Name
FROM (SELECT Message.ObjectId, fn_GpsUtil_Distance(Message.x, Message.y, lead(Message.x)
OVER (partition BY Message.objectid
ORDER BY Message.GpsTime), lead(Message.y) OVER (partition BY Message.objectid
ORDER BY Message.GpsTime)) AS distance, [Group].GroupId, [Group].Name
FROM [Group] INNER JOIN
GroupObject ON [Group].GroupId = GroupObject.GroupId INNER JOIN
Message ON GroupObject.ObjectId = Message.ObjectId INNER JOIN
Object ON GroupObject.ObjectId = Object.ObjectId
WHERE (Object.Enabled = 1) AND (Object.ClientId = 5) AND (Message.GpsTime >= GETDATE() - 1) AND
(Message.GpsTime <= GETDATE())) AS idle
GROUP BY idle.ObjectId, idle.GroupId, idle.Name) AS idlee
WHERE idlee.distance < 10
GROUP BY idlee.GroupId, idlee.Name)
output
Count GroupID Group
36 15 DC-1
30 16 DC-2
13 17 DC-3
64 13 LC-1
16 14 LC-2
second query which i use to retrieve data
(SELECT count(idlee.ObjectId) AS 'Count', idlee.GroupId, idlee.Name
FROM (SELECT CONVERT(int, Sum(idle.distance)) AS distance, idle.ObjectId, idle.GroupId, idle.Name
FROM (SELECT Message.ObjectId, fn_GpsUtil_Distance(Message.x, Message.y, lead(Message.x)
OVER (partition BY Message.objectid
ORDER BY Message.GpsTime), lead(Message.y) OVER (partition BY Message.objectid
ORDER BY Message.GpsTime)) AS distance, [Group].GroupId, [Group].Name
FROM [Group] INNER JOIN
GroupObject ON [Group].GroupId = GroupObject.GroupId INNER JOIN
Message ON GroupObject.ObjectId = Message.ObjectId INNER JOIN
Object ON GroupObject.ObjectId = Object.ObjectId
WHERE (Object.Enabled = 1) AND (Object.ClientId = 5) AND (Message.GpsTime >= GETDATE() - 1) AND
(Message.GpsTime <= GETDATE())) AS idle
GROUP BY idle.ObjectId, idle.GroupId, idle.Name) AS idlee
WHERE idlee.distance >= 100 AND idlee.distance <= 300
GROUP BY idlee.GroupId, idlee.Name)
Count GroupID Group
40 15 DC-1
50 16 DC-2
20 17 DC-3
64 13 LC-1
16 14 LC-2
but i want output like this on Group base.
GroupID Group Count 0<10 Count 100 To 300
15 DC-1 36 40
16 DC-2 30 50
17 DC-3 13 20
13 LC-1 64 64
14 LC-2 16 16
Try this...join the SQLs with UNION, in the first one count(idlee.ObjectId) AS count1, 0 as count2 and in the second one 0 as count1, count(idlee.ObjectId) AS count2.
Then enclose the entire SQL as a temp table and sum Count1 and Count2.
SELECT Temp.GroupID, Temp.Group, Sum(Temp.Count1) as CountLess10, Sum(Temp.Count2) as Count100300 FROM
(
(SELECT count(idlee.ObjectId) AS count1, 0 as count2, idlee.GroupId as GroupID, idlee.Name as Group
FROM (SELECT CONVERT(int, Sum(idle.distance)) AS distance, idle.ObjectId, idle.GroupId, idle.Name
FROM (SELECT Message.ObjectId, fn_GpsUtil_Distance(Message.x, Message.y, lead(Message.x)
OVER (partition BY Message.objectid
ORDER BY Message.GpsTime), lead(Message.y) OVER (partition BY Message.objectid
ORDER BY Message.GpsTime)) AS distance, [Group].GroupId, [Group].Name
FROM [Group] INNER JOIN
GroupObject ON [Group].GroupId = GroupObject.GroupId INNER JOIN
Message ON GroupObject.ObjectId = Message.ObjectId INNER JOIN
Object ON GroupObject.ObjectId = Object.ObjectId
WHERE (Object.Enabled = 1) AND (Object.ClientId = 5) AND (Message.GpsTime >= GETDATE() - 1) AND
(Message.GpsTime <= GETDATE())) AS idle
GROUP BY idle.ObjectId, idle.GroupId, idle.Name) AS idlee
WHERE idlee.distance < 10
GROUP BY idlee.GroupId, idlee.Name)
UNION
(SELECT 0 as count1, count(idlee.ObjectId) AS count2, idlee.GroupId as GroupID, idlee.Name as Name
FROM (SELECT CONVERT(int, Sum(idle.distance)) AS distance, idle.ObjectId, idle.GroupId, idle.Name
FROM (SELECT Message.ObjectId, fn_GpsUtil_Distance(Message.x, Message.y, lead(Message.x)
OVER (partition BY Message.objectid
ORDER BY Message.GpsTime), lead(Message.y) OVER (partition BY Message.objectid
ORDER BY Message.GpsTime)) AS distance, [Group].GroupId, [Group].Name
FROM [Group] INNER JOIN
GroupObject ON [Group].GroupId = GroupObject.GroupId INNER JOIN
Message ON GroupObject.ObjectId = Message.ObjectId INNER JOIN
Object ON GroupObject.ObjectId = Object.ObjectId
WHERE (Object.Enabled = 1) AND (Object.ClientId = 5) AND (Message.GpsTime >= GETDATE() - 1) AND
(Message.GpsTime <= GETDATE())) AS idle
GROUP BY idle.ObjectId, idle.GroupId, idle.Name) AS idlee
WHERE idlee.distance >= 100 AND idlee.distance <= 300
GROUP BY idlee.GroupId, idlee.Name)
) Temp Group By Temp.GroupID, Temp.Group
Hope this helps.
Related
I have a table TRANS that contains the following records:
TRANS_ID TRANS_DT QTY
1 01-Aug-2020 5
1 01-Aug-2020 1
1 03-Aug-2020 2
2 02-Aug-2020 1
The expected output:
TRANS_ID TRANS_DT BEGBAL TOTAL END_BAL
1 01-Aug-2020 0 6 6
1 02-Aug-2020 6 0 6
1 03-Aug-2020 6 2 8
2 01-Aug-2020 0 0 0
2 02-Aug-2020 0 1 1
2 03-Aug-2020 1 0 1
Each trans_id starts with a beginning balance of 0 (01-Aug-2020). For succeeding days, the beginning balance is the ending balance of the previous day and so on.
I can create PL/SQL block to create the output. Is it possible to get the output in 1 SQL statement?
Thanks.
Try this following script using CTE-
Demo Here
WITH CTE
AS
(
SELECT DISTINCT A.TRANS_ID,B.TRANS_DT
FROM your_table A
CROSS JOIN (SELECT DISTINCT TRANS_DT FROM your_table) B
),
CTE2
AS
(
SELECT C.TRANS_ID,C.TRANS_DT,SUM(D.QTY) QTY
FROM CTE C
LEFT JOIN your_table D
ON C.TRANS_ID = D.TRANS_ID
AND C.TRANS_DT = D.TRANS_DT
GROUP BY C.TRANS_ID,C.TRANS_DT
ORDER BY C.TRANS_ID,C.TRANS_DT
)
SELECT F.TRANS_ID,F.TRANS_DT,
(
SELECT COALESCE (SUM(QTY), 0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT < F.TRANS_DT
) BEGBAL,
(
SELECT COALESCE (SUM(QTY), 0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT = F.TRANS_DT
) TOTAL ,
(
SELECT COALESCE (SUM(QTY), 0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT <= F.TRANS_DT
) END_BAL
FROM CTE2 F
You can as well do like this (I would assume it's a bit faster): Demo
with
dt_between as (
select mindt + level - 1 as trans_dt
from (select min(trans_dt) as mindt, max(trans_dt) as maxdt from t)
connect by level <= maxdt - mindt + 1
),
dt_for_trans_id as (
select *
from dt_between, (select distinct trans_id from t)
),
qty_change as (
select distinct trans_id, trans_dt,
sum(qty) over (partition by trans_id, trans_dt) as total,
sum(qty) over (partition by trans_id order by trans_dt) as end_bal
from t
right outer join dt_for_trans_id using (trans_id, trans_dt)
)
select
trans_id,
to_char(trans_dt, 'DD-Mon-YYYY') as trans_dt,
nvl(lag(end_bal) over (partition by trans_id order by trans_dt), 0) as beg_bal,
nvl(total, 0) as total,
nvl(end_bal, 0) as end_bal
from qty_change q
order by trans_id, trans_dt
dt_between returns all the days between min(trans_dt) and max(trans_dt) in your data.
dt_for_trans_id returns all these days for each trans_id in your data.
qty_change finds difference for each day (which is TOTAL in your example) and cumulative sum over all the days (which is END_BAL in your example).
The main select takes END_BAL from previous day and calls it BEG_BAL, it also does some formatting of final output.
First of all, you need to generate dates, then you need to aggregate your values by TRANS_DT, and then left join your aggregated data to dates. The easiest way to get required sums is to use analitic window functions:
with dates(dt) as ( -- generating dates between min(TRANS_DT) and max(TRANS_DT) from TRANS
select min(trans_dt) from trans
union all
select dt+1 from dates
where dt+1<=(select max(trans_dt) from trans)
)
,trans_agg as ( -- aggregating QTY in TRANS
select TRANS_ID,TRANS_DT,sum(QTY) as QTY
from trans
group by TRANS_ID,TRANS_DT
)
select -- using left join partition by to get data on daily basis for each trans_id:
dt,
trans_id,
nvl(sum(qty) over(partition by trans_id order by dates.dt range between unbounded preceding and 1 preceding),0) as BEGBAL,
nvl(qty,0) as TOTAL,
nvl(sum(qty) over(partition by trans_id order by dates.dt),0) as END_BAL
from dates
left join trans_agg tr
partition by (trans_id)
on tr.trans_dt=dates.dt;
Full example with sample data:
alter session set nls_date_format='dd-mon-yyyy';
with trans(TRANS_ID,TRANS_DT,QTY) as (
select 1,to_date('01-Aug-2020'), 5 from dual union all
select 1,to_date('01-Aug-2020'), 1 from dual union all
select 1,to_date('03-Aug-2020'), 2 from dual union all
select 2,to_date('02-Aug-2020'), 1 from dual
)
,dates(dt) as ( -- generating dates between min(TRANS_DT) and max(TRANS_DT) from TRANS
select min(trans_dt) from trans
union all
select dt+1 from dates
where dt+1<=(select max(trans_dt) from trans)
)
,trans_agg as ( -- aggregating QTY in TRANS
select TRANS_ID,TRANS_DT,sum(QTY) as QTY
from trans
group by TRANS_ID,TRANS_DT
)
select
dt,
trans_id,
nvl(sum(qty) over(partition by trans_id order by dates.dt range between unbounded preceding and 1 preceding),0) as BEGBAL,
nvl(qty,0) as TOTAL,
nvl(sum(qty) over(partition by trans_id order by dates.dt),0) as END_BAL
from dates
left join trans_agg tr
partition by (trans_id)
on tr.trans_dt=dates.dt;
You can use a recursive query to generate the overall date range, cross join it with the list of distinct tran_id, then bring the table with a left join. The last step is aggregation and window functions:
with all_dates (trans_dt, max_dt) as (
select min(trans_dt), max(trans_dt) from trans group by trans_id
union all
select trans_dt + interval '1' day, max_dt from all_dates where trans_dt < max_dt
)
select
i.trans_id,
d.trans_dt,
coalesce(sum(sum(t.qty)) over(partition by i.trans_id order by d.trans_dt), 0) - coalesce(sum(t.qty), 0) begbal,
coalesce(sum(t.qty), 0) total,
coalesce(sum(sum(t.qty)) over(partition by i.trans_id order by d.trans_dt), 0) endbal
from all_dates d
cross join (select distinct trans_id from trans) i
left join trans t on t.trans_id = i.trans_id and t.trans_dt = d.trans_dt
group by i.trans_id, d.trans_dt
order by i.trans_id, d.trans_dt
I need to be able to get rid of one of the workdate and one of the sr_name columns but I can't figure out how to.
I'm getting the query returned like this:
Query
I'm also getting this error message when entered into tableau:
The column 'sr_name' was specified multiple times for 'Custom SQL Query'.
Below is the code I have. If I remove a sr_name from either subquery there will be an error in the join clause.
select *
from
(
select s.sr_name, cast(punchdatetime as date) as workdate,
((datediff(second, min(case when p.InOut = 1 then punchdatetime end),
max(case when p.InOut = 0 then punchdatetime end))/3600) - .5) as
hoursworked
from PunchClock p join ServiceReps s on p.ServRepID = s.ServRepID
where punchyear >= 2019
group by s.sr_name, cast(punchdatetime as date)
) v
join
(
select sr_name, t.*,
calls = (select count(*) from CRM_Correspondence cr where
cast(cr.DateCreated as date) = workdate and StatusType like '%call%' and
cr.ServRepID = t.servrepid),
reaches = (select count(*) from CRM_Correspondence cr where
cast(cr.DateCreated as date) = workdate and (StatusType = 'call reached'
or StatusType like '%SCHEDULE%') and cr.ServRepID = t.servrepid),
books = (select count(*) from os_appointments o where cast(o.DateCreated
as date) = workdate and isnull(o.confirmedby, o.booked_by) =
t.servrepid),
attends = (select count(*) from os_appointments o where
cast(o.DateCreated as date) = workdate and isnull(o.confirmedby,
o.booked_by) = t.servrepid and o.appointmentStatus = 'attended')
from
(
select cast(cor.datecreated as date) workdate, cor.ServRepID
from CRM_Correspondence cor
where cor.datecreated > '2019-01-01'
group by cast(cor.datecreated as date), cor.servrepid
) t
join ServiceReps sr on t.ServRepID = sr.ServRepID
) u on v.sr_name = u.sr_name and v.workdate = u.workdate
I need the same results just without the duplicate column so I can enter the query into tableau.
This is challenging because you have so many subqueries here. This could be refactored to use a single query which is what I would do. But using the existing query you could do something along these lines.
I had to format this very differently so I could isolate each piece.
select v.sr_name
, v.workdate
, v.hoursworked
, u.ServRepID
, u.calls
, u.reaches
, u.books
, u.attends
from
(
select s.sr_name
, cast(punchdatetime as date) as workdate
, ((datediff(second, min(case when p.InOut = 1 then punchdatetime end), max(case when p.InOut = 0 then punchdatetime end))/3600) - .5) as hoursworked
from PunchClock p
join ServiceReps s on p.ServRepID = s.ServRepID
where punchyear >= 2019
group by s.sr_name
, cast(punchdatetime as date)
) v
join
(
select sr_name
, t.*
, calls =
(
select count(*)
from CRM_Correspondence cr
where cast(cr.DateCreated as date) = workdate
and StatusType like '%call%'
and cr.ServRepID = t.servrepid
)
, reaches =
(
select count(*)
from CRM_Correspondence cr
where cast(cr.DateCreated as date) = workdate
and (StatusType = 'call reached' or StatusType like '%SCHEDULE%')
and cr.ServRepID = t.servrepid
)
, books =
(
select count(*)
from os_appointments o
where cast(o.DateCreated as date) = workdate and isnull(o.confirmedby, o.booked_by) = t.servrepid
)
, attends =
(
select count(*)
from os_appointments o
where cast(o.DateCreated as date) = workdate
and isnull(o.confirmedby, o.booked_by) = t.servrepid
and o.appointmentStatus = 'attended'
)
from
(
select cast(cor.datecreated as date) workdate
, cor.ServRepID
from CRM_Correspondence cor
where cor.datecreated > '2019-01-01'
group by cast(cor.datecreated as date)
, cor.servrepid
) t
join ServiceReps sr on t.ServRepID = sr.ServRepID
) u on v.sr_name = u.sr_name
and v.workdate = u.workdate
Anyone know how I can change the Total Median near bottom to show an average of the median instead? For some reason, the Total Median is always 100. Not sure what I should do.
Thanks in advance for any ideas! Current results also below.
WITH CTE AS (
SELECT DISTINCT c.CaseID AS CaseID,
DATEDIFF(d, c.CaseAddDt, coip.DispoDt) AS DaysApart
, DATEPART(month,c.CaseAddDt) AS [Month]
, DATEPART(year,c.CaseAddDt) AS [Year]
, CAST(DATEPART(year,c.CaseAddDt) AS varchar) + '|' + CASE WHEN DATEPART(month,c.CaseAddDt) IN (10,11,12) THEN CAST(DATEPART(month,c.CaseAddDt) AS varchar) ELSE '0' + CAST(DATEPART(month,c.CaseAddDt) AS varchar) END AS Srt
FROM jw50_Case c
JOIN jw50_CaseInvPers def ON def.CaseID = c.CaseID
AND def.InvolveTypeMasterCode = 1
JOIN
jw50_CountInvPers coip ON coip.CaseID = c.CaseID
AND coip.CaseInvPersID = def.CaseInvPersID
AND coip.DispoCode IN ('CODE','CODE')
AND coip.CountNum > 0
OUTER APPLY (
SELECT TOP 1 caz.CaseAgencyID
FROM jw50_CaseAgency caz
WHERE caz.CaseID = c.CaseID
AND caz.AgencyCode = 'ABC'
AND caz.NumberTypeCode IN ('i#','in#')) caz
WHERE
EXISTS (SELECT 1 FROM jw50_CaseAttributes ca WHERE ca.CaseID = c.CaseID AND ca.CaseAttributeCode = 'oa7')
AND caz.CaseAgencyID IS NOT NULL
AND c.CaseStatusCode <> 'AAA'
AND c.CaseAddDt BETWEEN '01/01/2017' AND '08/01/2017'
AND c.CaseAddDt <= coip.DispoDt)
SELECT a.CaseID,
a.Month
, a.Year
, a.DaysApart
, a.Srt
, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.DaysApart) OVER (PARTITION BY a.Month, a.Year) AS MonMedian
, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY a.DaysApart) OVER (PARTITION BY 1) AS TotalMedian
FROM CTE a
Results:
I have a query which select all car makes and count each make quantity in response
SELECT
q.Make, Count(q.ID)
FROM
(SELECT
cars.ID, cars.Make, cars.Model,
cars.Year1, cars.Month1, cars.KM, cars.VIN,
cars.Fuel, cars.EngineCap, cars.PowerKW,
cars.GearBox, cars.BodyType, cars.BodyColor,
cars.Doors, cars.FullName, Transport.address,
(DateDiff(second,Getdate(),cars.AuEnd)) as r,
cars.AuEnd, cars.BuyNowPrice, cars.CurrentPrice
FROM
cars
LEFT JOIN
Transport ON Cars.TransportFrom = Transport.ID
WHERE
Active = 'True'
AND AuEnd > GETDATE()
AND year1 >= 1900 AND year1 <= 2015
AND Make in ('AUDi', 'AIXAM', 'ALPINA')
ORDER BY
cars.make ASC, cars.model ASC
OFFSET 50 ROWS FETCH NEXT 50 ROWS ONLY) AS q
GROUP BY
q.make ORDER BY q.make ASC;
Now I need get in result as third field each make total count without offset,
so now I get result
Make CountInResponse
AIXAM 1
ALPINA 1
AUDI 48
But I need to get
Make CountInResponse Total
AIXAM 1 1
ALPINA 1 1
AUDI 48 100
I think I need something like
SELECT
q.Make, Count(q.ID),
(SELECT Make, Count(ID)
FROM cars
WHERE Active = 'True' AND AuEnd > GETDATE()
AND year1 >= 1900 AND year1 <= 2015
AND Make in ('AUDI', 'AIXAM', 'ALPINA')
GROUP BY Make) as q2
FROM
(SELECT
cars.ID, cars.Make, cars.Model,
cars.Year1, cars.Month1, cars.KM, cars.VIN,
cars.Fuel, cars.EngineCap, cars.PowerKW,
cars.GearBox, cars.BodyType, cars.BodyColor,
cars.Doors, cars.FullName, Transport.address,
(DateDiff(second,Getdate(),cars.AuEnd)) as r,
cars.AuEnd, cars.BuyNowPrice, cars.CurrentPrice
FROM
cars
LEFT JOIN
Transport ON Cars.TransportFrom = Transport.ID
WHERE
Active = 'True'
AND AuEnd > GETDATE()
AND year1 >= 1900 AND year1 <= 2015
AND Make in ('AUDi', 'AIXAM', 'ALPINA')
ORDER BY
cars.make ASC, cars.model ASC
OFFSET 50 ROWS FETCH NEXT 50 ROWS ONLY) AS q
But I get an error
Msg 116, Level 16, State 1, Line 10
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
How to write right syntax?
The problem is you are selecting two columns in q2 (ie) Make, Count(ID) you cannot do that in SQL Server.
Try something like this.
WITH cte AS
(
SELECT
row_number() OVER(order by cars.make ASC,cars.model ASC) AS rn,
cars.id, cars.make
FROM
cars
LEFT JOIN
transport ON cars.transportfrom = transport.id
WHERE
active = 'True'
AND auend > getdate()
AND year1 >= 1900 AND year1 <= 2015
AND make IN ('AUDI', 'AIXAM', 'ALPINA')
)
SELECT
make ,
count(CASE WHEN RN BETWEEN 50 AND 100 THEN 1 END) AS countinresponse,
count(1) AS total
FROM
cte
GROUP BY
make
Or you need to convert the sub-query in select to correlated sub-query
SELECT q.make,
Count(q.id) countinresponse,
(
SELECT Count(id)
FROM cars C1
WHERE c1.id = q.id
AND active='True'
AND auend > Getdate()
AND year1 >= 1900
AND year1 <= 2015
AND make IN ('AUDi',
'AIXAM',
'ALPINA')
GROUP BY make) AS total
FROM (
SELECT cars.id,
cars.make
FROM cars
LEFT JOIN transport
ON cars.transportfrom=transport.id
WHERE active='True'
AND auend > Getdate()
AND year1 >= 1900
AND year1 <= 2015
AND make IN ('AUDi',
'AIXAM',
'ALPINA')
ORDER BY cars.make ASC,
cars.model ASC offset 50 rowsfetch next 50 rows only ) AS q
GROUP BY q.make
ORDER BY q.make ASC;
I have the below query -
SELECT
P.PRODUCT_NUMBER,
P.PRODUCT_DESCRIPTION,
SUM(S.NET_AMOUNT),
ROUND(STDDEV(S.NET_AMOUNT),2) AS STD_DEV
--(SELECT COUNT OF NET_AMOUNT < = 1$ FROM PFI_FACT_SALES GROUPED BY THE SAME P.PRODUCT_NUMBER) AS CNT
FROM PFI_DIM_PRODUCT P
JOIN PFI_FACT_SALES S
ON P.PRODUCT_PK_ID = S.PRODUCT_PK_ID
WHERE P.PRODUCT_NUMBER = 'ABC'
GROUP BY P.PRODUCT_NUMBER, P.PRODUCT_DESCRIPTION;
This is the part I am not able to figure out -
(SELECT COUNT OF NET_AMOUNT < = 1$ FROM PFI_FACT_SALES GROUPED BY THE SAME P.PRODUCT_NUMBER) AS CNT
What would be the best way to get the necessary row level data group by product number & product description?
Thanks.
One way is with a correlated subquery:
SELECT P.PRODUCT_NUMBER, P.PRODUCT_DESCRIPTION, SUM(S.NET_AMOUNT),
ROUND(STDDEV(S.NET_AMOUNT), 2) AS STD_DEV ,
(SELECT COUNT(*)
FROM PFI_FACT_SALES s2
WHERE s2.PRODUCT_PK_ID = s.PRODUCT_PK_ID AND
NET_AMOUNT <= 1
) as CNT
FROM PFI_DIM_PRODUCT P JOIN
PFI_FACT_SALES S
ON P.PRODUCT_PK_ID = S.PRODUCT_PK_ID
WHERE P.PRODUCT_NUMBER = 'ABC'
GROUP BY P.PRODUCT_NUMBER, P.PRODUCT_DESCRIPTION;
I'm pretty sure that you can also do this with a conditional windowed sum:
SELECT P.PRODUCT_NUMBER, P.PRODUCT_DESCRIPTION, SUM(S.NET_AMOUNT),
ROUND(STDDEV(S.NET_AMOUNT), 2) AS STD_DEV,
SUM(CASE WHEN NET_AMOUNT <= 1 THEN 1 ELSE 0 END) OVER (PARTITION BY s.PRODUCT_PK_ID) as CNT
FROM PFI_DIM_PRODUCT P JOIN
PFI_FACT_SALES S
ON P.PRODUCT_PK_ID = S.PRODUCT_PK_ID
WHERE P.PRODUCT_NUMBER = 'ABC'
GROUP BY P.PRODUCT_NUMBER, P.PRODUCT_DESCRIPTION;