Thanks in advance for any help on this one.
Say I have a query that compares data across years, starting at some arbitrary year and never ending (going into the future), for the same period each year up to the last completed month (which has the characteristic that Jan data never shows until Feb 1). Say also, that one cannot use T-SQL. Is there a way to reformulate the following query to generate the dates dynamically starting from 2008/01/01 (or even just doing it for all years) and going forever without any hardcoding?
select
case
when oact.fathernum like '112%' then sum(jdt1.debit) - sum(jdt1.credit)
end as [Accounts Receivable],
jdt1.refdate as [Posting Date]
from jdt1
inner join oact on jdt1.account = oact.AcctCode
where (oact.fathernum like '1%')
and
(jdt1.refdate between '2008/01/01' and dateadd(day, -1, '2008/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2009/01/01' and dateadd(day, -1, '2009/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2010/01/01' and dateadd(day, -1, '2010/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2011/01/01' and dateadd(day, -1, '2011/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2012/01/01' and dateadd(day, -1, '2012/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2013/01/01' and dateadd(day, -1, '2013/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2014/01/01' and dateadd(day, -1, '2014/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2015/01/01' and dateadd(day, -1, '2015/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2016/01/01' and dateadd(day, -1, '2016/' + cast(month(getdate()) as varchar(2)) + '/01')
or jdt1.refdate between '2017/01/01' and dateadd(day, -1, '2017/' + cast(month(getdate()) as varchar(2)) + '/01'))
group by oact.fathernum, jdt1.refdate
Failing that, any one care to try their hand at a reformulation using T-SQL in a stored procedure that solves the problem? The date upper bound could always be the current year as long as it is dynamic.
The TSQL below shows a method of building a dynamic calendar table.
The query as shown changes the pivot date with each year, but further on is shown how you can fix the calendar 'start' date at a particular year.
select
case
when oact.fathernum like '112%' then sum(jdt1.debit) - sum(jdt1.credit)
end as [Accounts Receivable],
jdt1.refdate as [Posting Date]
from jdt1
inner join oact on jdt1.account = oact.AcctCode
inner join (select
FirstDayOfYear =DATEADD(m,datediff(m,0,getdate())-MONTH(getdate())+1,0),
FirstDayOfMonth =DATEADD(m,datediff(m,0,getdate()),0)) D
inner join master..spt_values v on v.type='P'
and v.number between 0 and 500 -- is 500 years enough? max=2047 from this table
on jdt1.refdate >= DATEADD(year,v.number,D.FirstDayOfYear)
and jdt1.refdate < DATEADD(year,v.number,D.FirstDayOfMonth)
where (oact.fathernum like '1%')
group by oact.fathernum, jdt1.refdate
The select creates a single record of 2 pivot dates, as named
inner join (select
FirstDayOfYear =DATEADD(m,datediff(m,0,getdate())-MONTH(getdate())+1,0),
FirstDayOfMonth =DATEADD(m,datediff(m,0,getdate()),0)) D
The 2 pivot dates are the first day of the **current year**, and the first day of the current month (also in the current year). If you need the first day of a **specific** year and the first day of month (current month) but in the same specific year, you can use the variation below (example for 2008-Jan-01)
select
FirstDayOfYear =cast('20080101' as datetime),
FirstDayOfMonth =dateadd(m,month(getdate())-1,'20080101')
This uses the pivot dates and the built-in number sequence to progressively add 1 year each time to the pivot dates, starting at adding 0 (for current year).
inner join master..spt_values v on v.type='P'
and v.number between 0 and 500
on jdt1.refdate >= DATEADD(year,v.number,D.FirstDayOfYear)
and jdt1.refdate < DATEADD(year,v.number,D.FirstDayOfMonth)
Notice also that instead of
date between A and B
I normally prefer
date >= A and date < B+1
Which works whether or not B includes time information. It doesn't matter for your query, but is good practice for consistence.
Start with a numbers table to generate datesets and join on it
This SO question does it for day sequences
Would somethign like this work?:
YEAR(jdt1.refdate) between 2008 and 2017
and
MONTH(jdt1.refdate) < MONTH(getdate())
If you are using SQL Server 2005+, you can simply build your calendar on the fly:
With MaxDate As
(
Select Max(refdate) As [Date]
From jdt1
)
, Calendar As
(
Select Cast( Cast(Year(GetDate())As char(4)) + '0101' As datetime ) As [StartDay]
, DateAdd(d, -1, Cast( Cast(Year(GetDate()) + 1 As char(4)) + '0101' As datetime ) )As [EndDay]
Union All
Select DateAdd(yyyy, 1, [StartDay])
, DateAdd(yyyy, 1, [EndDay])
From Calendar
Join MaxDate
On Year(DateAdd(yyyy, 1, [EndDay])) <= Year(MaxDate.[Date])
)
Select ...
From Calendar As C
Join jdt1
On jdt1.refdate Between C.StartDay And C.EndDay
Join oact
On oact.AcctCode = jdt1.account
Where oct.fathernum Like '%1'
Group By oact.fathernum, jdt1.refdate
Option ( MaxRecursion 0 );
In this solution, I started with today's Year and expanded out to the Year of the last refdate.
Related
Below is a list of attendance in a table. Every Month i would need to monitor the attendance based on the cut-off period starting 20/previous month - 19/current month.
Date Branch Attendance
13/04/2017 Branch A 5
14/04/2017 Branch A 5
22/04/2017 Branch A 5
30/04/2017 Branch A 5
17/05/2017 Branch A 6
18/05/2017 Branch A 6
01/04/2017 Branch B 17
15/04/2017 Branch B 14
20/04/2017 Branch B 14
19/05/2017 Branch B 17
20/05/2017 Branch B 15
25/05/2017 Branch B 17
For Example; I would like to show in a Month of May,where the cut-off period will be 20/4/2017 - 19/5/2017.
Below are my code:
SELECT
CONVERT(VARCHAR,Date,103) AS Date,
Branch,
Attendance
FROM
Table_attd
WHERE
Date>=DATEADD(DAY,(SELECT DATEDIFF (DAY,(DATEADD(DAY,0,GETEDATE())),
(DATEADD(MONTH,-1,CONVERT(DATETIME,CAST(YEAR(GETDATE()) AS
VARCHAR)+'/'+CAST(MONTH(GETDATE()) AS
VARCAHR)+'/'+'19',102))))),GETDATE())
AND Date<=DATEADD(DAY,-1,GETDATE())
The results:
Date Branch Attendance
22/04/2017 Branch A 5
30/04/2017 Branch A 5
17/05/2017 Branch A 6
18/05/2017 Branch A 6
20/04/2017 Branch B 14
19/05/2017 Branch B 17
But I realized that my code has a flaw where When the month of Jun (cut-off period will be 20/5/2017 - 19/6/2017), it result will be incorrect.
Kindly advice - Thanks!
Your query is not a correct syntax, you could use this
DECLARE #CurrentDate date = getdate()
DECLARE #20PreviousMonth date = dateadd(day,19,dateadd(month,datediff(month,0,#CurrentDate) - 1,0))
DECLARE #19CurrentMonth date = dateadd(day,18,dateadd(month,datediff(month,0,#CurrentDate) ,0))
SELECT
CONVERT(VARCHAR,[Date],103) AS Date,
Branch,
Attendance
FROM
Table_attd
WHERE
[Date] BETWEEN #20PreviousMonth AND #19CurrentMonth
...where [date] between dateadd(day, 19, dateadd(month, -1, cast(convert(char(6), getdate(), 112) + '01' as date)))
and dateadd(day, 18, cast(convert(char(6), getdate(), 112) + '01' as date))
An alternative way to solve this is to create a 'Date' table .
Date CalendarMonth ReportingMonth
2017-19-05 May May
2017-20-05 May June
Then simply left join this table to your fact table and group by 'ReportingMonth.' This allows you to quickly call on data from any time period, and allows for flexibility in the cut-over date.
Or you can try this.
DECLARE #previous DATETIME = CAST(DATEADD(DAY,
( DATEDIFF(DAY,
( DATEADD(DAY, 0,
GETDATE()) ),
( DATEADD(MONTH,
-1,
CONVERT(DATETIME, CAST(YEAR(GETDATE()) AS VARCHAR)
+ '/'
+ CAST(MONTH(GETDATE()) AS VARCHAR)
+ '/' + '19', 102)) )) ),
GETDATE()) AS DATE)
DECLARE #current DATETIME = CAST(DATEADD(DAY,
( DATEDIFF(DAY,
( DATEADD(DAY, 0,
GETDATE()) ),
( DATEADD(MONTH,
0,
CONVERT(DATETIME, CAST(YEAR(GETDATE()) AS VARCHAR)
+ '/'
+ CAST(MONTH(GETDATE()) AS VARCHAR)
+ '/' + '20', 102)) )) ),
GETDATE()) AS DATE)
SELECT [Date],
Branch ,
Attendance
FROM ( SELECT CONVERT(VARCHAR, Date, 103) AS [Date] ,
Branch ,
Attendance
FROM Table_attd
) T
WHERE ( t.Date >= #previous
AND t.Date <= #current
)
How can I get it to one statement that can get me
last month and this year?
I have a INSERT INTO and in a column report_date [datetime]
insert into table_a (report_date) values ( ??);
i want to show this past month and year,
So for example today is 4/21/2014
so it would show 3/2014 in the column
If today was MAY 1 2014 , it would show 4/2014?
Is this possible or does it have to have a day?
You're looking for the DATEADD function:
SELECT DATEPART(month, DATEADD(month, -1, GETDATE()), DATEPART(year, DATEADD(month, -1, GETDATE())
If you are trying to get 'last month' with 'this year' you could do:
SELECT CAST(
CAST(YEAR(GETDATE()) AS VARCHAR) + '-' +
CAST(MONTH(GETDATE())-1 AS VARCHAR) + '-' +
CAST(DAY(GETDATE()) AS VARCHAR)
AS DATETIME)
But then in JAN 2015, it would return DEC 2015.
If you just want last month, use the 'DATEADD' function.
SELECT DATEADD(MONTH, -1, GETDATE())
I have a table in SQL server that looks like this (there are other fields, but there are the only 2 I care about):
-----------------------------------------
| DraftDay (int)| MonthlyFee (money)|
-----------------------------------------
1 18.75
2 15.25
2 15.25
3 15.25
3 28.12
4 15.25
4 3.75
4 18.19
5 12.75
6 13.80
6 14.25
... ...
... ...
What I am trying to do is write a query that will group by DraftDay and sum the MonthlyFee field. Simple enough. But the hard part is, I need to get the date of one month ago, plus one day and pull the days from that day to the current day. For example, if today is 4/3/2014, then I need to pull all days between 3/4/2014 and 4/3/2014. My result set should look like this:
3/4/2014 37.19
3/5/2014 12.75
3/6/2014 28.05
... ...
... ...
4/1/2014 18.75
4/2/2014 30.50
4/3/2014 43.37
I have created this query:
SELECT
CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(CASE WHEN EftDraftDay>28 THEN 28 ELSE EftDraftDay END AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME) AS DraftDate,
SUM(MonthlyPayment) AS MonthlyPayment FROM dbo.Advertiser
WHERE IsDeleted=0
AND IsPaidInFull=0
AND IsAdvertiserActive=1
AND EftDraftDay>0
AND (CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(CASE WHEN EftDraftDay>28 THEN 28 ELSE EftDraftDay END AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME)) >=
CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(DATEPART(DAY, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME)
GROUP BY
CAST(CAST(DATEPART(MONTH, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(2)) + '/' +
CAST(CASE WHEN EftDraftDay>28 THEN 28 ELSE EftDraftDay END AS VARCHAR(2)) + '/' +
CAST(DATEPART(YEAR, DATEADD(DAY, 1, DATEADD(MONTH, -1, GETDATE()))) AS VARCHAR(4)) AS DATETIME)
Because today's date is 4/17/2014, I should be getting results back from 3/18/2014 thru 4/17/2014. The results I'm getting back are only from 3/18/2014 thru 3/28/2014. How can I also add 4/1/2014 thru 4/17/2014 into my result set? I thought about a UNION, but those don't seem to work well with GROUP BY's.
After trying to figure this out and looking at my query, I think I am totally over engineering this. My query looks so complex for such a simple thing. Can anyone please help me out with this?
Try this:
declare #lastmonth date = convert(date,dateadd(day,1,dateadd(month,-1,getdate())))
declare #thismonth date = convert(date,getdate())
select
case
when draftday < datepart(day,#thismonth)
then convert(date, cast(datepart(yyyy,#thismonth) as varchar) + right('00'+cast(datepart(MM,#thismonth) as varchar),2) + right('00'+cast(cast(draftday as varchar) as varchar),2))
else convert(date, cast(datepart(yyyy,#lastmonth) as varchar) + right('00'+cast(datepart(MM,#lastmonth) as varchar),2) + right('00'+cast(cast(draftday as varchar) as varchar),2))
end,
sum(monthlyfee) from tbl
group by draftday
This will treat days before current day as belonging to current month, and others as from last month, and then sum up. Please let me know if this is doing what you expect.
I'd use the Calendar table (http://www.dbdelta.com/calendar-table-and-datetime-functions/), something like this:
declare #d datetime ='20140403'
select c.CalendarDate, sum(#t.MonthlyFee)
from calendar c
left join #t on c.CalendarDay = #t.DraftDay
where CalendarDate between dateadd(month, -1, #d)+1 and #d
group by c.CalendarDate
I would like select data between two date, without day
An input example:
start month: 9 , start year: 2011
end month: 3, end year: 2012
I think that there are two way to do this.
The first is convert start month and start year to date like 2011-09-01 and convert last date to 2012-03-31, but this requires calculation of the last day of end month. Obtained these date we can use a BEETWEN function for the WHERE clause (but, is the CONVERT function reliable?)
The second solution is to use the DATEPART function like in the following code:
I try to explain: if end year is equal to the initial year, then month must be between the start and end months; else if the final months is greater than the initial years if different from the initial and final year, I take everything in between; else if the final year, the month must be less than or equal to the final month, if the initial year, month must be greater than or equal to the final month
Can you help me do this in the best way? Is correct, the solution I adopted?
declare #IndDebitoCredito bit,#ProgTributo int,#mi as integer,#ai as integer,#mf as integer,#af as integer,#IDAnagrafica varchar(5)
select #mi = 01,#ai = 2011,#mf = 12,#af = 2011,#IDAnagrafica = 'DELEL',#IndDebitoCredito = 1
select distinct rrd.IDTributo
from TBWH_Delega d
--inner join TBWH_SezioneDelega sd on d.IDDelega = sd.IDDelega
inner join TBWH_Rigo rd on rd.IDDelega = d.IDDelega
inner join TBWH_RataRigo rrd on rrd.IDRigo = rd.IDRigo
where
(
DATEPART(MM,d.DataDelega)<=#mf and
DATEPART(MM,d.DataDelega)>=#mi and
DATEPART(YYYY,d.DataDelega)=#ai and
#af = #ai
)
OR
(
--anno finale magg. anno iniziale
#af > #ai AND
(
( -- delega nell'intervallo
DATEPART(YYYY,d.DataDelega)<#af AND
DATEPART(YYYY,d.DataDelega)>#ai
-- DATEPART(MM,d.DataDelega)>=#mi
)
OR
( -- delega limite destro
DATEPART(YYYY,d.DataDelega)=#af AND
DATEPART(MM,d.DataDelega)<=#mf
)
OR
( -- delega limite sinistro
DATEPART(YYYY,d.DataDelega)=#ai AND
DATEPART(MM,d.DataDelega)>=#mi
)
)
)
GO
Your first solution is almost there, but is more complicated than it needs to be and won't work anyway. It will miss out any rows from the last day of the end month.
You can add one month to the end month and then use BETWEEN on the first of each month. eg.
start month: 9 , start year: 2011
end month: 3, end year: 2012
BETWEEN '2011-09-01' AND '2012-04-01'
or, as JNK points out, this will be better:
DataDelega >= '2011-09-01' AND DataDelega < '2012-04-01'
You'll need to add in some logic to deal with the end month being December, but this looks like the simplest way of doing it.
You are WAY overcomplicating this. You really only need two comparisons:
Is the month and year after or equal to the initial value?
Is the month and year before or equal to the final value?
Try:
SELECT *
FROM MyTable
WHERE Datefield BETWEEN
CAST(#mi as varchar) + '/1/' + CAST(#ai as varchar)
-- first of first month
AND
DATEADD(DAY, -1, (DATEADD(Month, + 1, (CAST(#mf as varchar) + '/1/' + CAST(#af as varchar)))))
-- Last day or final month
SELECT *
FROM Table
WHERE DateField
BETWEEN CONVERT(DATE, CONVERT(CHAR(4), #ai) + RIGHT('00' + CONVERT(VARCHAR(2), #mi), 2) + '01', 112)
AND DATEADD(DD, -1, DATEADD(MM, 1, CONVERT(DATE, CONVERT(CHAR(4), #af) + RIGHT('00' + CONVERT(VARCHAR(2), #mf), 2) + '01', 112)))
Avoid using expressions on the DateField columns, as it makes query not SARGable.
I would use:
WHERE DateToCheck >= --- first day of StartMonth
DATEADD( mm, #StartMonth-1,
DATEADD( yy, #StartYear-2000, '2000-01-01')
)
AND DateToCheck < --- first day of next month (after EndMonth)
DATEADD( mm, #EndMonth,
DATEADD( yy, #EndYear-2000, '2000-01-01')
)
DECLARE #mi INT
, #ai INT
, #mf INT
, #af INT
SELECT #mi = 01
, #ai = 2011
, #mf = 12
, #af = 2011
--local variables to hold dates
DECLARE #i DATETIME
, #f DATETIME
--build strings to represent dates in YYYYMMDD format
--add a month to the #f date
SELECT #i = CONVERT(VARCHAR(4), #ai) + RIGHT('0' + CONVERT(VARCHAR(2), #mi),
2) + '01'
, #f = DATEADD(month, 1,
CONVERT(VARCHAR(4), #af) + RIGHT('0'
+ CONVERT(VARCHAR(2), #mf),
2) + '01')
--select data where date >= #i, and < #f
SELECT *
FROM MyTable
WHERE DateField >= #i
AND DateField < #f
I want to select all the records in a table where their date of entry is older then 2 months.
Any idea how I can do that?
I haven't tried anything yet but I am on this point:
SELECT COUNT(1) FROM FB WHERE Dte > GETDATE()
If you are using SQL Server try this:
SELECT * FROM MyTable
WHERE MyDate < DATEADD(month, -2, GETDATE())
Based on your update it would be:
SELECT * FROM FB WHERE Dte < DATEADD(month, -2, GETDATE())
Would something like this work for you?
SELECT * FROM FB WHERE Dte >= DATE(NOW() - INTERVAL 2 MONTH);
TSQL, Alternative using variable declaration. (it might improve Query's readability)
DECLARE #gapPeriod DATETIME = DATEADD(MONTH,-2,GETDATE()); --Period:Last 2 months.
SELECT
*
FROM
FB as A
WHERE
A.Dte <= #gapPeriod; --only older records.
SELECT COUNT(1) FROM FB
WHERE Dte > DATE_SUB(now(), INTERVAL 2 MONTH)
I use this on SQL Server:
SELECT
DATEADD(MONTH,-2,GETDATE()) '- 2 months'
FROM MyTable
SELECT COUNT(1)
FROM FB
WHERE
Dte BETWEEN CAST(YEAR(GETDATE()) AS VARCHAR(4)) + '-' + CAST(MONTH(DATEADD(month, -1, GETDATE())) AS VARCHAR(2)) + '-20 00:00:00'
AND CAST(YEAR(GETDATE()) AS VARCHAR(4)) + '-' + CAST(MONTH(GETDATE()) AS VARCHAR(2)) + '-20 00:00:00'