SELECT DISTINCT Campaign_id
FROM Impressions
WHERE Date BETWEEN '2015-03-01' AND '2015-03-31' ;
The above query gives me the result for the Campaign_id's that have been active on any date between 2015-03-01 and 2015-03-31.
I want the result set to contain the campaign_id's if the have been active on all the dates in between 2015-03-01 and 2015-03-31.
How would I go about this?
Assuming DATE is a DATE datatype and has no time component.
DECLARE #Start DATE = '2015-03-01',
#End DATE = '2015-03-31'
SELECT Campaign_id
FROM Impressions
WHERE Date BETWEEN #Start AND #End
GROUP BY Campaign_id
HAVING COUNT(DISTINCT Date) = 1 + DATEDIFF(DAY, #Start, #End);
Or a version without the variables
SELECT Campaign_id
FROM Impressions
CROSS APPLY (VALUES ({ d '2015-03-01' },
{ d '2015-03-31' })) V([Start], [End])
WHERE [Date] BETWEEN [Start] AND [End]
GROUP BY Campaign_id, [Start], [End]
HAVING COUNT(DISTINCT Date) = 1 + DATEDIFF(DAY, [Start], [End]);
Using HAVING clause with COUNT(DISTINCT):
SELECT Campaign_id
FROM Impressions
WHERE Date between '2015-03-01' and '2015-03-31'
GROUP BY Campaign_id
HAVING COUNT(DISTINCT Date) = 31;
You should also review this blog post by Aaron Betrand to understand why using BETWEEN for dates is a bad idea.
You can make arrange the query to only mention the dates once by doing something like:
WITH params as (
SELECT CAST('2015-03-01' as DATE) as date1, CAST('2015-03-31' as DATE) date2
)
SELECT i.Campaign_id
FROM params CROSS JOIN
Impressions i
WHERE i.Date >= params.Date1 and i.Date < DATEADD(day, 1, params.Date2)
GROUP BY i.Campaign_id, params.date1, params.date2
HAVING COUNT(DISTINCT i.Date) = 1 + DATEDIFF(day, params.date1, params.date2);
Note: Some would prefer a JOIN to a CROSS JOIN in this case. By habit, I always put a parameters CTE in a query using CROSS JOIN.
Related
How to split record with start- and enddate into separate records in SQL?
Example:
Start Date End Date
05/09/2000 12/31/2002
The result should be:
Start Date End Date
05/09/2000 12/31/2000
01/01/2001 12/31/2001
01/01/2002 12/31/2002
You can use a recursive CTE. In SQL Server, this would look like:
with cte as (
select start_date, end_date
from t
union all
select datefromparts(year(start_date) + 1, 1, 1), end_date
from cte
where datediff(year, start_date, end_date) > 0
)
select start_date,
(case when datediff(year, start_date, end_date) > 0
then datefromparts(year(start_date), 12, 31)
else end_date
end)
from cte;
Here is a db<>fiddle.
Just another option using an ad-hoc tally table
Example
Declare #YourTable Table ([Start Date] date,[End Date] date) Insert Into #YourTable Values
('05/09/2000','12/31/2002')
Select B.*
From #YourTable A
Cross Apply (
Select [Start Date] = min(D)
,[End Date] = max(D)
From ( Select Top (DateDiff(DAY,[Start Date],[End Date])+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),[Start Date])
From master..spt_values n1,master..spt_values n2
) B1
Group By Year(D)
) B
Returns
Start Date End Date
2000-05-09 2000-12-31
2001-01-01 2001-12-31
2002-01-01 2002-12-31
I have the challenge that I have a table with start and end date of an event:
Event Start End
A 01Jan2018 01Mar2018
B 01Feb2018 01Apr2018
I would like to have a table as output with a group by of active events in a month:
Year Month count_active_events
2018 1 1
2018 2 2
2018 3 2
2018 4 1
Can anyone think of a SQL statement that makes this request feasible?
THX
Lazloo
One method generates the dates and then does the calculation using a correlated subquery or apply:
with dates as (
select cast('2018-01-01' as date) as dte
union all
select dateadd(month, 1, dte)
from dates
where dte < '2018-04-01'
)
select d.dte,
(select count(*)
from t
where t.start <= d.dte and t.end >= d.dte
) as num_active
from dates d;
Try this:
declare #tbl table([Event] char(1), [Start] date , [End] date);
insert into #tbl Values
('A' , '01Jan2018', '01Mar2018'),
('B' , '01Feb2018', '01Apr2018');
declare #start date,
#end date;
select #start = min([Start]), #end = max([End]) from #tbl;
with cte as(
select #start dt from #tbl
union all
select dateadd(month, 1, dt) from cte
where dateadd(month, 1, dt) <= #end
)
select datepart(month, c.dt), datepart(year, c.dt), count(distinct [Event]) from cte c
left join #tbl t on c.dt between t.Start and t.[End]
group by datepart(month, c.dt), datepart(year, c.dt)
You need apply with recursive cte to generate start event dates :
with tt1 as (
select event, cast(start as date) start, cast([end] as date) [end]
from table
union all
select event, dateadd(month, 1, start), [end]
from tt1
where start < [end]
)
select year(tt.dt), month(tt.dt), tt1.cnt
from table t cross apply
( values (start), ([end])
) tt (dt) outer apply
( select count(*) cnt
from tt1
where year(tt1.start) = year(tt.dt) and
month(tt1.start) = month(tt.dt)
) tt1;
I am trying to get the 30 days report of users, that will return date and total count of users as count created on that date and i did it with this query
Select count(*) As [Count] ,
(SELECT CONVERT(date, AddDate)) As [Date]
from Users
WHERE AddDate >= (SELECT DateAdd(month, -1, Convert(date, GetDate())))
Group By CONVERT(date, AddDate)
it give me only those dates on which any user is created, but i want to show all 30 days either if it has count 0.
Same Case i want to do with monthly report.
i am getting months in which users are created , now i want to change it to get last 12 months from this month and their total users count. For this i am using this query
Select count(*) As [Count] ,
(Select DATEPART( month , DateAdd( month , DATEPART(mm,AddDate) , -1 ) )) as Month
from Users
WHERE AddDate >= (SELECT DateAdd(YEAR, -1, Convert(date, GetDate())))
Group By DATEPART(mm,AddDate)
Using a Calendar CTE:
With NumberSequence ( Number ) as
(
Select 1 as Number
union all
Select Number + 1
from NumberSequence
where Number <= 30
)
, CalendarCTE as
(
select cast(dateadd(dd, -30 + Number,getdate()) as Date) as CalDate
from Numbersequence
)
select CalDate, count(U1.*) as CountUsers
from CalendarCTE
left join Users U1
on CalDate = convert(date, U1.AddDate)
group by CalDate
As I mentioned in comment, You need a Calendar table and Left Join
SELECT Count(u.adddate) AS [Count],
c.dates AS [Date]
FROM calendar_table C
LEFT JOIN users U
ON c.dates = CONVERT(DATE, adddate)
WHERE c.dates >= Dateadd(month, -1, CONVERT(DATE, Getdate()))
GROUP BY c.dates
To generate/create a calendar table or dates check out the below questions
How to generate a range of dates in SQL Server
Generate Dates between date ranges
How to create a Calender table for 100 years in Sql
Try this script :
WITH CTEDates
AS
(
SELECT CAST(GetDate() as date) AS [date]
UNION ALL
SELECT DATEADD(dd, 1, [date])
FROM CTEDates
WHERE DAY([date]) <= 30
)
Select count(*) As [Count] ,CONVERT(date, AddDate) As [Date]
from CTEDates
LEFT JOIN Users ON CTEDates.date=CONVERT(date, AddDate)
WHERE AddDate >= DateAdd(month, -1, GetDate())
Group By CONVERT(date, AddDate)
DECLARE #StartDate Datetime
DECLARE #EndDate Datetime
CREATE TABLE #tMyCalanderDate (dtDate Datetime Primary key)
SELECT #StartDate = '01-Sep-2016'
SELECT #EndDate = '30-Sep-2016'
WHILE #StartDate <= #EndDate
BEGIN
INSERT INTO #tMyCalanderDate (dtDate)
SELECT #StartDate
SELECT #StartDate = DATEADD(day,1,#StartDate)
END
SELECT count(A.UserID) As [Count] , B.dtDate As [Date]
FROM Users AS A
RIGHT JOIN #tMyCalanderDate AS B ON CONVERT(date, A.AddDate) = CONVERT(date, B.dtDate)
WHERE CONVERT(date, A.AddDate) BETWEEN #StartDate AND #EndDate
Group By CONVERT(date, B.dtDate)
You can use a CTE to get a thirty day calendar. Then left join your Users table to it.
DECLARE #CurrentTime DATETIME = GETDATE()
;WITH CTE AS
(
SELECT CONVERT(DATE, #CurrentTime) AS [Date]
UNION ALL
SELECT DATEADD(dd, -1, Date)
FROM CTE
WHERE DATEADD(dd, 29, Date) > #CurrentTime
)
SELECT COUNT(U.AddDate) AS [Count]
, CTE.[Date] AS [Date]
FROM CTE
LEFT JOIN users U
ON CTE.Date = CONVERT(Date, AddDate)
GROUP BY CTE.Date
You can use a similar CTE to get the twelve month calendar and use the same joins to get the count.
HTH.
I am trying to retrieve the range for the value field over each 24hr period over a predefined time period. The issue is that the time period is between 7am and 7am the next day. (not a daily figure)
For example, I would like the range for day 1, then range for day 2, etc. I've tried using the below query but the production field keeps coming back with the same data, could anyone please shed some light on how I can make this work?
Thank you very much.
select tagname, convert(date,datetime),
(
select (max(Value)-min(Value)) as Range
from Runtime.dbo.AnalogHistory
where (TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input')
and DateTime BETWEEN dateadd(hh,7,convert(datetime,convert(date,datetime))) AND dateadd(hh,31,convert(datetime,convert(date,datetime)))
) as Production
from runtime.dbo.analoghistory
where (TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input')
and datetime between '20151101' and '20151201'
group by tagname, convert(date,DateTime)
I would like to result to be as per below
tagname | date | production
Below is one method that uses CTEs to generate a row for each date in the specified range, and then joins to the table on the date plus a 7 hour offset. Consider creating a utility calendar table for this type of task.
DECLARE
#StartDate date = '20151101'
, #EndDate date = '20151201';
WITH
t8 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
, t512 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS num FROM t8 AS a CROSS JOIN t8 AS b CROSS JOIN t8 AS c)
, dates AS (SELECT DATEADD(day, num, #StartDate) AS Date FROM t512 WHERE num <= DATEDIFF(day, #StartDate, #EndDate))
SELECT TagName, Date, MAX(Value)-MIN(Value) as Production
FROM dates
JOIN dbo.AnalogHistory ON
AnalogHistory.DateTime >= DATEADD(hour, 7, Date)
AND AnalogHistory.DateTime < DATEADD(hour, 31, Date)
GROUP BY TagName, Date
ORDER BY TagName, Date;
Your subquery is not correlated to the outer query, so it is no surprise that it returns the same value. I think you want something like this:
select tagname, convert(date, datetime),
(select (max(ah2.Value) - min(ah2.Value)) as Range
from Runtime.dbo.AnalogHistory ah2
where ah2.TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input' and
ah2.DateTime BETWEEN dateadd(hour, 7, convert(datetime, convert(date, ah.datetime))) AND
dateadd(hour, 31, convert(datetime, convert(date, ah.datetime)))
) as Production
from runtime.dbo.analoghistory ah
where TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input' and
datetime between '20151101' and '20151201'
group by tagname, convert(date, DateTime);
Note the use of ah2 and ah in the subquery.
I have a question. I have a SQL Server 2008 table with a field column. I have for example the Following dates:
1/1/2001
5/5/2004
8/5/2009
10/7/2011
5/5/2012
1/13/2014
Id like to be able to show all dates >= the current date (7/29/2011) as well as largest table date that is < current date. In this example, the result would be all dates >= 8/5/2009.
Can someone help guide me please??
select max(date) [date] from table where date < getdate()
union
select date from table where date >= getdate()
If I understand correctly, you want to include the date prior to the current date. GETDATE() will get the current date (with time). If you're alright with that, then this should work. Otherwise, you may have to parse out just the date from GETDATE()
SELECT TheDate
FROM DateTable
WHERE TheDate >= (SELECT MAX(TheDate) FROM DateTable WHERE TheDate < GETDATE())
This gets all dates greater than or equal to the most recent date before the current date.
I am not entirely sure I understand, but this looks like a BETWEEN the relevant dates. Or is there something more I am missing?
Assuming your table is called DateTable and your field is called TheDate, do it like this:
SELECT TheDate
FROM DateTable
WHERE TheDate >= DATEADD(d, -2, GETDATE())
Good luck!
It depends on the SQL server you're using. In postgres, for example, you need something like
SELECT fields FROM table WHERE date_field >= CURRENT_DATE - 1
But other SQL servers have different ways to specify "yesterday"
SELECT d1.*
FROM dates d1
LEFT JOIN dates d2 ON d1.Date < d2.Date AND d2.Date < GETDATE()
WHERE d2.Date IS NULL
Explanation:
Select every date for which there does not exist a date that is both earlier than today and later than the one being inspected.
Lots of guessing here based on loose narrative and unknown data types.
DECLARE #t TABLE(d DATE);
INSERT #t SELECT '20010101'
UNION ALL SELECT '20040505'
UNION ALL SELECT '20090805'
UNION ALL SELECT '20111007'
UNION ALL SELECT '20120505'
UNION ALL SELECT '20140113';
DECLARE #now DATE = SYSDATETIME();
WITH t AS
(
SELECT d, rn = ROW_NUMBER() OVER (ORDER BY d)
FROM #t
)
SELECT t.d
FROM t LEFT OUTER JOIN t AS x
ON t.rn = x.rn - 1
WHERE COALESCE(x.d, #now) >= #now
ORDER BY t.d;