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
Related
I am using Microsoft SQL Server and am trying to achieve the following
Date
Distinct Customers last 30Days
2020-12-01
20000
2020-12-02
23000
What I am trying to get is that between 2020-11-01 and 2020-12-01 I had 20000 distinct customers.
I have created a cte table with the List of Dates as can be seen below:
WITH listdate AS
(
SELECT CAST('2020-11-01' AS datetime) DateValue
UNION ALL
SELECT DateValue + 1
FROM listdate
WHERE DateValue + 1 < getdate()
)
SELECT
cast(DateValue as date) as DateValue
FROM listdate d
Now I am trying to join the customer and usage table with the list of dates table, however, I am not getting the correct end result. The following is what I have tried doing:
WITH listdate AS
(
SELECT CAST('2020-11-01' AS datetime) DateValue
UNION ALL
SELECT DateValue + 1
FROM listdate
WHERE DateValue + 1 < getdate()
)
SELECT
cast(DateValue as date) as DateValue
,count(distinct case when m.CallDate between dateadd(dd,-30,cast(d.datevalue as date)) and cast(d.datevalue as date) then m.Customerid end) as Distinct_CID
FROM listdate d
join Usage m on d.DateValue=m.CallDate
left join Customer c on c.CustomerID=m.Customer
where c.customertype = 'type A'
group by d.DateValue
OPTION (MAXRECURSION 0)
Can someone maybe suggest a different way of how to solve such a query?
Thanks
I would go for a lateral join to bring the count of distinct customers for the last 30 days:
with listdate as (
select cast('20201101' as date) as datevalue
union all
select dateadd(day, 1, datevalue) from listdate where datevalue < cast(getdate() as date)
)
select ld.datevalue, x.cnt
from listdate ld
cross apply (
select count(distinct c.customerid) as cnt
from usage u
inner join customer c on c.customerid = u.customerid
where
c.customertype = 'type A'
and c.calldate >= dateadd(day, -29, datevalue)
and c.calldate < dateadd(day, 1, datevalue)
) x
option (maxrecursion 0)
Note that I simplified the parts related to dates: this uses proper literal dates and date arithmetics in the recursive query; the where clause of the subquery implements what I understand as the last 30 days (today + the preceding 29 days), and properly handles the time portion of calldate, if any.
I have a query that appeared to work well at first but needs a slight tweak. Where I am right in the middle of it I've got a total mind block on the tweak so I'm throwing it out there for assistance! :)
As part of a larger stored proc, this section goes away to aMarketDataHistory table to get columns for lastBusnessDay, lastWeekEnd (|friday / business day) and lastMonthEnd.
I've used a CASE to appoint an integer (1 for lastBusinessDay, 2 for lastWeek and 3 for lastMonthEnd and then 4 for anything else such that we can select only IN (1,2,3) on the outer SELECT.
This seemed cool except I then noticed in testing last week that lastWeek and lastMonthEnd were both 31 May and then I had missing values further down the line (we always need columns for all 3 later).
DECLARE #date DATETIME = GETDATE()
SELECT #lastBusinessDay = (CONVERT(VARCHAR(10), DATEADD(DAY,
(CASE DATENAME(WEEKDAY, convert(date,#date))
WHEN 'Sunday' THEN -2
WHEN 'Monday' THEN -3
ELSE -1
END),
CONVERT(DATE, #date))) + ' 00:00:00.000')
DECLARE #mydate datetime
SELECT #lastWeek = DATEADD(day, (DATEDIFF (day, '19990102', #DATE) / 7) * 7, '19990101') as friday_before_mydate
SELECT #lastMonthEnd = DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE())-1, -1);
Ranked AS
(SELECT ISIN
--,[DATE]
,GRP
,ROW_NUMBER() OVER (PARTITION BY date, grp ORDER BY t.isin) AS [ROWNUMBER]
,COALESCE(ISNULL(TRY_CAST(BID_SPREAD AS FLOAT) + TRY_CAST(ASK_SPREAD AS FLOAT),0) / 2, 0) AS CLOSE_BIDASKAVG
,COALESCE(ISNULL(TRY_CAST(ASK AS FLOAT) + TRY_CAST(BID AS FLOAT),0) / 2, 0) AS CLOSE_PRICEAVG
,COALESCE(ISNULL(TRY_CAST(ASK_YIELD AS FLOAT) + TRY_CAST(BID_YIELD AS FLOAT),0) / 2, 0) AS CLOSE_YIELDAVG
FROM (SELECT *, (CASE WHEN date = #lastBusinessDay THEN 1
WHEN date = #lastWeek THEN 2
WHEN date = #lastMonthEnd THEN 3
ELSE 4
END) as GRP --1 for yesterday, 2 for last week, 3 for last month end and ignore 4 on outer
FROM marketdatahistory
WHERE DATE >= #lastMonthEnd
) t
WHERE GRP IN (1,2,3)
AND isin <> '')
I'd appreciate someone helping me past my mind block, haha, with a tweak on how I cope with 2 of 3 variables sometimes being the same but always retrieving all 3.
Thanks
Leigh Tilley (TilleyTech Ltd)
If the desired result is that when two conditions are both true you want the rows to appear twice, then you want a join, not a case. Focusing in on your inner SELECT:
FROM (SELECT M.*, G.GRP
FROM marketdatahistory AS M
INNER JOIN (
--1 for yesterday, 2 for last week, 3 for last month end
SELECT 1 AS GRP, #lastbusinessday as D UNION ALL
SELECT 2 AS GRP, #lastWeek as D UNION ALL
SELECT 3 AS GRP, #lastMonthEnd as D
) AS G
ON M.DATE >= #lastMonthEnd
AND M.DATE = G.D
) t
Since you're going to exclude your group 4s anyway, just don't define those rows at all; that's why I left it out of my innermost select.
I am trying to create a 12 month grid view of all questions that were submitting for each month in that 12 month period.
SELECT
YEAR(h.metaInsert) [Year],
MONTH(h.metaInsert) [Month],
DATENAME(MONTH,h.metaInsert) [Month Name],
COUNT(1) [Total Documents]
FROM
Document_Count_History AS h
WHERE
YEAR(h.metaInsert) = 2017
GROUP BY
YEAR(h.metaInsert), MONTH(h.metaInsert), DATENAME(MONTH, h.metaInsert)
ORDER BY
1, 2
This returns the data perfectly for the months that have it, but I get no data returned for those with 0 records for that specific month.
My goal is to see all 12 months along with the count of documents. If there are no documents, it will simply be a 0 for that month but it will be included in the result set.
How can I take what I have and apply the missing months?
You could use something like this to generate the sequence of months for your query:
declare #StartDate date = '20170101'
,#NumberOfYears int = 1;
;with Months as (
select top (12*#NumberOfYears)
[Month] = dateadd(Month, row_number() over (order by number) -1, #StartDate)
, NextMonth = dateadd(Month, row_number() over (order by number), #StartDate)
from master.dbo.spt_values
)
select
year(m.Month) [Year],
Month(m.Month) [Month],
datename(Month,m.Month) [Month Name],
count(h.*) [Total Documents]
from Months as m
left join Document_Count_History AS h
on h.metaInsert >= m.Month
and h.metaInsert < m.NextMonth
--where h.metaInsert >= '20170101'
group by m.Month
order by m.Month
Although you may want to consider adding a Calendar table, or Date Dimension.
Calendar and Numbers table references:
Generate a set or sequence without loops - 1 - 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
An example months table:
create table dbo.Months(
MonthStart date not null primary key
, NextMonthStart date not null
, [Year] smallint not null
, [Month] tinyint not null
, [MonthName] varchar(16) not null
);
declare #StartDate date = '20100101'
,#NumberOfYears int = 30;
insert dbo.Months(MonthStart,NextMonthStart,[Year],[Month])
select top (12*#NumberOfYears)
[MonthStart] = dateadd(month, row_number() over (order by number) -1, #StartDate)
, NextMonthStart = dateadd(month, row_number() over (order by number), #StartDate)
, [year] = year(dateadd(month, row_number() over (order by number) -1, #StartDate))
, [Month] = Month(dateadd(month, row_number() over (order by number) -1, #StartDate))
, MonthName = datename(Month,dateadd(month, row_number() over (order by number) -1, #StartDate))
from master.dbo.spt_values;
and your query would simplify to:
select
m.[Year],
m.[Month],
m.[MonthName],
count(h.*) [Total Documents]
from Months as m
left join Document_Count_History AS h
on h.metaInsert >= m.MonthStart
and h.metaInsert < m.NextMonthStart
where m.Year = 2017
group by m.Month, m.Year, m.MonthName
order by m.MonthStart
You need a date dimension. Specifically, you need a table that has all the values for months. Then, you can do a left-join on the table that gets the totals, and pull out a sum value.
This question already has answers here:
Left Join With Where Clause
(7 answers)
Closed 4 years ago.
Here is what I´m doing:
WITH cte AS (
SELECT * FROM TimeDim
)
SELECT t.TimeDimPK, c.ID
FROM CTE AS t
LEFT OUTER JOIN TABLE c ON c.TimeDimFK = t.TimeDimPK
ORDER BY t.TimeDimPK
WHERE c.ID = 1
Result, which is missing dates as shown below:
TimeDimPK ID
20120930 1
20121231 1
20130131 1
What I´m trying to get
TimeDimPK ID
20120930 1
20121031 NULL
20121130 NULL
20121231 1
20130131 1
It looks like your WHERE clause is getting rid of the other dates. Try this instead:
WITH cte
AS ( SELECT TimedimPK
FROM TimeDim
)
SELECT t.TimeDimPK ,
c.ID
FROM cte t
LEFT OUTER JOIN TABLEname c ON c.TimeDimFK = t.TimeDimPK
WHERE c.ID = 1
OR c.ID IS NULL
ORDER BY t.TimeDimPK
You can try to use a "number/date generator", this queries will fill in all missing days, then select the last day of the month. I am not sure about all your data details, so I am currently giving you two suggestions:
1 - Compact suggestion:
DECLARE #minDate DATETIME, #maxDate DATETIME;
SELECT #minDate = MIN(TimeDimPK), #maxDate = MAX(TimeDimPK) FROM TimeDim;
WITH DateGenerator AS
(
--Create a list with a lot of dates from #minDate
SELECT TimeDimPK = CONVERT(DATE, DATEADD(dd, ROW_NUMBER() OVER (ORDER BY OBJECT_ID), #minDate)) FROM sys.objects
)
--List all days, including missing days
SELECT TimeDimPK
, c.ID
FROM DateGenerator n
LEFT JOIN AnotherTable c ON c.TimeDimFK = n.TimeDimPK
WHERE
--Stop the number generator at the last day of the last month from the cte table
n.TimeDimPK <= CONVERT(DATE, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0, #maxDate )+1,0)))
--This will get the last day of every month
AND TimeDimPK = CONVERT(DATE, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,TimeDimPK)+1,0)))
2 - Suggestion with your cte clause, if you want to use it for some tweaking:
WITH NumberGenerator AS
(
--Create a number list 1,2,3,4,5,6,7,8,++
SELECT Number = ROW_NUMBER() OVER (ORDER BY OBJECT_ID) FROM sys.objects
), cte AS (
--Your cte query with a date number based on the days between the firts and current days
SELECT
TimeDimPK = CONVERT(DATETIME, TimeDimPK)
--Get the number of days to add from the first day in your table
, DateNumber = DATEDIFF(dd, MIN(TimeDimPK) OVER (), TimeDimPK)
FROM #TimeDim
), TableWithMissingDates AS
(
--Fill missing days
SELECT TimeDimPK = CONVERT(DATE, DATEADD(dd, n.Number - 1, MIN(t.TimeDimPK) OVER ()))
, c.ID
FROM NumberGenerator n
LEFT JOIN cte t ON t.DateNumber = n.Number
LEFT JOIN #Test2 c ON c.TimeDimFK = t.TimeDimPK
--Stop the number generator at the last day of the last month from the cte table
WHERE CONVERT(DATE, DATEADD(dd, n.Number - 1, (SELECT MIN(TimeDimPK) FROM cte))) < CONVERT(DATE, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0, (SELECT MAX(TimeDimPK) FROM cte) )+1,0)))
)
SELECT * FROM TableWithMissingDates
WHERE
--This will get the last day of every month
TimeDimPK = CONVERT(DATE, DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,TimeDimPK)+1,0)))
Both queries will return the same table:
Hi I have an SQL server database with 3 columns Activity[start_date(datetime),end_date(datetime),title(string)]
and I wish to count for a whole year, how many activities have start_date in each month, I mean I would like a return of 12 values(12 months) that count the activities within the months, thanks.
If there is an index on start_date and/or if you need to include months in the result even if there was no activity in that month, you might consider this one:
DECLARE #year INT;
SET #year = 2012;
;WITH n AS
(
SELECT TOP (12) m = DATEADD(MONTH, ROW_NUMBER() OVER
(ORDER BY name)-1, DATEADD(YEAR, #year-1900, 0))
FROM sys.all_objects ORDER BY name
)
SELECT [Month] = n.m, ActivityCount = COUNT(t.title)
FROM n
LEFT OUTER JOIN dbo.unspecified_table_name AS t
ON t.start_date >= n.m
AND t.start_date < DATEADD(MONTH, 1, n.m)
GROUP BY n.m
ORDER BY [Month];
(If you don't want a row when there were zero activities in a given month, then change LEFT OUTER to INNER.)
select month(start_date) as Month, count(*) as Count
from Activity
where year(start_date) = 2011
group by month(start_date)
order by month(start_date)