I've got two fields, Period and YR with period representing the month. I would like to be able to select these records as a date field with the assumption that the day is the last day of the month. For example:
YR Period Date
2017 1 2017-01-31
2017 2 2017-02-28
Etc
I'm at a loss on how to go about doing this. Thanks for any help you can provide.
SELECT YR, Period, EOMONTH(DATEFROMPARTS(YR, Period, 1)) AS [Date]
FROM <<table>>
This is how you could do it using "basic" functions:
; with data as (
select 2017 as yr, 1 as period
union all
select 2017 as yr, 2 as period
)
, temp as (
select
yr,
period,
dateadd(day, -1, dateadd(month, 1,
convert(datetime, cast(yr*10000 + period*100 + 1 as varchar))
)
) as test
from data
)
select yr, period, convert(varchar(10), test, 126) from temp
Here is another query:
WITH tb(YR,Period)AS(
SELECT 2017,1 UNION ALL
SELECT 2017,2 UNION ALL
SELECT 2017,3
)
SELECT DATEADD(DAY,-1,DATEADD(MONTH,tb.Period+1, DATEADD( YEAR, YR-1900,0))) FROM tb
-----------------------
2017-02-28 00:00:00.000
2017-03-31 00:00:00.000
2017-04-30 00:00:00.000
Related
Good Day! I am working on a chart where I need to display all the days of the current week to show the sales per Week. So far, I am able to display all the days of the current week, I'm just having a trouble in displaying the sales for each day of the week.Since there are no records in the database for the days of the week, it the TOTAL_SALES column should all return a Null value. Instead, it returns the total sales recorded in the database. Here is my Stored Procedure query so far.
WITH DAYSOFTHEWEEK AS
(
SELECT 0 DAY
UNION ALL
SELECT DAY + 1 FROM DAYSOFTHEWEEK WHERE DAY < 6
)
SELECT DATEADD(DAY, DAY, DATEADD(DAY, 2-DATEPART(WEEKDAY, CONVERT (date, GETDATE())), CONVERT (date, GETDATE()))) AS DAY_OF_THE_WEEK,
SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES
FROM DAYSOFTHEWEEK, [ORDER]
GROUP BY DAYSOFTHEWEEK.DAY
I tried adding this condition statement,
WHERE DAYSOFTHEWEEK.DAY IN ([ORDER].ORDER_DATE)
But it returns this error
Operand type clash: date is incompatible with int
Can someone help me out on this?Is there a work around with the code that I already have? Thanks in advance!
What I think you're after is a SUM of each day's sales for the current week with NULL if there are no sales. The secret is to left join your date list onto your data:
-- Setup some fake sales data
WITH TestData(N, Order_Date, Net_Amount) AS (
SELECT 1 N, CAST(GETDATE() AS DATE) Order_Date, RAND() * 100 Net_Amount
UNION ALL
SELECT N+1 N, CAST(GETDATE()-N/5 AS DATE) Order_Date, RAND(CHECKSUM(NEWID())) * 100 Net_Amount FROM TestData
WHERE N < 20
)
SELECT TestData.Order_Date, TestData.Net_Amount INTO #Order FROM TestData
--Set the first day of the week (if required)
SET DATEFIRST 7 --Sunday
;WITH Days(N,DayOfTheWeek) AS (
SELECT 1 N, DATEADD(DAY, 1-DATEPART(WEEKDAY, GETDATE()), CONVERT(DATE,GETDATE())) DayOfTheWeek
UNION ALL
SELECT N+1 N,DATEADD(DAY, 1, DayOfTheWeek) DayOfTheWeek FROM Days
WHERE N < 7
)
SELECT d.DayOfTheWeek, SUM(Net_Amount) TotalAmount
FROM Days d
LEFT JOIN #Order ON d.DayOfTheWeek = Order_Date
GROUP BY d.DayOfTheWeek
DayOfTheWeek TotalAmount
------------ ----------------------
2016-08-07 219.036784917497
2016-08-08 273.319570812461
2016-08-09 271.148114731087
2016-08-10 194.780039228967
2016-08-11 NULL
2016-08-12 NULL
2016-08-13 NULL
Here is every day this week, starting at your datefirst date, which can be temporarily varied for the query with SET DATEFIRST if you need to have some other week start date
I think you have some sales table there that you haven't shown us, you need to join to that on date, then group by
WITH DAYSOFTHEWEEK AS
(
SELECT cast(dateadd(
day,
-datepart(weekday,getdate()) + 1 ,
GETDATE()
)
as date) [DAY], 0 as cnt
UNION ALL
SELECT dateadd(day,1,[DAY]), cnt + 1 FROM DAYSOFTHEWEEK WHERE cnt < 6
)
select DAYSOFTHEWEEK.[day], SUM([ORDER].NET_AMOUNT) AS TOTAL_SALES from daysoftheweek
JOIN
SalesTable on
CAST(SalesTable.SalesDate date) = DAYSOFTHEWEEK.[day]
GROUP BY DAYSOFTHEWEEK.[day]
A little over complicated for me:
To get name of the week use, for example
SELECT DATENAME(dw,getdate())
But you really need something like this:
SELECT ProductName,Sum(Sales) From NameOfTable GROUP BY
DATENAME(ww,salesDate)
I have an query that I'm feeling out-of-my depth with.
I need to loop through months between two dates and return a subset of data for each month with a blank row for months with no data.
For example:
TransactionID | Date | Value
1 | 01/01/2015 | £10
2 | 16/01/2015 | £15
3 | 21/01/2015 | £5
4 | 15/03/2015 | £20
5 | 12/03/2015 | £15
6 | 23/04/2015 | £10
Needs to return:
Month | Amount
January | £30
February | £0
March | £35
April | £10
My query will rely on specifying a date range so I can set the first and last date of the query.
I feel like I maybe over thinking this, but have gotten to that stage where you start to feel like you tying yourself in knots.
The key is having access to a list of integers to represent the months in the range. If you don't have a Numbers Table, then spt_values will do in a pinch.
SqlFiddle Demo
SELECT
[Year] = YEAR(DATEADD(month,[i],#range_start))
,[Month] = DATENAME(month,DATEADD(month,[i],#range_start))
,[Amount] = ISNULL(SUM([Value]),0)
FROM (
SELECT TOP (DATEDIFF(month,#range_start,#range_end)+1)
ROW_NUMBER() OVER(ORDER BY (SELECT 1))-1 [i]
FROM master.dbo.spt_values
) t1
LEFT JOIN #MyTable t2
ON (t1.[i] = DATEDIFF(month,#range_start,t2.[Date]) )
GROUP BY [i]
ORDER BY [i]
SQL is a tricky language at first. You actually do not want a loop. In fact, you pretty much never want to loop in SQL except in very few cases. Try this out:
DECLARE #StartDate DATE,
#EndDate DATE;
SET #StartDate = '01 January 2015';
SET #EndDate = '30 April 2015';
WITH CTE_Months
AS
(
SELECT #StartDate dates
UNION ALL
SELECT DATEADD(MONTH,1,dates)
FROM CTE_Months
WHERE DATEADD(MONTH,1,dates) < #EndDate
)
SELECT YEAR(B.[date]) AS yr,
DATENAME(MONTH,B.[Date]) AS month_name,
SUM(ISNULL(B.Value,0)) AS Amount
FROM CTE_Months A
LEFT JOIN yourTable B
ON YEAR(A.[date]) = YEAR(B.[date])
AND MONTH(A.[date]) = MONTH(B.[date])
GROUP BY YEAR(B.[date]),DATENAME(MONTH,B.[Date])
One way: create a table called months with a monthnum int field and 12 rows of [1..12]
declare #start date = '01 jan 2015',
#end date = '30 apr 2015'
select
datename(month, dateadd(month, monthnum, 0) - 1),
isnull(Amount, 0)
from months
left join (
select
month(date) Month,
sum(Value) Amount
from tbl
where date between #start and #end
group by month(date)
) T on (T.Month = months.monthnum)
where months.monthnum between month(#start) and month(#end)
order by monthnum
The following code will generate one output row for each month between the first and last transaction dates. Spanning a year boundary, or multiple years, is handled correctly.
-- Some sample data.
declare #Transactions as Table
( TransactionId Int Identity, TransactionDate Date, Value Int );
insert into #Transactions ( TransactionDate, Value ) values
( '20141125', 10 ), ( '20150311', 20 ), ( '20150315', 5 ), ( '20150509', 42 );
select * from #Transactions;
with
-- Determine the first and last dates involved.
Range as (
select Min( TransactionDate ) as FirstDate, Max( TransactionDate ) as LastDate
from #Transactions ),
-- Generate a set of all of the months in the range.
Months as (
select DateAdd( month, DateDiff( month, 0, FirstDate ), 0 ) as Month,
DateAdd( month, DateDiff( month, 0, LastDate ), 0 ) as LastMonth
from Range
union all
select DateAdd( month, 1, Month ), LastMonth
from Months
where Month < LastMonth )
-- Summarize the transactions.
select M.Month, Coalesce( Sum( T.Value ), 0 ) as Total
from Months as M left outer join
#Transactions as T on DateAdd( month, DateDiff( month, 0, T.TransactionDate ), 0 ) = M.Month
group by M.Month
order by M.Month
option ( MaxRecursion 1000 );
I'm trying to get all months in which no transaction is placed for the same year (If different years is not possible)
This is my query to get transactions between 2 dates, but don't know how can I select only months for which transaction in database is missing
SELECT *
FROM Installment
WHERE OrderId = 1
AND InstallmentDate
BETWEEN cast('8/02/2014' as date)
AND cast('12/25/2014' as date)
InstallmentId OrderId CustomerKey InstallmentAmount InstallmentDate
18 1 INS-1 3000 2014-09-03
92 1 INS-1 3000 2014-10-13
137 1 INS-1 3000 2014-11-05
in this case record for the 12th month and 8th month is missing, how can I get this with SQL Server Query ?
Update
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is not null;
Gordon's query is returning all the years and months from table between 2 dates, just by changing i.OrderId is null to i.OrderId is not null here is the out of his query
yy mm
2014 9
2014 10
2014 11
Expected Output (if possible)
yy mm
2014 12
2015 1
Using the following recursive CTE:
DECLARE #start DATE = '2014-09-02'
DECLARE #end DATE = '2015-01-15'
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
you can get a list of all Years/Months between the two dates of interest:
Year Month
==============
2014 9
2014 10
2014 11
2014 12
2015 1
Using EXCEPT on the above CTE:
;WITH IntervalDates (date)
AS
(
SELECT #start
UNION ALL
SELECT DATEADD(MONTH, 1, date)
FROM IntervalDates
WHERE DATEADD(MONTH, 1, date)<=#end
)
SELECT YEAR(date) AS Year, MONTH(date) AS Month
FROM IntervalDates
EXCEPT
SELECT DISTINCT YEAR(InstallmentDate) AS yy, MONTH(InstallmentDate) AS mm
FROM Installment
WHERE OrderId = 1 AND InstallmentDate BETWEEN #start AND #end
yields the required result set:
Year Month
=============
2014 12
2015 1
To do this in SQL, you need to start with a list of months. Assuming you have at least one record for each month in the table, you can then get the missing dates easily using a subquery. The rest of the query is just a left join and checking for non-matches:
select yymm.yy, yymm.mm
from (select distinct year(InstallmentDate) as yy, month(InstallmentDate) as mm
from Installment
where InstallmentDate BETWEEN '2014-09-02' and '2015-01-15'
) yymm left join
Installment i
on i.OrderId = 1 and
year(i.InstallmentDate) = yymm.yy and
month(i.InstallmentDate) = yymm.mm
where i.OrderId is null;
Simplest way I can think of is to have a date dimension table that contains (at least) date, and 1st of month then. For creating one take a look at something like https://dba.stackexchange.com/questions/74957/best-approach-for-populating-date-dimension-table , although that one doesn't have firstOfMonthDate in it as my example show but the idea is the same.
then your query becomes
SELECT DISTINCT
firstOfMonthDate
FROM
dateRef dr
LEFT OUTER JOIN
InstallmentDate i ON dr.date = i.InstallmentDate AND i.OrderId = 1
WHERE
i.InstallmentDate IS NULL
AND
dr.date BETWEEN #startDate and #endDate
change firstOfMonthDate for fiscal month etc. as required. This would work across any range of dates you have in your table so different years wouldn't be an issue.
Try the below script. I retrieve all dates between the specified dates and use a LEFT JOIN to get those which are not present in your table:
DECLARE #startDate AS DATETIME, #endDate AS DATETIME
DECLARE #dates AS TABLE (CurrentDate DATETIME)
SET #startDate = '2014-01-01'
SET #endDate = '2014-01-31';
with GetDates AS
(
SELECT #startDate AS TheDate
UNION ALL
SELECT DATEADD("DD", 1, TheDate) FROM GetDates
WHERE TheDate < #endDate
) INSERT INTO #dates SELECT TheDate FROM GetDates
OPTION (MAXRECURSION 0)
SELECT DISTINCT YEAR(d.CurrentDate), MONTH(d.CurrentDate) FROM #dates d
LEFT JOIN InstallmentDate i ON i.InstallmentDate BETWEEN #startDate AND #endDate AND OrderId = 1
WHERE i.InstallmentDate IS NULL
Hope this helps...
In a view have these two dates coming from a table:
2014-12-17 14:01:03.523 - 2014-12-20 09:59:28.783
I need to know the date diff in hours assuming that in a day i can count the hours just from 08 AM and 5 PM.
Of course saturdays and sundays must not be included.
I tried using this code inside the select but i only got the diff in days, excluding saturdays and sundays.
(DATEDIFF(HOUR, convert(datetime,t.EXT_DATAINS-2), convert(datetime,b.EXT_DATAINS-2)) + 1)
-(DATEDIFF(wk, convert(datetime,t.EXT_DATAINS-2), convert(datetime,b.EXT_DATAINS-2)) * 2)
-(CASE WHEN DATENAME(dw, convert(datetime,t.EXT_DATAINS-2)) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(dw, convert(datetime,b.EXT_DATAINS-2)) = 'Saturday' THEN 1 ELSE 0 END) differenza
Example:
2014-12-17 : 3hrs
2014-12-18 : 8hrs
2014-12-19 : 8hrs
2014-12-20 : 2hrs
Tot : 21hrs
Use a Recursive CTE to get all Hours with Dates.
METHOD 1 : Get all dates with hours between FromDate and ToDate
DECLARE #FROMDATE DATETIME='2014-12-17 14:01:03.523'
DECLARE #TODATE DATETIME='2014-12-20 09:59:28.783'
;WITH CTE AS
(
SELECT #FROMDATE FROMDATE
UNION ALL
SELECT DATEADD(HH,1,FROMDATE)
FROM CTE
WHERE FROMDATE<#TODATE
)
SELECT ISNULL(CAST(CAST(FROMDATE AS DATE)AS VARCHAR(12)),'Tot')FROMDATE,
CAST(COUNT(FROMDATE)AS VARCHAR(4))+'hrs' [HOURS]
FROM CTE
WHERE DATEPART(HH,FROMDATE) BETWEEN 9 AND 16
AND DATENAME(DW,FROMDATE)<>'SATURDAY' AND DATENAME(DW,FROMDATE)<>'SUNDAY'
GROUP BY CAST(FROMDATE AS DATE)
WITH ROLLUP
SQL FIDDLE
METHOD 2 : Gets missing dates between FromDate and ToDate with 8 as hardcoded as Hrs
This method will be more implementable - Performance Tuned
DECLARE #FROMDATE DATETIME='2014-12-17 14:01:03.523'
DECLARE #TODATE DATETIME='2014-12-20 09:59:28.783'
;WITH CTE AS
(
-- Get missing dates between FromDate and ToDate
SELECT DATEADD(DAY,1,#FROMDATE) FROMDATE,8 HRS
UNION ALL
SELECT DATEADD(DAY,1,FROMDATE),8
FROM CTE
WHERE FROMDATE < DATEADD(DAY,-1,#TODATE)
)
,CTE2 AS
(
-- Gets the Hours for FromDate
SELECT CAST(#FROMDATE AS DATE) DATES, CAST(CAST(DATEDIFF
(
MINUTE,#FROMDATE,CAST(CAST(CAST(#FROMDATE AS DATE) AS VARCHAR(12))+' 17:00:00' AS DATETIME)
)AS NUMERIC(18,2))/60 AS DECIMAL(18,0)) HRS
WHERE DATENAME(DW,#FROMDATE)<>'SATURDAY' AND DATENAME(DW,#FROMDATE)<>'SUNDAY'
UNION ALL
-- Select Hours in between dates
SELECT CAST(FROMDATE AS DATE) NEWDATE,HRS
FROM CTE
WHERE DATENAME(DW,FROMDATE)<>'SATURDAY' AND DATENAME(DW,FROMDATE)<>'SUNDAY'
UNION ALL
-- Select Hours for ToDate
SELECT CAST(#TODATE AS DATE), CAST(CAST(DATEDIFF
(
MINUTE,CAST(CAST(CAST(#TODATE AS DATE) AS VARCHAR(12))+' 08:00:00' AS DATETIME),#TODATE
)AS NUMERIC(18,2))/60 AS DECIMAL(18,0))
WHERE DATENAME(DW,#TODATE)<>'SATURDAY' AND DATENAME(DW,#TODATE)<>'SUNDAY'
)
-- Use ROLLUP to find the sum of Hours and show it in last row
SELECT ISNULL(CAST(DATES AS VARCHAR(20)),'Tot')DATES,
CAST(SUM(HRS)AS VARCHAR(4))+'hrs' HRS
FROM CTE2
GROUP BY DATES
WITH ROLLUP
SQL FIDDLE
#marco burrometo
Create a static table which will have all the calendar functionality like holiday functionality,saturday and sunday is a holiday. It will help you a lot.
EmployeeID RecordID DateRecord
1 1 2/19/2013 12:00:00 AM
1 2 2/21/2013 12:00:00 AM
1 3 2/23/2013 12:00:00 AM
1 4 2/27/2013 12:00:00 AM
1 5 3/3/2013 12:00:00 AM
2 11 3/10/2013 12:00:00 AM
2 12 3/14/2013 12:00:00 AM
1 14 3/16/2013 12:00:00 AM
How can I count the number of days?
Example in February 2013 which has "19, 21, 23, 27" that should be count to "4" days .. ??
I found this method ..
SELECT DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord),
COUNT(*)
FROM Records
GROUP BY DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
DATEPART(dd, Daterecord)
and resulted to ..
2013 2 19 1
2013 2 21 1
2013 2 23 1
2013 2 27 1
2013 3 3 1
2013 3 10 1
2013 3 14 1
2013 3 16 1
it just get the specific dates but didm't count the total number of days in each month .. help me .. pls
I have change few names hopr you won't mind
WITH Emp_CTE AS (
SELECT EmployeeID ,DATEPART(yy, Daterecord) AS years,
DATEPART(mm, Daterecord) AS months
-- DATEPART(dd, Daterecord) AS days
FROM testTrial
)
SELECT COUNT(months) AS noOfMonths ,* FROM Emp_CTE GROUP BY months,EmployeeID,years
SqlFiddle
Let you try this:-
1: Find the number of days in whatever month we're currently in
DECLARE #dt datetime
SET #dt = getdate()
SELECT #dt AS [DateTime],
DAY(DATEADD(mm, DATEDIFF(mm, -1, #dt), -1)) AS [Days in Month]Solution
2: Find the number of days in a given month-year combo
DECLARE #y int, #m int
SET #y = 2012
SET #m = 2
SELECT #y AS [Year],
#m AS [Month],
DATEDIFF(DAY,
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m - 1, 0)),
DATEADD(DAY, 0, DATEADD(m, ((#y - 1900) * 12) + #m, 0))
) AS [Days in Month]
If your table is called Employee then this will do the trick:
select convert(varchar, DateRecord, 112)/ 100, count(*)
from Employee
group by convert(varchar, DateRecord, 112)/ 100
Your initial query was almost right, just needed to remove the DATEPART(dd, Daterecord) from the grouping and it would work. Add in a HAVING clause to find the records from the month of February:
SELECT
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord),
COUNT(1)
FROM
Records
GROUP BY
DATEPART(yy, Daterecord),
DATEPART(mm, Daterecord)
HAVING
DATEPART(yy, eCreationTime) = 2013
AND DATEPART(mm, Daterecord) = 2
there is no 'yearmonth' in the suggested code ??
try this perhaps
select
datename(month,daterecord) as [Month]
, year(DateRecord) as [Year]
, count(distinct DateRecord ) as day_count
, count(distinct dateadd(day, datediff(day,0, DateRecord ), 0)) as daytime_count
from your_table
where ( DateRecord >= '20130201' and DateRecord < '20130301' )
group by
datename(month,daterecord)
, year(DateRecord)
note the column [daytime_count] is only required if the field [DateRecord] has times othe than 12:00 AM (i.e. it "trims off" times so you deal with dates at 12:AM)
Regarding date range selections: many people will feel that using 'between' is the solution however that isn't true and the safest most reliable method is as I shown above. Note that the higher date is 1st March, but we are asking for information that is less than the 1st March, so we don't need to worry about leap years and we don't have to worry about hours and minutes either.
see: What do BETWEEN and the devil have in common?
try this...
declare #date2 nvarchar(max)
set #date2 = (select getdate())
select DateDiff(Day,#date2,DateAdd(month,1,#date2))