Group By in sql subquery - sql

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)

Related

SqlServer : Number of 'open' issues on a daily basis

I'm getting an error with this query "Msg 102, Level 15, State 1, Line 12
Incorrect syntax near ';'" and have been at it for a few hours now. I'm trying to calculate 'open' issues on given days (ideally between a timeframe but for now just on dates that have entries).
My Data is simplified as:
IssueID, CreationDate, CompletionDate
I'd like to tally open issues which is when Year(CompletionDate)=1900 and they are cumulative while they are open, ie: if yesterday there was 1 issue open and today has 1 issue open as well, then today's count of open is 2. They should drop off once they are resolved (Year(CompletionDate) <> 1900). Please help I think i'm close?
SELECT
x.created_date,
aOpen + Open_Issue - Resolved_Issue as totopen
from(
select
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)='1900' then 1 else 0 end) as aOpen,
sum( case when YEAR(i.CompletionDate)<>'1900' AND (i.CompletionDate >=
i.CreationDate) then 1 else 0 end ) as Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (SELECT count(IssueID) as tot, CompletionDate as resolved
from Issues where YEAR(CompletionDate)<>'1900' group by CompletionDate,
count(IssueID))x
ON i.CreationDate = x.resolved);
UPDATE
I have this returning output correctly on a daily basis only, as in it is not accounting for previous, still open issues (Legacy_Open_Issue) and adding them.
SELECT
created_date,
aOpen_Today + Legacy_Open_Issue - Resolved_Issue as totopen
FROM(
SELECT
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)=1900 then 1 else 0 end) as aOpen_Today,
sum( case when (YEAR(i.CompletionDate)<>1900 AND (i.CompletionDate >= i.CreationDate)) then 1 else 0 end ) as Legacy_Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (
SELECT count(IssueID) as tot, CompletionDate as resolved
FROM Issues
WHERE YEAR(CompletionDate)<>1900 group by CompletionDate
)x ON x.resolved = i.CreationDate
GROUP BY convert(varchar(10), cast(i.CreationDate as date), 101), coalesce(tot,0)
) AS y;
My Data is
IssueID CreationDate CompletionDate
1 1/15/2019 1/1/1900
2 1/16/2019 1/17/2019
3 1/16/2019 1/1/1900
4 1/20/2019 1/21/2019
5 1/28/2019 1/1/1900
6 1/30/2019 1/1/1900
My Output is
created_date totopen
1/15/2019 1
1/16/2019 2
1/20/2019 1
1/28/2019 1
1/30/2019 1
My Output SHOULD be
created_date totopen
1/15/2019 1
1/16/2019 3
1/20/2019 3
1/28/2019 3
1/30/2019 4
thank you for your help
You need to alias the derived table such as:
SELECT
x.created_date,
aOpen + Open_Issue - Resolved_Issue as totopen
from(
select
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)='1900' then 1 else 0 end) as aOpen,
sum( case when YEAR(i.CompletionDate)<>'1900' AND (i.CompletionDate >=
i.CreationDate) then 1 else 0 end ) as Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (SELECT count(IssueID) as tot, CompletionDate as resolved
from Issues where YEAR(CompletionDate)<>'1900' group by CompletionDate,
count(IssueID))x
ON i.CreationDate = x.resolved) as DT;
Derived tables require an alias. You need to add "AS {alias}" to the end of your query. You should also format and line break the code for better legibility.
SELECT
x.created_date,
aOpen + Open_Issue - Resolved_Issue as totopen
from(
select
convert(varchar(10), cast(i.CreationDate as date), 101) as created_date,
sum( case when YEAR(i.CompletionDate)=1900 then 1 else 0 end) as aOpen,
sum( case when YEAR(i.CompletionDate)<>1900 AND (i.CompletionDate >= i.CreationDate) then 1 else 0 end ) as Open_Issue,
coalesce(tot,0) as Resolved_Issue
FROM Issues i
LEFT JOIN (
SELECT count(IssueID) as tot, CompletionDate as resolved
from Issues
where YEAR(CompletionDate)<>1900
group by CompletionDate
)x ON i.CreationDate = x.resolved
group by convert(varchar(10), cast(i.CreationDate as date), 101)
) AS y;
Also, SO generally doesn't do multiple questions per post. I addressed the error/alias issue, but if you have results issues, you should post a new question with sample data and expected results.
To be able to use date ranges and ensure there are no gaps in your output (ie. on dates where no tickets were created), you may want to consider using a Dates fact / reference table like this:
Select d.Date
, count(i.IssueID) as TotalOpen
, sum(case when DateDiff(DD, d.Date, cast(i.CreationDate as date)) = 0 then 1 else 0 end) as NewOpened
, sum(case when DateDiff(DD, d.Date, cast(i.CompletionDate as date)) = 0 then 1 else 0 end) as NewClosed
From Dates d
Left join Issues i
on d.Date between convert(varchar(10), cast(i.CreationDate as date), 101) and
case when YEAR(i.CompletionDate)='1900' then d.Date else i.CompletionDate end
Group by d.Date
Fill the Dates table with all the dates you'd want to display results for (ie. everyday, weekdays) or use a where clause to filter the date range / pattern.

SUM by not all fields from GROUP BY

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'

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

Duplicates from an SQL Query

