How do I round up a date? - sql-server

I have a table that contains the contract information for our customers. I need to round up the contract maturity date to a certain day of the month depending on the contract date itself. For example, if the contract date is 01-05-2016 I need to round it up to 01-10-2016. If the contract date is 01-11-2016 I need to round it up to 01-20-2016. And finally, if the contract date is 01-21-2016 I need to round it up to 01-30-2016. These round up dates match our billing cycles and I need all of our contracts to fall within one of these billing cycles. All dates are a DATETIME data type. Any help would be appreciated.

Maybe something like this?
DECLARE #testData TABLE(TestDate DATE);
INSERT INTO #testData VALUES({d'2016-02-05'}),({d'2016-02-12'}),({d'2016-02-21'});
SELECT TestDate
,CASE WHEN DAY(TestDate) BEtWEEN 1 AND 10 THEN 1
WHEN DAY(TestDate) BEtWEEN 11 AND 20 THEN 2
ELSE 3 END AS BillingCycle
,CASE WHEN DAY(TestDate) BEtWEEN 1 AND 10 THEN CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0')+'01' AS DATE)
WHEN DAY(TestDate) BEtWEEN 11 AND 20 THEN CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0') +'11' AS DATE)
ELSE CAST(CAST(YEAR(TestDate) AS CHAR(4))+REPLACE(STR(MONTH(TestDate),2),' ','0')+'28' AS DATE) END AS BillingCycleDate
FROM #testData
The result:
TestDate BillingCycle BillingCycleDate
2016-02-05 1 2016-02-01
2016-02-12 2 2016-02-11
2016-02-21 3 2016-02-28

You would do much better to avoid casting dates from strings. The long way is something like this though I'm sure it could be shortened and obfuscated. You did not specify what to do with dates after the 28th though:
dateadd(
day,
case day(contract_dt)
when 1 then 9
when 2 then 8
...
when 10 then 0
when 11 then 9
when 12 then 8
...
when 20 then 0
when 21 then 7
when 22 then 6
...
when 28 then 0
when 29 then -1
when 30 then -2
when 31 then -3
end,
contract_dt
)
Here is one of the more compact forms I alluded to:
dateadd(day, case
when day(contract_dt) <= 20
then 10 - day(contract_dt) % 10
else 28 - day(contract_dt)
end, contract_dt)
EDIT: I presume based on your acceptance of the other answer that you want to fall back to the 28th for the outliers and so I edited the above accordingly.

Related

How to select records of same day as today from all months and years

I have sample data as below:
Date
Index
26-07-2022
26
26-06-2022
23
24-07-2022
12
19-06-2022
16
26-04-2022
01
26-05-2022
10
26-07-2022
12
I want to select data of latest day from each month. For example if today's date is 26-07-2022 then I want to select all records where date is 26th.
So my output should look like below:
Date
Index
26-07-2022
26
26-06-2022
23
26-04-2022
01
26-05-2022
10
26-07-2022
12
Do anybody know how can I achieve this. Thanks.
Assuming that the data type of the column [Date] is DATE, use the function DAY():
SELECT *
FROM tablename
WHERE DAY([Date]) = DAY(GETDATE())
AND [Date] <= GETDATE(); -- you may remove this if it is not needed
See the demo.

Return Maximum Value along with day + time value grouped by month

