convert ssis expression datetime to int - sql-server

I want to calculate last friday's date in ssis. Below code is doing it.
DATEADD("dd", -1 - (DATEPART("dw", getdate()) % 7), getdate())
But it gives results for datetime datatype and I want results for int data type in ssis.
How to convert?
DATEADD("dd", -1 - (DATEPART("dw", getdate()) % 7), getdate())

According to this Microsoft article:
When a string is cast to a DT_DATE, or vice versa, the locale of the transformation is used. However, the date is in the ISO format of YYYY-MM-DD, regardless of whether the locale preference uses the ISO format.
In order to convert value to integer yyyyMMdd,
First you have to convert the value to a string
Get the first 10 digit
Remove the - separator
Convert to integer.
You can use the following expression:
(DT_I4)REPLACE(SUBSTRING((DT_WSTR,50)DATEADD("dd", -1 - (DATEPART("dw", getdate()) % 7), getdate()),1,10),"-","")

Related

DateAdd side effects?

I am experiencing a very strange behavior here with Microsoft SQL Server 2016 (SP2-CU15):
select convert(datetime, max(TS) + 1.0/24) as A
from table;
yields 2021-01-16 11:59:00.000
while
select convert(datetime, max(TS) + 1.0/24) as A
, dateadd(hour, 1, max(TS)) as B
from table;
gives me 2021-01-16 11:58:59.943 for A (and 2021-01-16 11:59:00.000 for B). So, it seems to me that adding the second column changes the result for the first?!
I can force the two-column version to work by casting 1.0 to real, btw: convert(datetime, max(TS) + cast(1.0 as real)/24), but I can not force the one-column version to fail by writing convert(datetime, max(TS) + cast(1.0 as float)/24).
Any ideas what's happening here?
Thanks!
Hendrik.
Update: As requested, here is a minimal example:
CREATE TABLE TestTS (TS FLOAT);
INSERT INTO TestTS (TS) VALUES (44210.4993055556);
SELECT convert(datetime, max(TS) + 1.0/24) as A
, dateadd(hour, 1, max(TS)) as B
from TestTS
As described, if you comment out the B-column, the value of A changes.
There's nothing wrong with DATEADD. The problem is the rest of the question.
First, there's a critical bug. Dates are stored as floats. An appropriate type should be used instead, eg datetime2, datetime or datetimeoffset. The best options are datetime2(0) or datetimeoffset(0), assuming no millisecond precision is needed.
datetime is essentially a legacy type, whose internal storage format is ... a float in the OADate format. That doesn't mean floats should be instead of the correct type though, no more than varbinary should be used instead of int or bigint.
Then, there's an attempt to add one hour to the OADate value, by calculating the floating point value of 1 hour in that format, 1/24. That's an irrational number though (0.04166666666....) which means that rounding errors always result in an inaccurate value.
Solution
The real solution is to use the correct type and DATEADD, eg :
CREATE TABLE TestTS (TS datetime2(0));
INSERT INTO TestTS (TS) VALUES ('2021-01-16 10:59:00.000');
SELECT dateadd(hour, 1, max(TS)) as B
from TestTS
If you want millisecond precision, use datetime2(3).
Getting the hack to work.
If you used datetime you wouldn't need to convert to datetime in the end, but the result would still be imprecise. This :
declare #TestTS table (TS datetime);
INSERT INTO #TestTS (TS) VALUES ('2021-01-16 10:59:00.000');
SELECT max(ts)+ (1.0/24)
from #TestTS
Produces 2021-01-16 11:58:59.943. The only reason the hack looked to be working in the first place was probably due to rounding errors during conversion.
The only way to get a correct result by adding floating point numbers is to increase precision to 8 fractional digits :
declare #TestTS table (TS datetime);
INSERT INTO #TestTS (TS) VALUES ('2021-01-16 10:59:00.000');
SELECT max(ts)+ (1.00000/24)--, dateadd(hour, 1, max(TS)) as B
from #TestTS
That produces 2021-01-16 11:59:00.000.
1.0 is a decimal(2,1). T-SQL calculates the fractional digits of decimal division based on the functional digits of the operands. If the operands have up to 4 fractional digits, the result will have 6 fractional digits, which isn't enough. 1 digit is added for any fractional digit above 4. 1.00000 results in 8 fractional digits 0.04166666
Don't do this though.
Cause
Thanks to #MartinSmith for the clue.
The cause is query auto-parameterization and the data types being chosen to store values.
Query 1 is auto-parameterized:
StatementText="SELECT CONVERT([datetime],MAX([TS])+#1/#2)
....
<ColumnReference Column="#2" ParameterCompiledValue="(24)" ParameterRuntimeValue="(24)" />
<ColumnReference Column="#1" ParameterCompiledValue="(1.0)" ParameterRuntimeValue="(1.0)" />
Query 2 is not auto-parameterized:
StatementText="SELECT convert(datetime, max(TS) + 1.0/24) as A...."
Why it happens is the first query and not the second query is a bit of a black magic.
From SQL Server data types page:
When you use the +, -, *, /, or % arithmetic operators to perform
implicit or explicit conversion of int, smallint, tinyint, or bigint
constant values to the float, real, decimal or numeric data types, the
rules that SQL Server applies when it calculates the data type and
precision of the expression results differ depending on whether the
query is autoparameterized or not.
Therefore, similar expressions in queries can sometimes produce
different results. When a query is not autoparameterized, the constant
value is first converted to numeric, whose precision is just large
enough to hold the value of the constant, before converting to the
specified data type. For example, the constant value 1 is converted to
numeric (1, 0), and the constant value 250 is converted to numeric (3, 0).
When a query is autoparameterized, the constant value is always
converted to numeric (10, 0) before converting to the final data
type. When the / operator is involved, not only can the result type's
precision differ among similar queries, but the result value can
differ also. For example, the result value of an autoparameterized
query that includes the expression SELECT CAST (1.0 / 7 AS float)
will differ from the result value of the same query that is not
autoparameterized, because the results of the autoparameterized query
will be truncated to fit into the numeric (10, 0) data type.
Effect
Based on the above, the following data types are used (refer to See: Precision, scale, and Length (Transact-SQL) for explanation of how result types are calculated):
Query 1 gives higher precision:
NUMERIC( 2, 1 ) / NUMERIC( 10, 0 ) = NUMERIC( 13, 12 )
Query 2:
NUMERIC( 2, 1 ) / NUMERIC( 2, 0 ) = NUMERIC( 7, 6 )
Solution
Cast your literals and / or intermediate results to the desired type to avoid surprises.
In your specific case, best solution is not to use number arithmetic to manipulate dates as Panagiotis Kanavos explains in his answer.
Alternatively, forcing float data types (per Dan Guzman comment) convert(datetime, max(TS) + 1e/24) would do the trick as well.
This question deals with the same issue.

