How to combine two SQL date functions into one to get output - sql

I am trying to populate a column with the date that is the first of the month before a given date
i.e. If the date in column one is 25/01/2017, I want the date in column two to read 01/12/2016
So far, I have managed to take a month off, or reset it to the first of the month, but I can't work out a way to combine the two. Is it possible, or do I have to do it in two different statements. Here is what I have so far:
SELECT
PAY_START_DATE,
DATEADD(MONTH, -1, CONVERT(DATE,PAY_START_DATE)) AS AMMENDONE,
DATEADD(m, DATEDIFF(m, 0, PAY_START_DATE), 0) AS AMMENDTWO
FROM [hronline_iTrent].[iTrent].[Payroll]
And this gives the output:
PAY_START_DATE | AMMENDONE | AMMENDTWO
2016-04-06 |06/03/2016 | 01/04/2016 00:00
2016-06-12 |12/05/2016 | 01/06/2016 00:00
Any chance I can combine the two so I can get an output like:
PAY_START_DATE | AMMEND
2016-04-06 | 2016-03-01
2016-06-12 | 2016-05-01
(The format of the output is not important)

Try the below query,
SELECT PAY_START_DATE
,DATEADD(DAY,1, DATEADD(MONTH,-2,EOMONTH(PAY_START_DATE)))
FROM [hronline_iTrent].[iTrent].[Payroll]

SELECT DATEADD(month,-1,DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)) AS StartOfMonth

Here is one method:
select dateadd(month, -1,
dateadd(day, 1 - day(PAY_START_DATE), PAY_START_DATE)
)
The inner dateadd() gets the first day of the month. The outer one subtracts one month.

You can do it with just one DATEADD/DATEDIFF pair, so long as you don't mind that it's somewhat cute:
SELECT DATEADD(month,DATEDIFF(month,'20010101',PAY_START_DATE),'20001201')
It employs the fact that we pick two arbitrary dates that already have the required relationship between them that you want to achieve - I.e. 20001201 is the first of the month previous to 20010101.

Related

How to retrieve data within a 6 day time period

I want to retrieve the data between a 6 day time period.
The output I want is:
Date
--------
2019-05-01
2019-05-04
2019-06-01
2019-06-06
2019-07-01
This is my query so far:
select date from data d
where CAST(d.createdate as Date) between CAST('2019-05-01' as Date)
AND DATEADD(CAST(dd,6,'2016-07-01') as Date)
Why is this not retrieving the results I want?
You have several problems with your query.
The first is with your DATEADD statement which is all mixed up. You are not nesting the casted date into the statement properly. This is the corrected version:
DATEADD(dd, 6, CAST('2016-07-01' as Date))
The second is that your select projection refers to the column date which does not exist. Instead, you probably want your createdate column.
The third is that your between clause is back to front. You are saying between 2019-05-01 and 2016-07-01 but the smaller date must come first.
In fact, your given example is incorrect. In your question, you say "want to retrieve the data between two dates only for 6 days." So, why would you start with a date in 2016 and then jump to a date in 2019 and add 6 days to the date in 2019? If you want to use the DATEADD approach, you need to use the same date in both positions.
So here is your corrected query:
select d.createdate from data d
where CAST(d.createdate as Date) between CAST('2019-05-01' as Date)
AND DATEADD(dd, 6, CAST('2019-05-01' as Date))

SQL deduct one month from a date but keep the order correct

The title is probably worded poorly but what I want to have is say I do this
SELECT DATEADD(month,-1,'4/30/2019')
Instead of getting 3/30/2019 I want 3/31/2019, how do we do this?
If you want the last day of the previous month:
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, '2019-04-30'), -1)
See the demo.
Result:
2019-03-31

Dividing by number of days not referencing correct month/number of days