Using MS-SQL 2012. Having a real puzzle trying to retrieve specific datafields from a large climatology dataset.
I have stripped this large raw data file down to a temp table called #max_temp which correctly pulls back the max value for each day along with the time it occurred and day/month value for reference:
monthid month day time current_temp
1 12 24 12:45 9.1
1 12 25 12:25 8.3
1 12 26 23:55 8.6
1 12 27 00:00 8.6
1 12 28 13:15 5.9
1 12 29 12:50 5
1 12 30 13:32 6.3
1 12 31 12:49 6.9
2 1 1 23:59 12
2 1 2 01:12 12.7
2 1 3 03:55 6.2
What I want to retrieve is an output grouped by monthID, so returning:
monthid month day time current_temp
1 12 24 12:45 9.1
2 1 9 20:04 15.1 <<*not shown in above sample*>>
From looking at other similar questions I have tried the following the code but not getting to the end solution or the query fails.
select *
from (select t.*, ROW_NUMBER () over (partition by t.monthid, t.time order by t.current_temp desc) as rn
from #max_temp t) x
where rn=1
order by monthid asc
or
select monthid, day, time, current_temp
from #max_temp
where current_temp= (select max(current_temp) from #max_temp group by MonthID, day, time)
Thanks in advance for your help,
Elliot.
Remove t.time from the partition by like so:
select *
from (
select t.*, ROW_NUMBER () over (partition by t.monthid order by t.current_temp desc) as rn
from #max_temp t
) x
where rn=1
order by monthid asc
Having time in the partition would give you the greatest value for current_temp for each monthid and time, but since you just want the greatest current_temp for each monthid, remove time from that expression.

DateTime in sql only comparing the time

ID DateTime Code
---------- -------------- ----------
58 2015-01-01 20:00:00 1111
58 2015-01-11 10:00:00 8523
58 2015-01-11 03:00:00 4555
58 2015-01-19 00:01:00 8888
9 2015-01-01 20:00:00 4444
how do i count the number of codes for a specific ID ignoring which date it is but it must be between 20:00:00 and 06:00:00
select count(code) as count from table 1 where ID='58' and DateTime between '20:00:00' and '06:00:00'
the expected output would be
count
3
SELECT count(code) as count
FROM table1
WHERE
ID='58' and
(CAST(DateTime as time) >= '20:00'
or CAST(DateTime as time) <= '06:00')
EDIT: John, I understand the issue. Here is a full solution to handle those cases:
In order to use variables:
DECLARE #HourBegin time = '07:00'
DECLARE #HourEnd time = '17:30'
SELECT count(code) as count
FROM table1
WHERE
ID='58' and
(CAST(DateTime as time) between #HourBegin and #HourEnd or
((CAST(DateTime as time) <= #HourEnd or
CAST(DateTime as time) >= #HourBegin) and
#HourBegin > #HourEnd)
)
Almost the same as previous answer, but with hours it looks nicer for me and might be you need DISTINCT code
SELECT count(DISTINCT code) as count
FROM table1
WHERE
ID='58' and
(DATEPART(HOUR,DateTime) >= 20
or DATEPART(HOUR,DateTime) < 6)
UPDATED: changed from <= 6 to < 6
Update
This answer applies to MySQL.
When I started writing the answer, the question was tagged mysql and sql-server. The OP edited it in the meantime.
This query should do what you want on MySQL.
SELECT count(code) AS `count`
FROM `table 1`
WHERE ID='58'
AND TIME(`DateTime`) NOT BETWEEN '06:00:01' AND '19:59:59'
The MySQL function TIME() extracts only the time component from a DATETIME value.
On version 5.7, MySQL added support for fractional seconds (up to 6 digits) on DATETIME columns. The query above will include the entries having time greater than 06:00:00 but smaller than 06:00:01 (events that happened during the first second after 6 AM sharp).
For MySQL 5.7 and newer, the correct query is:
SELECT count(code) AS `count`
FROM `table 1`
WHERE ID='58'
AND (TIME(`DateTime`) <= '06:00:00' OR '20:00:00' <= TIME(`DateTime`))
I don't know about SQL Server.

Add dynamic column in select query - MS SQL

Im using Ms SQL database,
My table looks,
SrNo Emp_ID Date Time
------------------------------------
1 25 03-Sep-12 9:35:35 AM
2 25 03-Sep-12 10:31:32 AM
3 25 03-Sep-12 10:34:13 AM
4 25 03-Sep-12 11:05:08 AM
5 25 03-Sep-12 11:08:39 AM
6 25 04-Sep-12 09.05.40 AM
The expected output is
SrNo Emp_ID Date Time Type
---------------------------------------------
1 25 03-Sep-12 9:35:35 AM IN
2 25 03-Sep-12 10:31:32 AM OUT
3 25 03-Sep-12 10:34:13 AM IN
4 25 03-Sep-12 11:05:08 AM OUT
5 25 03-Sep-12 11:08:39 AM IN
6 25 04-Sep-12 09.05.40 AM IN
For an employee, type "in" and "out" has to be added simultaneously for particular date. For the next date or next /same employee, type has to start with "IN". Can any 1 help me in writing the sql query.
You can achieve this using an additional column
ROW_NUMBER() OVER (PARTITION BY Emp_ID, Date ORDER BY Time) AS RN
then checking modulus 2 on it, if remainder is 1, it's an IN, if it's 0 it's an OUT, based on your requirements.
More specifically
CASE WHEN
ROW_NUMBER() OVER (PARTITION BY Emp_ID, Date ORDER BY Time) % 2 = 1
THEN 'IN'
ELSE 'OUT'
END AS [Type]

Need help with a SQL query selecting date ranges from a table of period quarters

I have a table called Periods that looks like this
PeriodID | PeriodYear | PeriodQuarter
7 | 2009 | 1
8 | 2009 | 2
9 | 2009 | 3
10 | 2009 | 4
11 | 2010 | 1
12 | 2010 | 2
Each row in the table represents 1 of the 4 quarters of the year (like 3-monthly school terms). E.g. The first row represents Period 1 of 2009 (i.e. the date range 1 Jan 2009 - 31 March 2009.
Now I need to write a query that selects rows/periods from the above table, where the period occurs between 2 date ranges, as per the following pseudocode.
select *
from Periods
where Period is between #startDate and #endDate
The query will be used inside a table-valued function called dbo.GetPeriodsFromDateRange, and #startDate and #endDate are parameters to the function.
I'm stuck and can't figure out how to do it. Please help. This applies to T-SQL (MS SQL Server 2000/2005)
Try
select *
from Periods
where dateadd(qq,PeriodQuarter-1,dateadd(yy,PeriodYear -1900,0))
between #startDate and #endDate
A seek instead of a scan is possible:
SELECT *
FROM Periods
WHERE
PeriodYear BETWEEN Year(#startdate) AND Year(#enddate)
AND PeriodYear * 4 + PeriodQuarter
BETWEEN Year(#startdate) * 4 + DATEPART(Quarter, #startdate)
AND Year(#startdate) * 4 + DATEPART(Quarter, #enddate)
Explanation:
I'm composing a new, scaled integer from two component pieces, the year and the quarter, treating each combination of year and quarter as a single number.
Imagine instead that I had done it this way:
AND PeriodYear + (PeriodQuarter - 1) / 4.0
BETWEEN Year(#startdate) + (DATEPART(Quarter, #startdate) - 1) / 4.0
AND Year(#startdate) + (DATEPART(Quarter, #enddate) - 1) / 4.0
Calling my original expression "Mult" and this new one "Div", here are some years and quarters and what those expressions will evaluate to:
Year Qtr Div Mult
2009 1 2009.00 8037
2009 2 2009.25 8038
2009 3 2009.50 8039
2009 4 2009.75 8040
2010 1 2010.00 8041
2010 2 2010.25 8042
2010 3 2010.50 8043
So now if we run a WHERE clause against these rows:
WHERE Div BETWEEN 2009.25 AND 2010.00
You can see how it will return the correct rows. The Mult version really does exactly the same, just scaling the year up instead of the quarter down. The reason I used it is because integer math and multiplication are faster than fractional math and division.
The reason that I use two conditions starting with just the year is to make the query sargable. We want to do the seek based on just year, which isn't possible if we're multiplying it by 4 or doing other math on it. So we get the scan into only the right years first, then fine tune it to eliminate any quarters that shouldn't be in the result.
Another option is to add a calculated column and put an index on it. This wouldn't require any changes to code inserting or updating (as long as they properly use column lists), but would let you do regular range math as you desire.
I would be tempted to add 2 further columns to the table...
StartDate and EndDate - these will store the date that each period starts and ends (i.e. in your example StartDate=1st Jan 2009 and EndDate=31st March 2009)
This will give you more flexibility if the quarters are defined differently than you have suggested.
If you do this, then the query become fairly simple...
select *
from Periods
where #startDate<Periods.StartDate and #endDate>Periods.EndDate
This is assuming you only want to include Periods which are completely encapsulated between #StartDate and #EndDate. If you want Periods that overlap then try something like...
select *
from Periods
where #EndDate>Periods.StartDate and #StartDate<Periods.EndDate

Resources