SUM by not all fields from GROUP BY - sql

I have table :
Id(int),
CompanyId(uniqueidentifier),
DateCreated(datetime),
TypeId(int),
Value(int),
Date(date)
and query:
SELECT
W2CliendId,
CAST(DATEADD(month, DATEDIFF(month, 0, Date), 0) AS date) AS MonthStarts,
CASE
WHEN TypeId = 1 THEN '4 Search views'
WHEN TypeId = 2 THEN '5 Maps views'
WHEN TypeId = 0 THEN 'Unknown'
END AS [Type],
SUM(Value) AS [Value]
FROM CompanyTypeValues CTV
JOIN CompanyProfile CP
ON CP.CompanyId = CTV.CompanyId
WHERE CP.Status = 0 AND Date >= '2015-08-11'
GROUP BY CAST(DATEADD(month, DATEDIFF(month, 0, Date), 0) AS date), TypeId, W2CliendId
ORDER BY TypeId, MonthStarts, W2CliendId
How can I SELECT sum by only 2 fields from GROUP BY? (DATEADD(month, DATEDIFF(month, 0, Date), 0), W2CliendId)

If you have only 3 type Ids (seems that it is the case from your CASE above the sum) a solution can be to create three columns which repeat the case so that you have 3 aggregate results corresponding to the three type Ids:
So at the end of your query, replace the
SUM(Value) AS [Value]
BY
SUM(CASE
WHEN TypeId = 1 THEN Value
WHEN TypeId = 2 THEN 0
WHEN TypeId = 0 THEN 0
END AS [Value_Type1],
SUM(CASE
WHEN TypeId = 1 THEN 0
WHEN TypeId = 2 THEN Value
WHEN TypeId = 0 THEN 0
END AS [Value_Type2],
SUM(CASE
WHEN TypeId = 1 THEN 0
WHEN TypeId = 2 THEN 0
WHEN TypeId = 0 THEN Value
END AS [Value_Type0],
Group by is not designed to aggregate at two different levels in the same query. What you can do otherwise is two separate subqueries, one which aggregates on 3 columns and the other one which aggregages on 2 columns, and join the two queries.

Try using a Window Function if you have to partition by selected columns
SELECT w2cliendid,
cast(dateadd(month, datediff(month, 0, date), 0) AS date) AS monthstarts,
CASE
WHEN typeid = 1 THEN '4 Search views'
WHEN typeid = 2 THEN '5 Maps views'
WHEN typeid = 0 THEN 'Unknown'
END AS [Type],
sum(value) OVER (partion BY cast(dateadd(month, datediff(month, 0, date), 0) AS date)
, w2cliendid ORDER BY typeid, monthstarts, w2cliendid) AS [Value]
FROM companytypevalues ctv
JOIN companyprofile cp
ON cp.companyid = ctv.companyid
WHERE cp.status = 0
AND date >= '2015-08-11'

Related

A SQL query for the retrieval of result based on input month

Table 1
Table 2
My requirement is to input the Redemption month and list the tickets that has been scanned double or more.
For example Ticket No. T1 has been scanned 2 times under pickup,only once under PickupOutforDelivery and 2 times under Delivery.
Result needed like this:
How can I write a query to get the result like this?
Tried:
SELECT
Ticket,
COUNT(Scantype = 0) AS Pickup,
COUNT(Scantype = 1) AS PickupOutforDelivery,
COUNT(Scantype = 2) AS Delivery
FROM
Scans
GROUP BY
Ticket, ScanType
HAVING
(Pickup > 1 OR PickupOutforDelivery > 1 OR Delivery > 1)
OR (Pickup >= 1 AND PickupOutforDelivery >= 1)
ORDER BY
Ticket
Result
Assuming that RedemptionMonth has a datatype of DATE (which is clearly required); the following query will give you the result you want, except the "cosmetic" part (breaking by year month for the report part) that you have to do on your application:
SELECT YEAR(RedemptionMonth) AS [YEAR], MONTH(RedemptionMonth) AS [MONTH], TicketNo,
COALESCE(SUM(CASE WHEN ScanName = 'Pickup' THEN 1 ELSE 0 END), 0) AS Pickup,
COALESCE(SUM(CASE WHEN ScanName = 'PickupOutForDelivery' THEN 1 ELSE 0 END), 0) AS PickupOutForDelivery ,
COALESCE(SUM(CASE WHEN ScanName = 'Delivery' THEN 1 ELSE 0 END), 0) AS Delivery
FROM [Table 1] AS T1
JOIN [Table 2] AS T2
ON T1.ScanType = T2.ScanType
GROUP BY YEAR(RedemptionMonth) AS [YEAR], MONTH(RedemptionMonth) AS [MONTH], TicketNo
Because you are using the numeric value of scanType, no JOIN is needed. So, the only fix is needed for conditional aggregation:
SELECT Ticket,
SUM(CASE WHEN Scantype = 0 THEN 1 ELSE 0 END) as Pickup,
SUM(CASE WHEN Scantype = 1 THEN 1 ELSE 0 END) as PickupOutforDelivery,
SUM(CASE WHEN Scantype = 2 THEN 1 ELSE 0 END) as Delivery
FROM Scans
WHERE redemptionMonth = 'Jan-21'
GROUP BY Ticket
HAVING Pickup > 1 OR
PickupOutforDelivery > 1 OR
Delivery > 1 OR
(Pickup >= 1 AND PickupOutforDelivery >= 1)
ORDER BY Ticket;
Note that you can add redemptionMonth to the GROUP BY (and SELECT) to get the results for each month.
If redemptionMonth is really a date and not a string, then define the time period using a range of dates:
WHERE redemptionMonth >= '2021-01-01' AND
redemptionMonth < '2021-02-01'

Group By in sql subquery

So i have a query where i am trying to accumulate the count of order for phone and website however i realise you cant use a GROUP BY clause in the subquery when it is directly within a select as it returns more than one value. Therefore my question is where do i put the subquerys to achieve the same result.
SELECT CAST(a.DateCreated AS DATE),
(SELECT count(CAST(DateCreated AS DATE))
FROM [Sterlingbuild].[dbo].[CustomerOrder]
WHERE BookingSourceId = 1
GROUP BY CAST(DateCreated AS DATE)) AS 'Website',
(SELECT count(CAST(DateCreated AS DATE))
FROM [Sterlingbuild].[dbo].[CustomerOrder]
WHERE BookingSourceId = 2
GROUP BY CAST(DateCreated AS DATE)) AS 'Phone'
, count(CAST(a.DateCreated AS DATE)) AS 'Total Orders'
FROM [Sterlingbuild].[dbo].[CustomerOrder] a
WHERE CustomerOrderStatusId = 7
AND DepartmentId = 1
GROUP BY CAST(a.DateCreated AS DATE)
ORDER BY CAST(a.DateCreated AS DATE)
When i run this query it works therefore i know the data is correct:
SELECT count(CAST(DateCreated AS DATE))
FROM [Sterlingbuild].[dbo].[CustomerOrder]
WHERE BookingSourceId = 1
AND CustomerOrderStatusId = 7 AND DepartmentId = 1
GROUP BY CAST(DateCreated AS DATE)
I am guessing you want conditional aggregation:
SELECT CAST(co.DateCreated AS DATE),
SUM(CASE WHEN BookingSOurceId = 1 THEN 1 ELSE 0 END) as Website,
SUM(CASE WHEN BookingSOurceId = 2 THEN 1 ELSE 0 END) as Phone,
COUNT(*) as Total_Orders
FROM [Sterlingbuild].[dbo].[CustomerOrder] co
WHERE CustomerOrderStatusId = 7 AND DepartmentId = 1
GROUP BY CAST(a.DateCreated AS DATE)
ORDER BY CAST(a.DateCreated AS DATE)

How to return count in 2 columns?

I have this query. It should return Count for both AWARDED (1) and NOT AWARDED(0) works from works table.
Select Count(w.WorkID)as Total, w.IsAwarded, org.OrganizationName
From Works w
Inner Join MC_MemberShip.Membership.Organization org
ON org.OrganizationID= w.Organization_ID
where Convert(varchar(11), w.OpeningDate) >= Convert(varchar(11), #FromDate)
and Convert(varchar(11), w.OpeningDate) < DATEADD(day, 1, Convert(varchar(11), #ToDate))
and w.IsActive=1 and
ISNULL(w.IsAwarded,0)= 0 and w.Organization_ID= case when #OrgID= -1 then w.Organization_ID else #OrgID end
group by org.OrganizationName, w.IsAwarded
Now this query returns Total count for NOT AWARDED i.e. 0 only but i want to return count for AWARDED too in same query.
Organization TotalAwardedWorks TotalNotAwardedWorks
Town 1 1 2
Town 2 44 33
Your query should look something like this:
select org.OrganizationName,
Count(*) as Total,
sum(case when w.IsAwarded = 0 or w.IsAwarded is null then 1 else 0 end) as TotalNotAward,
sum(case when w.IsAwarded = 1 then 0 else 1 end) as TotalAward
from Works w Inner Join
MC_MemberShip.Membership.Organization org
on org.OrganizationID = w.Organization_ID
where w.OpeningDate >= #FromDate and
w.OpeningDate < dateadd(day, 1, #ToDate) and
w.IsActive = 1 and
(w.Organization_ID = #OrgId or #OrgID= -1)
group by org.OrganizationName;
Notes:
Do not convert dates to strings to perform comparisons. That is just perverse.
Generally, the use of case in the where clause is discouraged. The logic is more clearly represented using or.
You can get what you want by using case to put conditions in the aggregation functions.

Bring through previous 12 months count while grouping by period

I'm trying to use the below code to bring though a count of all lines in the last 12 month period for the period and plant, please see the output below.
So for example with the below output, rather than the 12 months column currently containing the total for the period, I want the count using a period between 201001-201101 (Please note, my example was only for the dataset below and the 12 months column needs to adapt for each period).
Period Plant Stock Special MonthTotal 12Months
201101 0EA0 27 0 27 27
201101 0EB0 35 2 37 37
The issue I'm having is that rather than bring through the last 12 month count, my code is merely bringing through the count for the current period. Please can someone assist?
select
convert(varchar(6),dateadd(mm,0,P.Dt),112) as Period,P.Plant,
Sum(Case When Left(Upper(Material),2) = 'ZZ' then 1 else 0 end) as Stock,
Sum(Case When Left(Upper(Material),2) <> 'ZZ' then 1 else 0 end) as Special
,Count(*) as MonthTotal,Sum(Case When
convert(varchar(6),dateadd(mm,0,P.Dt),112)
Between
convert(varchar(6),dateadd(mm,-12,P.Dt),112)
And
convert(varchar(6),dateadd(mm,0,P.Dt),112) Then 1 else 0 End
)as [12Months]
from
iesaonline.dbo.DS_POs as P where
Plant IN(
Select Client From METRICS.DBO.CO_001_Plants_090_Final
where CustGrp = 'Hovis'
)
Group by
P.Plant,convert(varchar(6),dateadd(mm,0,P.Dt),112)
order by
convert(varchar(6),dateadd(mm,0,Dt),112),Plant
The problem seems to be that you're grouping by year/month and trying to sum values outside that year/month range. Without sample data, I can't be certain, but it sounds like you want a rolling 12 month sum. Something like below should get you where you want to go.
;with monthlySubtotal as
(
select
dateadd(m, 1-datepart(day, p.dt), p.dt) as PeriodMonth
,P.Plant
,Sum(Case When Left(Upper(Material),2) = 'ZZ' then 1 else 0 end) as Stock
,Sum(Case When Left(Upper(Material),2) <> 'ZZ' then 1 else 0 end) as Special
,Count(*) as MonthTotal
from
iesaonline.dbo.DS_POs as P where
Plant IN(
Select Client From METRICS.DBO.CO_001_Plants_090_Final
where CustGrp = 'Hovis'
)
Group by
P.Plant
,dateadd(m, 1-datepart(day, p.dt), p.dt)
)
SELECT
convert(varchar(6),m1.PeriodMonth,112) Period
, m1.Plant
, m1.Stock
, m1.Special
, m1.MonthTotal
, SUM(m2.monthtotal) 12mototal
FROM monthlySubtotal m1
JOIN monthlySubtotal m2
ON m2.plant = m1.plant
AND m2.periodmonth BETWEEN dateadd(m, -11, m1.periodmonth)
AND m1.periodmonth
--You may want to filter this
--WHERE m1.periodmonth >= startdate
GROUP BY
convert(varchar(6),m1.PeriodMonth,112)
, m1.Plant
, m1.Stock
, m1.Special
, m1.MonthTotal
ORDER BY
Period
, Plant
There is no need to do everything at the same time.
It's easier to first get the monthly values
SELECT DATEADD(month, DATEDIFF(month, 0, Dt), 0) as FOM
, Plant
, Stock = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 1 ELSE 0 END)
, Special = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 0 ELSE 1 END)
FROM DS_POs
GROUP BY Plant, DATEADD(month, DATEDIFF(month, 0, Dt), 0)
and using that as the base to get the last 12 month of result using a CROSS APPLY
WITH DS_POSM AS (
SELECT DATEADD(month, DATEDIFF(month, 0, Dt), 0) as FOM
, Plant
, Stock = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 1 ELSE 0 END)
, Special = SUM(CASE WHEN LEFT(Upper(Material), 2) = 'ZZ' THEN 0 ELSE 1 END)
FROM DS_POs
GROUP BY Plant, DATEADD(month, DATEDIFF(month, 0, Dt), 0)
)
SELECT Convert(char(6), FOM, 112) Period
, Plant
, Stock
, Special
, MonthTotal = Stock + Special
, ly.[12Months]
FROM DS_POSM a
CROSS APPLY (SELECT Sum(Stock + Special) [12Months]
FROM DS_POSM lastyear
WHERE lastyear.FOM Between DateAdd(mm, -12, a.FOM) And a.FOM
AND lastyear.Plant = a.Plant
) ly
ORDER BY FOM, Plant
DATEADD(month, DATEDIFF(month, 0, Dt), 0) get the first day of the month of Dt

sql join and group by generated date range

I have Table1 and I need a query to populate Table2:
Problem here is with Date column. I want to know the process of location/partner combination per day. Main issue here is that I can't pick DateCreated and make it as default date since it doesn't necessarily cover whole date range, like in this example where it doesn't have 2015-01-07 and 2015-01-09. Same case with other dates.
So, my idea is to first select dates from some table which contains needed date range and then perform calculation for each day/location/partner combination from cte but in that case I can't figure out how to make a join for LocationId and PartnerId.
Columns:
Date - CreatedItems - number of created items where Table1.DateCreated = Table2.Date
DeliveredItems - number of delivered items where Table1.DateDateOut = Table2.Date
CycleTime - number of days delivered item was in the location (DateOut - DateIn + 1)
I started with something like this but it's very like that I completely missed the point with it:
with d as
(
select date from DimDate
where date between DATEADD(DAY, -365, getdate()) and getdate()
),
cr as -- created items
(
select
DateCreated,
LocationId,
PartnerId,
CreatedItems = count(*)
from Table1
where DateCreated is not null
group by DateCreated,
LocationId,
PartnerId
),
del as -- delivered items
(
select
DateOut,
LocationId,
ParnerId,
DeliveredItems = count(*),
CycleTime = DATEDIFF(Day, DateOut, DateIn)
from Table1
where DateOut is not null
and Datein is not null
group by DateOut,
LocationId,
PartnerId
)
select
d.Date
from d
LEFT OUTER JOIN cr on cr.DateCreated = d.Date -- MISSING JOIN PER LocationId and PartnerId
LEFT OUTER JOIN del on del.DateCompleted = d.Date -- MISSING JOIN PER LocationId and PartnerId
with range(days) as (
select 0 union all select 1 union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 /* extend as necessary */
)
select dateadd(day, r.days, t.DateCreated) as "Date", locationId, PartnerId,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when dateadd(day, r.days, t.DateCreated) = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.days between 0 and datediff(day, t.DateCreated, t.DateOut)
group by dateadd(day, r.days, t.DateCreated), LocationId, PartnerId;
If you only want the end dates (rather than all the dates in between) this is probably a better approach:
with range(dt) as (
select distinct DateCreated from T union
select distinct DateOut from T
)
select r.dt as "Date", locationId, PartnerId,
sum(
case
when r.dt = t.DateCreated
then 1 else 0
end) as CreatedItems,
sum(
case
when r.dt = t.Dateout
then 1 else 0
end) as DeliveredItems,
sum(
case
when r.dt = t.Dateout
then datediff(days, t.DateIn, t.DateOut) + 1 else 0
end) as CycleTime
from
<yourtable> as t
inner join range as r
on r.dt in (t.DateCreated, t.DateOut)
group by r.dt, LocationId, PartnerId;
If to specify WHERE clause? Something Like that:
WHERE cr.LocationId = del.LocationId AND
cr.PartnerId = del.PartnerId