Detailing query for split by month - sql-server

I have the following MS SQL query:
SELECT DNIS, COUNT(*) AS Numbers
FROM I3_IC.dbo.CallDetail_Viw WITH (NOLOCK)
WHERE CallDirection = 'Inbound'
AND CallType = 'External'
AND InteractionType = 0
GROUP BY DNIS
ORDER BY DNIS;
Which returns a list like this:
DNIS Count
+12013317062 34
+12018841142 1
+12019771912 1
+12025594026 72
This is over a period of 2 years. I would like a break-down per month
This is the layout of the DB I get this data from (some fields ommited)
CallId
CallType
CallDirection
LineId
RemoteNumberFmt
RemoteNumberCallId
InitiatedDate
InitiatedDateTimeGmt
ConnectedDate
ConnectedDateTimeGmt
TerminatedDate
TerminatedDateTimeGmt
DNIS
CallEventLog
The InitiatedDate and InitiatedDateTimeGmt fields are the ones that hold the date-time server based ad GMT based like this: 2013-02-11 23:01:26.000
Any suggestions appriciated.

So group it by month. Is there something I'm missing in your question?
SELECT DNIS, YEAR(InitiatedDate) as [Year], MONTH(InitiatedDate) as [Month], COUNT(*) AS Numbers
FROM I3_IC.dbo.CallDetail_Viw WITH (NOLOCK)
WHERE CallDirection = 'Inbound'
AND CallType = 'External'
AND InteractionType = 0
GROUP BY DNIS, YEAR(InitiatedDate), MONTH(InitiatedDate)
ORDER BY DNIS, YEAR(InitiatedDate), MONTH(InitiatedDate);

You can use the DATEPART function to extract the month and year from the InitiatedDate field and then include those values in the GROUP BY. You can also use those functions in the ORDER BY as well.
SELECT DNIS, COUNT(*) AS Numbers, DATEPART(mm, InitiatedDate) AS Month, DATEPART(yyyy, InitiatedDate) AS Year
FROM I3_IC.dbo.CallDetail_Viw WITH (NOLOCK)
WHERE CallDirection = 'Inbound'
AND CallType = 'External'
AND InteractionType = 0
GROUP BY DNIS, DATEPART(yyyy, InitiatedDate), DATEPART(mm, InitiatedDate)
ORDER BY DNIS, DATEPART(yyyy, InitiatedDate), DATEPART(mm, InitiatedDate) ;

Related

SQL Server Query # of Events Per Day