Add an offset to current time

I get an time difference value of 5.5 which should subtract 5 hours and 30 minutes from current date time .
select Dateadd(HH, -5.5,GETUTCDATE()),GETUTCDATE()
In output it only subtracts 5 hours .
I always get an value in form of this offset 5.5 or 13.5 likewise depending on the timezone..
Is this possible ???
I always get an value in form of this offset 5.5 or 13.5 likewise depending on the timezone..
Then you can multiply the value by 60 and use minute datepart
select Dateadd(MINUTE, -5.5 * 60,GETUTCDATE())
time arithmetic always works in whole units - use -330 minutes instead
select Dateadd(MINUTE, -330,GETUTCDATE()),GETUTCDATE()
the input to the function is intended to be an integer (whole number) - you may be finding that -5.5 is truncating off the decimal part
Yes this can be done. First, you need to convert your decimal offset into minutes. Then you can use DATEADD as before.
DECLARE #Offset DECIMAL(18, 2) = 5.5;
DECLARE #Mins INT = 60 * #Offset;
-- Offsetting by decimal time values.
SELECT
DATEADD(MINUTE, #Mins, GETDATE())
;

Convert from 4 digit Military time to Standard time

I am using SQL Server 2016 and I'm trying to convert military time to standard time. The military time is a 4 digit integer and I'm trying to get the standard time format (00:00 am/pm).
I ran a simple CASE statement that made it standard but without ':' and 'am/pm'.
CASE
WHEN SA_BEGTIME between 0 and 1259 then SA_BEGTIME
WHEN SA_BEGTIME between 1300 and 2400 then SA_BEGTIME - 1200
ELSE ''
END as Time
Results
How do I convert it so that it is in the right format: '00:00 am/pm'
Thank you!
You can split it into parts with integer division and modulo, cast it to a VARCHAR and then you can convert it to a TIME:
declare #x int = 109
select cast(cast(#x / 100 as varchar(2)) + ':' + cast(#x % 100 as varchar(2)) as time)
Or you can use the new TIMEFROMPARTS() function (SQL Server 2012+):
declare #x int = 109
select TIMEFROMPARTS(#x / 100,#x % 100, 0, 0, 0)
You can then format it however you'd like.
Assuming your data is stored as an integer, and also assuming there is not invalid time stored (i.e. values above 2400 or below 0) you can use the following:
declare #TheTime int = 900
select right(convert(varchar(20),
cast(stuff(right('0000' + convert(varchar(4),#TheTime),4),3,0,':')
as datetime),100),7)
-------
9:00AM
Sorry for the density of the solution. This is what I did:
Convert #TheTime to varchar(4)
Add a string of zeros at the front
Take the rightmost 4 characters from this new string
Stuff a colon sign in the middle of this new string
Cast the string as datetime
Convert back to string using 100 as the style indicator to get AM/PM
Get the right most 7 characters of the string.
I am sure there are more elegant ways, but this one works for me quite well.
I'm using the solution that #BaconBits provided but I had to make a tweak because 2400 was a valid representation but it failed with that code. Here's the tweaked solution:
declare #x int = 2400
select TIMEFROMPARTS((#x / 100) % 24,#x % 100, 0, 0, 0)
I needed to convert a datetime field where the time was military to just the time in AM/PM format. This worked beautifully.
Left(convert(varchar(20), cast(MyDateField as time),100),7)
You can use convert to get n appropriate presentation of time.
declare #mt varchar(4) = '1500'
select convert(varchar, cast(left(#mt, 2) + ':' + right(#mt, 2) as time), 0)

How to convert Date and Time column to just time as a decimal

Does anybody know how I can convert a date and time column to a decimal column based on the time only?
For example:
Select StaffNum, Name, ContractHours
From StaffContract
Where StaffNum = 00123
Returns
**StaffNum**--------**Name**----------------**ContractHours**
00123-----------Shaw, Jason--------2012-10-01 07:30:000
Instead of the ContractHours column showing '2012-10-01 **07:30:000**' I need it to show the time section only and convert it to a decimal so instead of 07:30 I need it to show 7.5
You can use the datepart to extract parts of the date:
SELECT StaffNum,
Name,
DATEPART(hour, ContractHours) + (DATEPART(minute, ContractHours) / 60.0)
FROM StaffContract
WHERE StaffNum = 00123

SQL Server : datalength conversion

I have a table dbo.files with 9 columns that include file_size and created_time and filepath.
Sample values:
file_size = 528300
created_time = 2012-06-28 09:31:17.610
I have the following query where I'm trying to show the total # of MB have been written to the filesystem 'today' by these files.
select
sum(datalength(f.file_size)) / 1048576.0 AS 'Storage Used Today"
from
dbo.files AS f
where
f.created_time >= CAST(getdate() as DATE)
and f.created_time < CAST(DATEADD(day, 1, getdate()) as DATE)
The result is '0.173525810'. Is there a way to move that decimal over to show the proper value?
Thanks
SUM(DATALENGTH(x)) tells you the size in bytes of the numeric representation.
Which isn't what you need.
For example if the datatype was integer (4 bytes) and you had three rows with none null values in the column it would evaluate to 12 irrespective of the actual numeric contents.
Just remove the function call.
sum(f.file_size) / (1024.0 * 1024)
Will work fine

Resources