I have a table that users enter a daily population. How many people in a particular facility that day. The table looks similar to this:
select * from stat_summary where MONTH(report_date) = 9
results:
stat_summary_id | report_date | facility | adp
----------------------------------------------------
29 |2015-09-01 | YORK | 1855
30 |2015-09-02 | YORK | 1750
31 |2015-09-04 | YORK | 1655
32 |2015-09-04 | YORK | 1699
What I want to do is calculate the average daily population grouped by month. I want to take the MAX(report_date) in case a corrected value has to be re-entered. My query looks like:
SELECT
MONTH(t.report_date) as 'report_month',
SUM(ss1.adp)/DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,MONTH(t.report_date)),0))),
DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,MONTH(t.report_date)),0)))
FROM
stat_summary ss1
INNER JOIN
(SELECT MAX(stat_summary_id) as 'stat_summary_id', report_date
FROM stat_summary
GROUP BY report_date
) t ON t.stat_summary_id = ss1.stat_summary_id
WHERE
ss1.facility_id = 'YORK'
AND MONTH(t.report_date) = 9
GROUP BY
MONTH(t.report_date)
ORDER BY
MONTH(t.report_date)
I've referenced this thread:
Dividing a value by number of days in a month in a date field in SQL table
And I was able to see how to dynamically divide by the number of days in the month, but it looks like it is dividing by the current month (October) which has 31 days, when I need the query to divide by the referenced month of September which has 30 days.
Currently my results look like:
The adp value should be 176.8 since there are 30 days in September, not 31.
So quick check it looks like that formula returns 31 for all months. The proper formula can be found here: How to determine the number of days in a month in SQL Server?
datediff(day, dateadd(day, 1-day(#date), #date),
dateadd(month, 1, dateadd(day, 1-day(#date), #date)))
More precisely use:
datediff(day, dateadd(day, 1-day(MIN(t.report_date)), MIN(t.report_date)),
dateadd(month, 1, dateadd(day, 1-day(MIN(t.report_date)), MIN(t.report_date))))
EDIT: Note the original formula was in fact correct, the problem was that you were passing in a month instead of a day. Months are numbers from 1-12, so all of your dates were in January.
I should use
day(DateAdd(day, DateAdd(month, MONTH(t.report_date), DateAdd(Year, YEAR(t.report_date)-1900, 0)), -1)) as monthDays
Also sounds to me that to obtain average, it is wrong to divide by that number, it is only correct if the number of records match the number of days in the month, in other case, just the count is enough
SUM(ss1.adp)/count(ss1.adp) as average

Grouping by similar categories over time

I looked around for awhile, but couldn't find anything.
I have a table that looks like this:
DATE | Shift | Parts Used
1/1/15 1:15.....1........1
1/1/15 2:06.....1........2
1/1/15 3:45.....1........3
1/1/15 7:33.....2........1
1/1/15 8:14.....2........2
1/1/15 9:00.....2........3
1/1/15 23:01....1........1
1/1/15 23:55....1........2
I would like to group by each individual shift. UNFORTUNATELY shift one can sometimes end on a monday morning and start again on monday night (sometimes our twelve hour shifts are not exactly 12 hours). Is there a way I can group by shifts individually, essentially grouping by shift until shift changes even if my day is the same (my actual column contains a time too)? I could write a loop to analyze the data, but getting it from sql would be great.
The date is orders the rows. Shifts cannot overlap.
Hopefully my result would be something like
DATE | Shift | AVG parts used
1/1/15.....1........2
1/1/15.....2........2
1/1/15.....1........1.5
Edit****** Worst case scenario with expected results
DATE | Shift | AVG parts used
1/1/15.....1........1.8
1/1/15.....2........2
I'm assuming that shift 1 is suppose to start at midnight and shift 2 starts at noon, but its sometimes off by some amount of time. This query should work if those assumptions are true. I've made a variable called #ShiftFudgeHours to account for how off the shift start times usually are. Adjust that based on your numbers. From your sample 1 hours looks sufficient, but you could easily increase it.
declare #shiftFudgeHours int = 1
select convert(date, DATEADD(hh, #shiftFudgeHours, [date]) ) as [date]
,[shift]
,avg(convert(float, parts)) as AvgParts
from Table1
group by convert(date, DATEADD(hh, #shiftFudgeHours, [date]) ), [shift]
order by convert(date, DATEADD(hh, #shiftFudgeHours, [date]) ), [shift]

Sales Grouped by Week of the year

I have a requirement to output the number sales in a year to date in weekly format where Monday is the first day of the week and Sunday is the last.
The table structure is as follows.
SalesId | Representative | DateOfSale.
Below is what I have tried but it doesn't seem to give me the correct result. The counts don't seem to add up for a given week. The Sunday results are not included in the correct week. I am thinking it has something to do with the date not including 11:59:59.999 for the last day of the week.
SELECT DATEADD(wk, DATEDIFF(wk, 6, Sales.DateOfSale), 6) as [Week Ending], count(SalesID) as Sales,
count(distinct(representative)) as Agents, count(SalesID) / count(distinct(representative)) as SPA
FROM Sales
where DateOfSale >= DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)
GROUP BY DATEADD(wk, DATEDIFF(wk, 6, Sales.DateOfSale), 6)
ORDER BY DATEADD(wk, DATEDIFF(wk, 6, Sales.DateOfSale), 6)
I am hoping to have something like this:
Week Ending | Sales
01/05/2014 | 5
01/12/2014 | 8
01/19/2014 | 11
01/26/2014 | 14
Please excuse the formatting of the table above. I couldn't seem to figure out how to create a pipe/newline based table using the editor.
~Nick
I suggest creating a table or table parameter that has all of your calendar information. In this case, it would need at minimum the column WeekEnding.
For example
DECLARE #MyCalendar TABLE
(
WeekEnding date
);
Populate this with your valid WeekEnding dates. I might also make parameters to limit the amount of sales data, e.g. #BeginDate and #EndDate.
If you join using "<=" on the week ending date, then I believe you will get the return you want:
SELECT
MyCalendar.WeekEnding,
COUNT(Sales.SalesId) Sales,
COUNT(DISTINCT Sales.Representative) Agents,
CAST(COUNT(Sales.SalesId) AS float) / CAST(COUNT(DISTINCT Sales.Representative) AS float) Spa
FROM
Sales
INNER JOIN
#MyCalendar MyCalendar
ON
Sales.DateOfSale <= MyCalendar.WeekEnding
WHERE
Sales.DateOfSale BETWEEN #BeginDate AND #EndDate
GROUP BY
MyCalendar.WeekEnding;
I am assuming you are using SQL 2012, but I believe this will work in 2008 too. I might point out two other things. First, consider your data type when dividing the COUNT of SalesId by the distinct count of Representative. You may not get the return you expect, and that is why I cast as float. Second, you apply count distinct slightly differently than what I use; the extra parenthesis are not needed.
I have a simplified version in SQL Fiddle.