Perhaps I am making this more complicated that it really is, hopefully someone can point me in the right direction. I get pretty close this this query:
SELECT
Action, TimeOccurred,
COUNT(Action)
FROM
[].[dbo].[]
WHERE
Action LIKE '%Logon Failed%'
AND (DATEDIFF(day, TimeOccurred, GETDATE()) BETWEEN 0 AND 30)
GROUP BY
Action, TimeOccurred
ORDER BY
TimeOccurred
My problem is TimeOccurred is formatted like this: 2017-05-13 00:02:00 so right now instead of giving me all the "logon failed" events per day, I get it per hour/min/second as well.
I would like to essentially cut the hh:mm:ss off so my results are per day. Hopefully that makes sense.
You can convert() to date to truncate the time portion of a datetime data type.
select
Action
, TimeOccurred = convert(date,TimeOccurred )
, Count(Action)
from [].[dbo].[]
where Action like '%Logon Failed%'
and TimeOccured >= dateadd(day,-30,dateadd(day, datediff(day, 0, getdate()), 0))
group by Action
, convert(date,TimeOccurred)
order by TimeOccurred
For your where, you can calculate the date for 30 days ago instead of getting a datediff() and restricting that range to 0-30.
For conditional aggregation you could do something like this:
select
TimeOccurred = convert(date, TimeOccurred)
, logon_kerberos = count (case when Action like ' %logon (kerberos)%' then 1 end)
, logon_local_wts = count (case when Action like ' %logon (local/wts)%' then 1 end)
, logon_ntlm = count (case when Action like ' %logon (ntlm)%' then 1 end)
, logon_total = count (case when Action like ' %logon (%' then 1 end)
, Count(Action)
from [CPTRAX_for_Windows].[dbo].[Logon_Logoff_and_Failed_Logon_Profiles]
where Action like '%Logon (%'
and TimeOccurred >= dateadd(day, -30, dateadd(day, datediff(day, 0, getdate()), 0))
group by convert(date, TimeOccurred)
order by TimeOccurred
You can use a Calendar or dates table for this sort of thing.
For only 152kb in memory, you can have 30 years of dates in a table with this:
/* dates table */
declare #fromdate date = '20000101';
declare #years int = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, #fromdate,dateadd(year,#years,#fromdate)))
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date
on dbo.Dates([Date]);
Without taking the actual step of creating a table, you can use it inside a common table expression with just this:
declare #fromdate date = dateadd(day , datediff(day , 0, getdate() )-30 , 0);
declare #thrudate date = dateadd(day , datediff(day , 0, getdate() ), 0);
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
select top (datediff(day, #fromdate, #thrudate)+1)
[Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,#fromdate))
from n as deka cross join n as hecto cross join n as kilo
cross join n as tenK cross join n as hundredK
order by [Date]
)
select [Date]
from dates;
Use either like so:
select
TimeOccurred = d.Date
, logon_kerberos = count (case when Action like ' %logon (kerberos)%' then 1 end)
, logon_local_wts = count (case when Action like ' %logon (local/wts)%' then 1 end)
, logon_ntlm = count (case when Action like ' %logon (ntlm)%' then 1 end)
, logon_total = count (case when Action like ' %logon (%' then 1 end)
, Count(Action)
from Dates d
left join [CPTRAX_for_Windows].[dbo].[Logon_Logoff_and_Failed_Logon_Profiles] l
on d.Date = convert(date,l.TimeOccured)
and l.Action like '%Logon (%'
where d.Date >= dateadd(day, -30, dateadd(day, datediff(day, 0, getdate()), 0))
group by d.Date
order by d.Date
Number and Calendar table reference:
Generate a set or sequence without loops - 2 - Aaron Bertrand
The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
Creating a Date Table/Dimension in sql Server 2008 - David Stein
Calendar Tables - Why You Need One - David Stein
Creating a date dimension or calendar table in sql Server - Aaron Bertrand

SQL sum of current year commissions is higher than previous year

Trying to compare sum of current year commissions to the previous year but having trouble creating an inner join. If thisYear is higher I will update a growth_incentive field to '.05' Currently have something like this that gets the data but I feel like its inefficient
SELECT a.emp_no, SUM(a.total_commission) AS ThisYearComm
,(select SUM(total_commission) AS LastYearComm
from tbl_comm_medmon_employees_stats
where emp_no = a.emp_no
and year(getdate())-1 = comm_year) as lastyear
FROM tbl_comm_medmon_employees_stats a
where year(getdate()) = a.comm_year
GROUP BY a.emp_no, a.comm_year
If I understand your table correctly, you don't need a join. Try using conditional aggregation instead, like this:
SELECT a.emp_no
, SUM(case when year(getdate()) = a.comm_year then a.total_commission end) AS ThisYearComm
, SUM(case when year(getdate()) - 1 = a.comm_year then a.total_commission end) AS LastYearComm
FROM tbl_comm_medmon_employees_stats a
where a.comm_year in (year(getdate()), year(getdate()) - 1)
GROUP BY a.emp_no
You could try to use a conditional SUM instead
SELECT a.emp_no,
SUM(CASE WHEN year(getdate())-1 = a.comm_year THEN a.total_commission
ELSE 0
END) [LastYear],
SUM(CASE WHEN year(getdate()) = a.comm_year THEN a.total_commission
ELSE 0
END) [ThisYearComm]
FROM tbl_comm_medmon_employees_stats a
GROUP BY a.emp_no

SQL SMS 2008 -Count column ids and count duplicate ids if createddate is greater than 3 months between ids

*Edit (Hopefully to be more clear)
Table below, I would like to count ids and count duplicate ids where the createddate has a gap of 3 months or more for that ID.
Query I have so far...
if object_id('tempdb..#temp') is not null
begin drop table #temp end
select
top 100
a.id, a.CreatedDate
into #temp
from tbl a
where 1=1
--and year(CreatedDate) = '2015'
if object_id('tempdb..#temp2') is not null
begin drop table #temp2 end
select t.id, count(t.id) as Total_Cnt
into #temp2
from #temp t
group by id
select distinct #temp2.Total_Cnt, #temp2.id, #temp.CreatedDate, DENSE_RANK() over (partition by #temp.id order by createddate) RK
from #temp2
inner join #temp on #temp2.id = #temp.id
where 1=1
order by Total_Cnt desc
Results:
Total_cnt id createddate rk
3 1 01-01-2015 1
3 1 03-02-2015 2
3 1 01-02-2015 3
2 2 05-01-2015 1
2 2 05-02-2015 2
1 3 06-01-2015 1
1 4 07-01-2015 1
Count ids and only count duplicate ids when the createddate from the id is greater than 3 months.
Something like this...
Total_cnt id Countwith3monthgap
3 1 2
2 2 1
1 3 1
1 4 1
You can use a cte and ROW_NUMBER to get your order and self join the cte based on the order..
WITH cte AS
( SELECT
*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY CreatedDate) Rn
FROM
Test
)
SELECT
c1.ID,
COUNT(CASE WHEN c2.CreatedDate IS NULL THEN 1
WHEN c1.CreatedDate >= DATEADD(month,3,c2.CreatedDate) THEN 1
END)
FROM
cte c1
LEFT JOIN cte c2 ON c1.ID = c2.ID
AND c1.RN = c2.RN + 1
GROUP BY
c1.ID
You also need to use a conditional count where the Previous CreatedDate is null or if the Current CreatedDate is >= the Previous CreatedDate + 3 months
If you happen to be using SQL 2012+ you can also use LAG here to get the same result
SELECT
ID,
COUNT(*)
FROM
(SELECT
ID,
CreatedDate CurrentDate,
LAG(CreatedDate) OVER (PARTITION BY ID ORDER BY CreatedDate) PreviousDate
FROM
Test
) T
WHERE
PreviousDate IS NULL
OR CurrentDate >= DATEADD(month, 3, PreviousDate)
GROUP BY
ID
You can use a lag to get the previous date, Null for the first in the list
SELECT
id,
lag(CreatedDate,1) OVER (PARTITION BY Id ORDER BY CreatedDate) AS PreviousCreateDate,
CreatedDate
FROM #t
You can use that as a subquery and get the difference in months using DATEDIFF
SELECT sub.id,DATEDiff(month, sub.PreviousCreateDate ,sub.CreatedDate)
FROM (SELECT
id,
lag(CreatedDate,1) OVER (PARTITION BY Id ORDER BY CreatedDate) AS PreviousCreateDate,
CreatedDate
FROM #t) sub
WHERE DATEDiff(month, sub.PreviousCreateDate ,sub.CreatedDate) >=3
OR sub.PreviousCreateDate IS NULL
You can then take your totals
SELECT sub.id,COUNT(sub.id) as cnt
FROM (SELECT
id,
lag(CreatedDate,1) OVER (PARTITION BY Id ORDER BY CreatedDate) AS PreviousCreateDate,
CreatedDate
FROM #t) sub
WHERE DATEDIFF(month, sub.PreviousCreateDate ,sub.CreatedDate) >=3
OR sub.PreviousCreateDate IS NULL
GROUP BY sub.id
Note that using datediff the last day of january is three months before the first day of march. That appears to be the logic you were after.
You might want to define your three month gap criteria as
WHERE sub.PreviousCreateDate <= DATEADD(month, -3, sub.CreatedDate)
OR sub.PreviousCreateDate IS NULL
or
WHERE sub.CreatedDate >= DATEADD(month, +3, sub.PreviousCreateDate )
OR sub.PreviousCreateDate IS NULL
I'm guessing that your desired definition of three-month gap doesn't coincide with datediff()'s. Most of the logic here is to look back at the previous date and decide if the gap is big enough to qualify.
When datediff() counts three months difference we still need to make sure the day of month is later than the first one (per example and ID 5). If difference is more than three months then we're good automatically.
But I'm also assuming that you would want to treat the distance from November 30th to February 28th (or 29th in a leap year) as a full three months because the end date falls on the final day of the month. By adjusting the end date by an extra day this is an easy scenario to snag as it will bump the date into the following month and increase the month difference by one as well. If that's not what you want then just remove the dateadd(day, 1, ...) portion and use only the raw CreatedDate value.
You sample data is limited so I'm also making the assumption that the gaps are measure between consecutive dates. If you're wanting to find blocks of runs that don't span more than three months across the set, then that's a different problem and you should clarify with more information.
Since you've indicated that you're probably on SQL Server 2008 you'll have to do without the lag() function. Although the first query could be adjusted for that it's likely easier to go with the second approach at the end.
with diffs as (
select
ID,
row_number() over (partition by ID order by CreatedDate) as RN,
case when
datediff(
month,
lag(CreatedDate, 1) over (partition by ID order by CreatedDate),
CreatedDate
) = 3
and
datepart(
day,
lag(CreatedDate, 1) over (partition by ID order by CreatedDate)
) <= datepart(day, CreatedDate)
or
datediff(
month,
lag(CreatedDate, 1) over (partition by ID order by CreatedDate),
/* adding one day to handle gaps like Nov30 - Feb28/29 and Jan31 - Apr30 */
dateadd(day, 1, CreatedDate)
) >= 4
then 1
else 0
end as GapFlag
from <T> /* <--- your table name here */
), gaps as (
select
ID, RN,
sum(1 + GapFlag) over (partition by ID order by RN) as Counter
from diffs
)
select ID, count(distinct Counter - RN) as "Count"
from gaps
group by ID
The rest of the logic is a typical gaps and islands scenario looking for holes in the sum(1 + GapCount) sequence with the offset of 1 acting pretty much like row_number().
http://sqlfiddle.com/#!6/61b12/3
JamieD77's approach is also valid. I was originally thinking your problem involved more than looking at the rows in sequence. Here's how I would tweak it for the gap definition I've been running with:
with data as (
select ID, CreatedDate, row_number() over (partition by ID order by CreatedDate) as RN
from T
)
select ID, count(*) as "Count"
from data d1 left outer join data d0
on d0.ID = d1.ID and d0.RN = d1.RN - 1 /* connect to the one before */
where
datediff(month, d0.CreatedDate, d1.CreatedDate) = 3
and datepart(day, d0.CreatedDate) <= datepart(day, d0.CreatedDate)
or datediff(month, d0.CreatedDate, dateadd(day, 1, d0.CreatedDate)) >= 4
or d0.ID is null
group by ID
Edit: You have changed the question since yesterday.
Change this line in the first query to include the total count:
...
select count(*) as TotalCnt, ID, count(distinct Counter - RN) as GapCount
...
Second would look like:
with data as (
select ID, CreatedDate, row_number() over (partition by ID order by CreatedDate) as RN
from T
)
select
count(*) as TotalCnt, ID,
count(case when
datediff(month, d0.CreatedDate, d1.CreatedDate) = 3
and datepart(day, d0.CreatedDate) <= datepart(day, d0.CreatedDate)
or datediff(month, d0.CreatedDate, dateadd(day, 1, d0.CreatedDate)) >= 4
or d0.ID is null then 1 end
) as GapCount
from data d1 left outer join data d0
on d0.ID = d1.ID and d0.RN = d1.RN - 1 /* connect to the one before */
where
group by ID

tsql UNION without nulls

I have the following query
SELECT MONTH, COUNT(DISTINCT VISITS) AS BRAND_VISITS, NULL AS NONB_VISITS
FROM Table1
WHERE KEYWORD_TYPE = BRAND(
AND DATE >= '2013-01-01'
GROUP BY MONTH
UNION ALL
SELECT MONTH, NULL, COUNT(DISTINCT VISITS) AS NONB_VSTS
FROM Table1
WHERE KEYWORD_TYPE = NON-BRAND
AND DATE >= '2013-01-01'
GROUP BY MONTH
I get the following results:
1 352540 NULL
2 309834 NULL
3 228764 NULL
4 236054 NULL
5 218096 NULL
6 172527 NULL
1 NULL 5337
2 NULL 14120
3 NULL 9954
4 NULL 23755
5 NULL 19771
6 NULL 30797
However, what I want is inline results without NULLS
1 352540 5337
2 309834 14120
3 228764 9954
4 236054 23755
5 218096 19771
6 172527 30797
You can do this with using a single statement with CASE or with an JOIN on month instead of a UNION. If you take the join approach you may need to account for null values (no visist for a keyword in a month). You will want to profile them to see which is faster with your data and table structure. It is really all about the indexes and the amount of data you need to aggregate.
Assuming you don't have to worry about nulls based on the counts in your example, here is what you want.
SELECT brand.month, brand.brand_visits,nonbrand.non_brand_visits
FROM (SELECT month, COUNT(visits) AS brand_visits
FROM Table1
WHERE keyword_type = 'BRAND'
AND date >= '2013-01-01'
GROUP BY month) brand
INNER JOIN
(SELECT month, COUNT(visits) AS non_brand_visits
FROM Table1
WHERE keyword_type = 'NON-BRAND'
AND date >= '2013-01-01'
GROUP BY month) nonbrand
ON brand.month=nonbrand.month
Here is the CASE approach. You should profile based on your actual data you are aggregating and your indexes to see which method is faster.
SELECT month,
SUM(CASE WHEN keyword_type = 'BRAND' THEN 1 ELSE 0 END) AS brand_visits,
SUM(CASE WHEN keyword_type = 'NON-BRAND' THEN 1 ELSE 0 END) AS non_brand_visits
FROM Table1
WHERE date >= '2013-01-01'
GROUP BY month
Finally, you did not provide table structure or example data so I made some assumptions above. I strongly believe you did not need the COUNT(DISTINCT in your original statement. I have removed it and verified the two statement above yield the same results. If COUNT(DISTINCT is required then the CASE approach will not work but the join approach will still work fine.
Using your columns:
SELECT month,
count(distinct CASE WHEN keyword_type = 'BRAND' THEN visits END) AS BRAND_VISITS,
count(distinct CASE WHEN keyword_type = 'NON-BRAND' THEN visits END) AS NONB_VSTS
FROM Table1
WHERE date >= '2013-01-01'
and keyword_type in ('BRAND','NON-BRAND')
GROUP BY month
Am tempted to believe that month is simply the month from the date column, I would prefer this solution, it con cover more years than 1 and the same query will still be valid in the year 2014
SELECT cast(dateadd(month, datediff(month, 0, date), 0) as date) month,
count(distinct CASE WHEN keyword_type = 'BRAND' THEN visits END) AS BRAND_VISITS,
count(distinct CASE WHEN keyword_type = 'NON-BRAND' THEN visits END) AS NONB_VSTS
FROM Table1
WHERE date >= '2013-01-01'
and keyword_type in ('BRAND','NON-BRAND')
GROUP BY datediff(month, 0, date)
If you want to stick with your old script, you can fix it this way:
SELECT MONTH, max(BRAND_VISITS) BRAND_VISITS, max(NONB_VISITS) NONB_VISITS
FROM
(
SELECT MONTH, COUNT(DISTINCT VISITS) AS BRAND_VISITS, NULL AS NONB_VISITS
FROM Table1
WHERE KEYWORD_TYPE = 'BRAND'
AND DATE >= '2013-01-01'
GROUP BY MONTH
UNION ALL
SELECT MONTH, NULL, COUNT(DISTINCT VISITS) AS NONB_VSTS
FROM Table1
WHERE KEYWORD_TYPE = 'NON-BRAND'
AND DATE >= '2013-01-01'
GROUP BY MONTH
) a
GROUP BY MONTH

how to query two column of same table with two condition with groupby

Table :tbl_user
dateofregistration ID registrationstate
6-03-11 3 0
6-03-11 1 0
6-03-11 2 1
7-03-11 2 1
7-03-11 1 1
how can I display result like this for sql server 2008 express
date TotalID(count) Total State(0 only)
6-03-11 3 2
7-03-11 2 0
I have tried with this
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
(select COUNT(ID)) AS Subbase,
(Select Count(ID)from tbl_User where (registrationstate='0')) AS Totalchurn
FROM tbl_User
GROUP BY CONVERT(varchar(10), dateofregistration, 103);
but wrong result.Any help plz.
How about;
select
cast(dateofregistration as date),
count(distinct id), --or * for all
sum(
case registrationstate when '0' then 1 else 0 end
)
from tbl_user
group by cast(dateofregistration as date)
order by 1
2011-06-03 3 2
2011-07-03 2 0
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
COUNT(1) AS Subbase,
SUM(CASE WHEN registrationstate='0' THEN 1 ELSE 0 END) AS Totalchurn
FROM tbl_User
GROUP BY CONVERT(varchar(10), dateofregistration, 103)
ORDER BY 1
You were nearly there. You don't need a subselect for COUNT(ID) since that is handled by the GROUP BY. You group by date, and so the count will be the number of IDs within each date. I've made the count distinct, just in case you can have multiple registrations of the same ID on one day.
Your subquery was almost right - it needs to be correlated with the main query by selecting rows with the same registration date.
SELECT CONVERT(varchar(10), dateofregistration, 103) AS Date,
COUNT(DISTINCT ID) AS TotalID,
(Select Count(*) from tbl_User t2 where (registrationstate='0') AND t2.registrationdate=t1.registrationdate) AS Totalchurn
FROM tbl_User t1
GROUP BY CONVERT(varchar(10), dateofregistration, 103);

Resources