I have a dataset I retrieve from multiple joins. I have used SELECT DISTINCT in my statements but I still see duplicates in the result set. Here is the code:
SELECT DISTINCT Account
, PayoffAmtDOL as 'Payoff Amount DOL'
, PayoffAmtLOG as 'Payoff Amount LOG'
, PayoffAmountLive as 'Payoff Amount Live'
, [Difference]
, PrincipalBalance as 'Principal Balance'
, CreationDate as 'Date Entered System'
, CACSState as 'CACS State at Entry'
, PaymentsMade AS 'Payments Made'
, TotalPaymentAmount as 'Total Payment Amount'
, 'Liquidation Percentage' = CASE WHEN PayoffAmountLive = 0 THEN 1
WHEN ISNULL([Difference],0) = ISNULL(PayoffAmtDOL, 0) THEN 1
WHEN ISNULL([Difference],0) < 0 AND ISNULL(PayoffAmtDOL, 0) > 0 THEN 0
WHEN ISNULL([Difference],0) > 0 AND ISNULL(PayoffAmtDOL, 0) < 0 THEN 1
WHEN ISNULL([Difference],0) > ISNULL(PayoffAmtDOL, 0) THEN 1
WHEN [Difference] > 0 AND ISNULL(PayoffAmtDOL, 0) = 0 THEN 1
WHEN ISNULL(PayoffAmtDOL, 0) = 0 THEN 0
ELSE ISNULL([Difference],0)/ISNULL(PayoffAmtDOL, 0) END
, Cnt = 1
FROM
(
SELECT DISTINCT a.Account,
c.PayoffAmtDOL,
c.PayoffAmtLOG,
(ISNULL(c.PayoffAmtCACS, cacs.payoff_amt)) as 'PayoffAmountLive',
(ISNULL(c.PayoffAmtDOL, 0) - (ISNULL(c.PayoffAmtCACS , ISNULL(cacs.payoff_amt, 0)))) as 'Difference',
c.PrincipalBalance,
c.CreationDate,
c.CACSState,
(SELECT COUNT(PaymentID)
FROM tblATLPaymentInfo p
WHERE p.AccountID = a.AccountID
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) <= '3/27/2014'
) as 'PaymentsMade',
(SELECT SUM(PaymentAmount)
FROM tblATLPaymentInfo p
WHERE p.AccountID = a.AccountID
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), p.CreationDate, 101)) <= '3/27/2014'
) as 'TotalPaymentAmount'
FROM tblATLAcctInfo a
RIGHT JOIN tblATLClaimInfo c
ON c.AccountID = a.AccountID
LEFT JOIN SCFLOKYDCMSQL03.CACS_DM.dbo.Cacs_Info cacs
ON cacs.Account = a.Account
WHERE CONVERT(DATETIME, CONVERT(VARCHAR(10), c.CreationDate, 101)) >= '1/1/2014'
AND CONVERT(DATETIME, CONVERT(VARCHAR(10), c.CreationDate, 101)) <= '3/27/2014'
AND c.ClaimTypeID = (SELECT DISTINCT ClaimTypeID FROM tblATLClaimType WHERE ClaimType = 'N02 - Claims')
) a
ORDER BY Account
Here is an example of the duplicate rows:
AccountID DateEntered
123 01/19/2014
123 01/21/2014
345 02/1/2014
345 02/10/2014
The difference between appears to be the date entered. Maybe selecting the Row_Number() and then deleting the later date could be a solution
DISTINCT should not return multiple rows.. there should be at least one column that is different in each row, no? With character data, sometimes one can be fooled by non-visible differences, such as trailing spaces. Not sure if that is the case here, though.
Can you give an example of the duplicate rows?
OK, I see your edit. You have to select which of the dates to display. Try this to get the earliest date per AccountID:
SELECT AccountID, MIN(DateEntered) AS DateEntered
FROM ....
GROUP BY AccountID
ORDER BY AccountID
You can add more columns in the SELECT, as long as they are distinct you will not get more rows.
If you want, you can add COUNT(*) to the select to get the number of rows grouped.
DISTINCT will only reject lines that are exact duplicates, the DateEntered is different on each ID. If you want the latest, use Max(DateEntered)

counting events over flexible ranges

I am trying to count events (which are rows in the event_table) in the year before and the year after a particular target date for each person. For example, say I have a person 100 and target date is 10/01/2012. I would like to count events in 9/30/2011-9/30/2012 and in 10/02/2012-9/30/2013.
My query looks like:
select *
from (
select id, target_date
from subsample_table
) as i
left join (
select id, event_date, count(*) as N
, case when event_date between target_date-365 and target_date-1 then 0
when event_date between target_date+1 and target_date+365 then 1
else 2 end as after
from event_table
group by id, target_date, period
) as h
on i.id = h.id
and i.target_date = h.event_date
The output should look something like:
id target_date after N
100 10/01/2012 0 1000
100 10/01/2012 1 0
It's possible that some people do not have any events in the before or after periods (or both), and it would be nice to have zeros in that case. I don't care about the events outside the 730 days.
Any suggestions would be greatly appreciated.
I think the following may approach what you are trying to accomplish.
select id
, target_date
, event_date
, count(*) as N
, SUM(case when event_date between target_date-365 and target_date-1
then 1
else 0
end) AS Prior_
, SUM(case when event_date between target_date+1 and target_date+365
then 1
else 0
end) as After_
from subsample_table i
left join
event_table h
on i.id = h.id
and i.target_date = h.event_date
group by id, target_date, period
This is a generic answer. I don't know what date functions teradata has, so I will use sql server syntax.
select id, target_date, sum(before) before, sum(after) after, sum(righton) righton
from yourtable t
join (
select id, target_date td
, case when yourdate >= dateadd(year, -1, target_date)
and yourdate < target_date then 1 else 0 end before
, case when yourdate <= dateadd(year, 1, target_date)
and yourdate > target_date then 1 else 0 end after
, case when yourdate = target_date then 1 else 0 end righton
from yourtable
where whatever
group by id, target_date) sq on t.id = sq.id and target_date = dt
where whatever
group by id, target_date
This answer assumes that an id can have more than one target date.