calculation after converting nvarchar to datetime/date - tsql

I am trying to mimic this calculation from Excel into T-SQL.
The first value is "7/25/2021 6:44:00 AM"
The second value is "7/25/2021 1:10:00 PM"
I am trying to come up with the value of 0.35138..
In SQL table, both values are currently in nvarchar(max) data type.
I am trying to write SQL Function, and I am stuck with process of Convert or Cast and do the calculation of two values.
So far, this is what I have:
CREATE FUNCTION [dbo].[fn_break_cal] (
#Punch_Start nvarchar(max),
#Punch_End nvarchar(max)
)
RETURNS nvarchar(max) AS
BEGIN
DECLARE #return_value nvarchar(max);
SET #return_value =
CONVERT(varchar, #Punch_Start, 103)
RETURN #return_value
First, I am not sure how to do subtraction after I convert into date or datetime.
Whenver I use Datediff, output is Integer, and I am not sure how to make it to numeric like (0.35133..).
What is best approach of dealing with data from nvarchar(max) --> calculation of date/datetime?
Thanks in advance.

Please refer to the below added function and the function call. I am not sure why you return nvarchar(max) value in the function. I changed that to Decimal. I used CAST to cast the nvarchar to datetime. you can use TRY_CAST as well.
ALTER FUNCTION [dbo].[fn_break_cal] (
#Punch_Start nvarchar(max),
#Punch_End nvarchar(max)
)
RETURNS decimal(36, 8) AS
BEGIN
DECLARE #return_value nvarchar(max);
SET #return_value = DATEDIFF(MINUTE, CAST(#Punch_Start AS datetime2) , CAST(#Punch_End AS datetime2) ) / (24* 60.0)
RETURN #return_value
END
Function call with params and return value:
Select [dbo].[fn_break_cal] ('7/25/2021 6:44:00 AM','7/25/2021 15:10:00 PM') return_value

DECLARE #ps nvarchar(30) = '7/25/2021 6:44:00 AM'
DECLARE #pe nvarchar(30) = '7/25/2021 15:10:00 PM'
SELECT
DATEDIFF(MINUTE, TRY_CONVERT(datetime, #ps), TRY_CONVERT(datetime, #pe)) / (24 * 60.0) AS fraction
SELECT
LEFT(CONVERT(time, TRY_CONVERT(datetime, #pe) - TRY_CONVERT(datetime, #ps)), 5) AS ' hr : mm '
-- more than 24 hrs
DECLARE #ps nvarchar(30) = '7/25/2021 6:44:00 AM'
DECLARE #pe nvarchar(30) = '7/27/2021 15:10:00 PM'
SELECT
CONCAT((DATEDIFF(MINUTE, TRY_CONVERT(datetime, #ps), TRY_CONVERT(datetime, #pe)) / 60),
':',
(DATEDIFF(MINUTE, TRY_CONVERT(datetime, #ps), TRY_CONVERT(datetime, #pe)) % 60)) ' hr : mm '

Related

Conversion failed when converting date and/or time from character string - SQL Server 2008 R2

I am busy with a stored procedure to calculate production numbers of shifts. I already have an idea on how to do that but for some kind of strange reason I do not get an insert into with a variable time working. Below is query for the stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[ProductionReport]
#filterStartTime datetime,
#filterEndTime datetime,
#machine varchar(10)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
CREATE TABLE #tempProductionTable(
id varchar(3),
ploeg varchar(3),
starttime2 datetime,
endtime2 datetime,
daynumber int)
declare #i int
, #SQLString varchar(400)
, #id varchar(3)
, #ploeg varchar(3)
, #starttime datetime
, #endtime datetime
set #i = 0
while #i < 16
begin
set #i = #i+1
set #id = #i
set #starttime = convert(datetime, #filterStartTime,110)
print #starttime
set #ploeg = '2'
SET #SQLString = 'INSERT INTO #tempProductionTable (id,ploeg,starttime2) values ('+#id+','+#ploeg+','+#starttime+')'
EXEC(#SQLString)
end
-- Insert statements for procedure here
SELECT * from #tempProductionTable
END
And this is the query for opening the stored procedure:
USE [NRPConfiguration]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[ProductionReport]
#filterStartTime = '2017-01-01 10:00:00.000',
#filterEndTime = N'2-1-2017 0:00',
#machine = N'ASM_008'
SELECT 'Return Value' = #return_value
GO
I already tried a lot of things but still can't get it working. For example when I manually insert a time than it is working. But when I want to do it with an variable it is not working also when I am using the convert function for it. What am I doing wrong?
I use SQL Server 2008 R2 for this.
Quit concatenating strings to executed dynamic sql, use sp_executesql instead.
Your error can be correct by specifying dates in ISO format (or in the format Dan Bracuk mentioned in his comment). e.g. '2017-04-01T23:59:59.363'
#BackToBasics : Dating Responsibly - Aaron Bertrand
Here is how you would use sp_executesql instead:
alter procedure [dbo].[ProductionReport] (
#filterStartTime datetime,
#filterEndTime datetime,
#machine varchar(10)
) as
begin;
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on;
create table #tempProductionTable(
id varchar(3)
, ploeg varchar(3)
, starttime2 datetime
, endtime2 datetime
, daynumber int
);
declare #i int
, #params nvarchar(max)
, #sqlstring nvarchar(max)
, #id varchar(3)
, #ploeg varchar(3)
, #starttime datetime
, #endtime datetime;
set #params = '#id int, #ploeg varchar(3), #starttime datetime';
set #sqlstring = 'insert into #tempProductionTable (id,ploeg,starttime2) values (#id,#ploeg,#starttime);';
set #i = 0
while #i < 16
begin
set #i = #i+1
set #id = #i
set #starttime = convert(datetime, #filterStartTime,110)
set #ploeg = '2'
print #starttime
exec sp_executesql #sqlstring, #params, #id, #ploeg, #starttime;
end
-- Insert statements for procedure here
select * from #tempProductionTable
end;
go
rextester demo: http://rextester.com/KPBR1056

T-SQL How to check if DataTime contains a Date string?

I have a DateTime column row and I declare a date string:
Row:
2010-08-27 13:45:55
My string:
'2010-08-27'
How can I check if that string is in that row ?
I tried the following query:
declare #year as nvarchar(4)
declare #month as nvarchar(2)
declare #day as nvarchar(2)
set #year = '2010'
set #month = '08'
set #day = '23'
select * FROM [dbo].[customer_import] CsrImport
where
(YEAR(CsrImport.import_date) = #year
AND MONTH(CsrImport.import_date) = #month
AND DAY(CsrImport.import_date) = #day)
but I see that it returns all rows (even that are not contains that date)
Sql server : ISDATE (Transact-SQL)
----Invalid date
SELECT ISDATE('30/2/2007')
RETURNS : 0 (Zero)
----Valid date
SELECT ISDATE('12/12/20007)'
RETURNS : 1 (ONE)
----Invalid DataType
SELECT ISDATE('SQL')
RETURNS : 0 (Zero)
Like this, this will also be able to use the index, do not use function on the column itself..it is not SARGable!!
where import_date >= convert(datetime,#year + #month + #day)
and import_date < convert(datetime,#year + #month + #day) + 1
The best way for you would be to use dates and not 3 different parameters, what if someone passes in 13 for month?
Here is an example which checks that the values that are passed in can be converted to a date, if not it will show an error message
DECLARE #year AS NVARCHAR(4)
DECLARE #month AS NVARCHAR(2)
DECLARE #day AS NVARCHAR(2)
SET #year = '2010'
SET #month = '08'
SET #day = '23'
DECLARE #date DATETIME
IF ISDATE(#year + #month + #day) = 0
BEGIN
RAISERROR('values passed in are not a valid date',16,1)
RETURN
END
ELSE
BEGIN
SET #date = #year + #month + #day
END
SELECT * FROM [dbo].[customer_import] CsrImport
WHERE import_date >=#date
AND import_date < #date + 1
That should work, howabout if you make the values INTS
declare #year as INT
declare #month as INT
declare #day as INT
set #year = 2010
set #month = 08
set #day = 23
select * FROM [dbo].[customer_import] CsrImport
where
(YEAR(CsrImport.import_date) = #year
AND MONTH(CsrImport.import_date) = #month
AND DAY(CsrImport.import_date) = #day)
EDIT: Make sure all the statement is highlighted when you run it too. As simple as it seems, is it possible you mised the where clause if you highlighted the statement.
Just turn the datestring into a date (or datetime) variable and use a where clause:
Since your table has times in it, you have to strip them out or compare them to the midnioght before and after
Declare #myDate DateTime
Set #myDate = 'August 23 2010'
Select * FROM [dbo].[customer_import] CsrImport
Where DateDiff(day, myDate,import_date) = 0 -- Not Sargable
or
Declare #myDate DateTime
Set #myDate = 'August 23 2010'
Select * FROM [dbo].[customer_import] CsrImport
Where import_date) Between #mydate And #Mydate + 1 -- Sargable

SQL Scalar UDF to get number of days in Year

I writing code to determine how many days in a year. I am trying to keep it really simple.
I found code that I think is very clean to determine a leap year. I am passing the inputted date using DATEPART(Y,#Year) to the leap year program and some how am not getting the correct results so I has to be in my SQL code to process the input date as the correct bit is returned.
Here is the code for the Leap Year:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[FN_Is_Leap_Year]
(
-- the parameters for the function here
#year int
)
RETURNS BIT
AS
BEGIN
RETURN (select case datepart(mm, dateadd(dd, 1, cast((cast(#year as varchar(4)) + '0228') as datetime)))
WHEN 2 THEN 1
ELSE 0 END)
END
Here is the code I wrote to process the input date & get the # days in a year:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[FN_Get_Days_In_Year]
(
#InputDT varchar(10)
)
RETURNS int
AS
BEGIN
DECLARE #Result int,
#Year int
Set #Result =
CASE
WHEN dbo.FN_Is_Leap_Year(Datepart(yyyy,#Year)) = 0 Then 365
WHEN dbo.FN_Is_Leap_Year(Datepart(yyyy,#Year)) = 1 Then 366
END
RETURN #Result
END
Got it working!!
GO
ALTER FUNCTION [dbo].[FN_Get_Days_In_Year]
(
#InputDT int
)
RETURNS varchar(3)
AS
BEGIN
Declare #Year int,
#RetVal bit,
#Result varchar(3)
Set #Year = datepart(yy, #InputDT)
Set #RetVal = dbo.FN_Is_Leap_Year(Datepart(yy,'2012'))
Set #Result = CASE #RetVal
WHEN 1 THEN 366
ELSE 365
End
Return #Result
END
Modified version of the above answer :
DECLARE #year INT,
#DaysInYear INT
SET #year = 2011
SELECT #DaysInYear = CASE DATEPART(mm, DATEADD(dd, 1, CAST((CAST(#year AS VARCHAR(4)) + '0228') AS DATETIME)))
WHEN 2 THEN 366 ELSE 365 END
SELECT #DaysInYear 'DaysInYear'

How to convert DATETIME to FILETIME value in T-SQL?

I need to convert a SQL Server DATETIME value to FILETIME in a T-SQL SELECT statement (on SQL Server 2000). Is there a built-in function to do this? If not, can someone help me figure out how to implement this conversion routine as a UDF (or just plain Transact-SQL)? Here is what I know:
FILETIME is 64-bit value representing the number of 100-nanosecond intervals since
January 1, 1601 (UTC) (per MSDN: FILETIME Structure).
SQL Server base time starts on 1900-01-01 00:00:00 (per SELECT CAST(0 as DATETIME)).
I found several examples showing how to convert FILETIME values to T-SQL DATETIME (I'm not 100% sure they are accurate, though), but could not find anything about reverse conversion. Even the general idea (or algorithm) would help.
Okay, I think I was able to implement this myself. Here is the function:
IF EXISTS
(
SELECT 1
FROM sysobjects
WHERE id = OBJECT_ID('[dbo].[fnDateTimeToFileTime]')
AND type = 'FN'
)
BEGIN
DROP FUNCTION [dbo].[fnDateTimeToFileTime]
END
GO
-- Create function.
CREATE FUNCTION [dbo].[fnDateTimeToFileTime]
(
#DateTime AS DATETIME
)
RETURNS
BIGINT
BEGIN
IF #DateTime IS NULL
RETURN NULL
DECLARE #MsecBetween1601And1970 BIGINT
DECLARE #MsecBetween1970AndDate BIGINT
SET #MsecBetween1601And1970 = 11644473600000
SET #MsecBetween1970AndDate =
DATEDIFF(ss, CAST('1970-01-01 00:00:00' as DATETIME), #DateTime) *
CAST(1000 AS BIGINT)
RETURN (#MsecBetween1601And1970 + #MsecBetween1970AndDate) * CAST(10000 AS BIGINT)
END
GO
IF ##ERROR = 0
GRANT EXECUTE ON [dbo].[fnDateTimeToFileTime] TO Public
GO
It seems to be accurate up to 1 second, which is okay with me (I could not make it more accurate due to data overflow). I used the TimeAndDate web tool to calculate the durations between dates.
What do you think?
2 SQL Server time era starts on
1900-01-01 00:00:00 (per SELECT CAST(0
as DATETIME).
No, that is the base date, datetime starts at 1753
run this
select cast('17800122' as datetime)
output
1780-01-22 00:00:00.000
But this is still less than filetime so you need to add that...however remember the gregorian and Julian calendars (also the reason that datetime starts at 1753)
The accepted answer work well, but will crash for date above 19 January 2038. Either use
DATEDIFF_BIG instead of DATEDIFF if you are on SQL Server 2016 or above, or use the following correction
CREATE FUNCTION [dbo].[fnDateTimeToFileTime]
(
#DateTime AS DATETIME
)
RETURNS
BIGINT
BEGIN
IF #DateTime IS NULL
RETURN NULL
DECLARE #MsecBetween1601And1970 BIGINT
DECLARE #MsecBetween1970AndDate BIGINT
DECLARE #MaxNumberDayBeforeOverflowDateDiff int;
SET #MaxNumberDayBeforeOverflowDateDiff = 24855; --SELECT DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), CAST('2038-01-19 00:00:00' as DATETIME))
DECLARE #nbMaxDaysBetween1970AndDate int;
SET #nbMaxDaysBetween1970AndDate = DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), #DateTime) / #MaxNumberDayBeforeOverflowDateDiff;
DECLARE #moduloResteDay int
SET #moduloResteDay = DATEDIFF(day, CAST('1970-01-01 00:00:00' as DATETIME), #DateTime) % #MaxNumberDayBeforeOverflowDateDiff;
DECLARE #nbSecondBefore19700101And20380119 bigint = 2147472000;
SET #MsecBetween1601And1970 = 11644473600000;
DECLARE #DateTimeModulo datetime;
SET #DateTimeModulo = DATEADD(day, -#nbMaxDaysBetween1970AndDate * #MaxNumberDayBeforeOverflowDateDiff, #DateTime)
SET #MsecBetween1970AndDate = CAST(CAST(#nbMaxDaysBetween1970AndDate as bigint) * #nbSecondBefore19700101And20380119 +
DATEDIFF(ss, CAST('1970-01-01 00:00:00' as DATETIME), #DateTimeModulo) as bigint)*
CAST(1000 AS BIGINT)
RETURN (#MsecBetween1601And1970 + #MsecBetween1970AndDate) * CAST(10000 AS BIGINT)
END

String.Format like functionality in T-SQL?

I'm looking for a built-in function/extended function in T-SQL for string manipulation similar to the String.Format method in .NET.
If you are using SQL Server 2012 and above, you can use FORMATMESSAGE. eg.
DECLARE #s NVARCHAR(50) = 'World';
DECLARE #d INT = 123;
SELECT FORMATMESSAGE('Hello %s, %d', #s, #d)
-- RETURNS 'Hello World, 123'
More examples from MSDN: FORMATMESSAGE
SELECT FORMATMESSAGE('Signed int %i, %d %i, %d, %+i, %+d, %+i, %+d', 5, -5, 50, -50, -11, -11, 11, 11);
SELECT FORMATMESSAGE('Signed int with leading zero %020i', 5);
SELECT FORMATMESSAGE('Signed int with leading zero 0 %020i', -55);
SELECT FORMATMESSAGE('Unsigned int %u, %u', 50, -50);
SELECT FORMATMESSAGE('Unsigned octal %o, %o', 50, -50);
SELECT FORMATMESSAGE('Unsigned hexadecimal %x, %X, %X, %X, %x', 11, 11, -11, 50, -50);
SELECT FORMATMESSAGE('Unsigned octal with prefix: %#o, %#o', 50, -50);
SELECT FORMATMESSAGE('Unsigned hexadecimal with prefix: %#x, %#X, %#X, %X, %x', 11, 11, -11, 50, -50);
SELECT FORMATMESSAGE('Hello %s!', 'TEST');
SELECT FORMATMESSAGE('Hello %20s!', 'TEST');
SELECT FORMATMESSAGE('Hello %-20s!', 'TEST');
SELECT FORMATMESSAGE('Hello %20s!', 'TEST');
NOTES:
Undocumented in 2012
Limited to 2044 characters
To escape the % sign, you need to double it.
If you are logging errors in extended events, calling FORMATMESSAGE comes up as a (harmless) error
take a look at xp_sprintf. example below.
DECLARE #ret_string varchar (255)
EXEC xp_sprintf #ret_string OUTPUT,
'INSERT INTO %s VALUES (%s, %s)', 'table1', '1', '2'
PRINT #ret_string
Result looks like this:
INSERT INTO table1 VALUES (1, 2)
Just found an issue with the max size (255 char limit) of the string with this so there is an alternative function you can use:
create function dbo.fnSprintf (#s varchar(MAX),
#params varchar(MAX), #separator char(1) = ',')
returns varchar(MAX)
as
begin
declare #p varchar(MAX)
declare #paramlen int
set #params = #params + #separator
set #paramlen = len(#params)
while not #params = ''
begin
set #p = left(#params+#separator, charindex(#separator, #params)-1)
set #s = STUFF(#s, charindex('%s', #s), 2, #p)
set #params = substring(#params, len(#p)+2, #paramlen)
end
return #s
end
To get the same result as above you call the function as follows:
print dbo.fnSprintf('INSERT INTO %s VALUES (%s, %s)', 'table1,1,2', default)
I have created a user defined function to mimic the string.format functionality.
You can use it.
stringformat-in-sql
UPDATE:
This version allows the user to change the delimitter.
-- DROP function will loose the security settings.
IF object_id('[dbo].[svfn_FormatString]') IS NOT NULL
DROP FUNCTION [dbo].[svfn_FormatString]
GO
CREATE FUNCTION [dbo].[svfn_FormatString]
(
#Format NVARCHAR(4000),
#Parameters NVARCHAR(4000),
#Delimiter CHAR(1) = ','
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
/*
Name: [dbo].[svfn_FormatString]
Creation Date: 12/18/2020
Purpose: Returns the formatted string (Just like in C-Sharp)
Input Parameters: #Format = The string to be Formatted
#Parameters = The comma separated list of parameters
#Delimiter = The delimitter to be used in the formatting process
Format: #Format = N'Hi {0}, Welcome to our site {1}. Thank you {0}'
#Parameters = N'Karthik,google.com'
#Delimiter = ','
Examples:
SELECT dbo.svfn_FormatString(N'Hi {0}, Welcome to our site {1}. Thank you {0}', N'Karthik,google.com', default)
SELECT dbo.svfn_FormatString(N'Hi {0}, Welcome to our site {1}. Thank you {0}', N'Karthik;google.com', ';')
*/
DECLARE #Message NVARCHAR(400)
DECLARE #ParamTable TABLE ( Id INT IDENTITY(0,1), Paramter VARCHAR(1000))
SELECT #Message = #Format
;WITH CTE (StartPos, EndPos) AS
(
SELECT 1, CHARINDEX(#Delimiter, #Parameters)
UNION ALL
SELECT EndPos + (LEN(#Delimiter)), CHARINDEX(#Delimiter, #Parameters, EndPos + (LEN(#Delimiter)))
FROM CTE
WHERE EndPos > 0
)
INSERT INTO #ParamTable ( Paramter )
SELECT
[Id] = SUBSTRING(#Parameters, StartPos, CASE WHEN EndPos > 0 THEN EndPos - StartPos ELSE 4000 END )
FROM CTE
UPDATE #ParamTable
SET
#Message = REPLACE(#Message, '{'+ CONVERT(VARCHAR, Id) + '}', Paramter )
RETURN #Message
END
There is a way, but it has its limitations. You can use the FORMATMESSAGE() function. It allows you to format a string using formatting similar to the printf() function in C.
However, the biggest limitation is that it will only work with messages in the sys.messages table. Here's an article about it: microsoft_library_ms186788
It's kind of a shame there isn't an easier way to do this, because there are times when you want to format a string/varchar in the database. Hopefully you are only looking to format a string in a standard way and can use the sys.messages table.
Coincidentally, you could also use the RAISERROR() function with a very low severity, the documentation for raiseerror even mentions doing this, but the results are only printed. So you wouldn't be able to do anything with the resulting value (from what I understand).
Good luck!
Raw t-sql is limited to CHARINDEX(), PATINDEX(), REPLACE(), and SUBSTRING() for string manipulation. But with sql server 2005 and later you can set up user defined functions that run in .Net, which means setting up a string.format() UDF shouldn't be too tough.
I think there is small correction while calculating end position.
Here is correct function
**>>**IF OBJECT_ID( N'[dbo].[FormatString]', 'FN' ) IS NOT NULL
DROP FUNCTION [dbo].[FormatString]
GO
/***************************************************
Object Name : FormatString
Purpose : Returns the formatted string.
Original Author : Karthik D V http://stringformat-in-sql.blogspot.com/
Sample Call:
SELECT dbo.FormatString ( N'Format {0} {1} {2} {0}', N'1,2,3' )
*******************************************/
CREATE FUNCTION [dbo].[FormatString](
#Format NVARCHAR(4000) ,
#Parameters NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
AS
BEGIN
--DECLARE #Format NVARCHAR(4000), #Parameters NVARCHAR(4000) select #format='{0}{1}', #Parameters='hello,world'
DECLARE #Message NVARCHAR(400), #Delimiter CHAR(1)
DECLARE #ParamTable TABLE ( ID INT IDENTITY(0,1), Parameter VARCHAR(1000) )
Declare #startPos int, #endPos int
SELECT #Message = #Format, #Delimiter = ','**>>**
--handle first parameter
set #endPos=CHARINDEX(#Delimiter,#Parameters)
if (#endPos=0 and #Parameters is not null) --there is only one parameter
insert into #ParamTable (Parameter) values(#Parameters)
else begin
insert into #ParamTable (Parameter) select substring(#Parameters,0,#endPos)
end
while #endPos>0
Begin
--insert a row for each parameter in the
set #startPos = #endPos + LEN(#Delimiter)
set #endPos = CHARINDEX(#Delimiter,#Parameters, #startPos)
if (#endPos>0)
insert into #ParamTable (Parameter)
select substring(#Parameters,#startPos,#endPos - #startPos)
else
insert into #ParamTable (Parameter)
select substring(#Parameters,#startPos,4000)
End
UPDATE #ParamTable SET #Message =
REPLACE ( #Message, '{'+CONVERT(VARCHAR,ID) + '}', Parameter )
RETURN #Message
END
Go
grant execute,references on dbo.formatString to public
One more idea.
Although this is not a universal solution - it is simple and works, at least for me :)
For one placeholder {0}:
create function dbo.Format1
(
#String nvarchar(4000),
#Param0 sql_variant
)
returns nvarchar(4000)
as
begin
declare #Null nvarchar(4) = N'NULL';
return replace(#String, N'{0}', cast(isnull(#Param0, #Null) as nvarchar(4000)));
end
For two placeholders {0} and {1}:
create function dbo.Format2
(
#String nvarchar(4000),
#Param0 sql_variant,
#Param1 sql_variant
)
returns nvarchar(4000)
as
begin
declare #Null nvarchar(4) = N'NULL';
set #String = replace(#String, N'{0}', cast(isnull(#Param0, #Null) as nvarchar(4000)));
return replace(#String, N'{1}', cast(isnull(#Param1, #Null) as nvarchar(4000)));
end
For three placeholders {0}, {1} and {2}:
create function dbo.Format3
(
#String nvarchar(4000),
#Param0 sql_variant,
#Param1 sql_variant,
#Param2 sql_variant
)
returns nvarchar(4000)
as
begin
declare #Null nvarchar(4) = N'NULL';
set #String = replace(#String, N'{0}', cast(isnull(#Param0, #Null) as nvarchar(4000)));
set #String = replace(#String, N'{1}', cast(isnull(#Param1, #Null) as nvarchar(4000)));
return replace(#String, N'{2}', cast(isnull(#Param2, #Null) as nvarchar(4000)));
end
and so on...
Such an approach allows us to use these functions in SELECT statement and with parameters of nvarchar, number, bit and datetime datatypes.
For example:
declare #Param0 nvarchar(10) = N'IPSUM' ,
#Param1 int = 1234567 ,
#Param2 datetime2(0) = getdate();
select dbo.Format3(N'Lorem {0} dolor, {1} elit at {2}', #Param0, #Param1, #Param2);
Actually there is no built in function similar to string.Format function of .NET is available in SQL server.
There is a function FORMATMESSAGE() in SQL server but it mimics to printf() function of C not string.Format function of .NET.
SELECT FORMATMESSAGE('This is the %s and this is the %s.', 'first variable', 'second variable') AS Result
Here is my version. Can be extended to accommodate more number of parameters and can extend formatting based on type. Currently only date and datetime types are formatted.
Example:
select dbo.FormatString('some string %s some int %s date %s','"abcd"',100,cast(getdate() as date),DEFAULT,DEFAULT)
select dbo.FormatString('some string %s some int %s date time %s','"abcd"',100,getdate(),DEFAULT,DEFAULT)
Output:
some string "abcd" some int 100 date 29-Apr-2017
some string "abcd" some int 100 date time 29-Apr-2017 19:40
Functions:
create function dbo.FormatValue(#param sql_variant)
returns nvarchar(100)
begin
/*
Tejasvi Hegde, 29-April-2017
Can extend formatting here.
*/
declare #result nvarchar(100)
if (SQL_VARIANT_PROPERTY(#param,'BaseType') in ('date'))
begin
select #result = REPLACE(CONVERT(CHAR(11), #param, 106), ' ', '-')
end
else if (SQL_VARIANT_PROPERTY(#param,'BaseType') in ('datetime','datetime2'))
begin
select #result = REPLACE(CONVERT(CHAR(11), #param, 106), ' ', '-')+' '+CONVERT(VARCHAR(5),#param,108)
end
else
begin
select #result = cast(#param as nvarchar(100))
end
return #result
/*
BaseType:
bigint
binary
char
date
datetime
datetime2
datetimeoffset
decimal
float
int
money
nchar
numeric
nvarchar
real
smalldatetime
smallint
smallmoney
time
tinyint
uniqueidentifier
varbinary
varchar
*/
end;
create function dbo.FormatString(
#format nvarchar(4000)
,#param1 sql_variant = null
,#param2 sql_variant = null
,#param3 sql_variant = null
,#param4 sql_variant = null
,#param5 sql_variant = null
)
returns nvarchar(4000)
begin
/*
Tejasvi Hegde, 29-April-2017
select dbo.FormatString('some string value %s some int %s date %s','"abcd"',100,cast(getdate() as date),DEFAULT,DEFAULT)
select dbo.FormatString('some string value %s some int %s date time %s','"abcd"',100,getdate(),DEFAULT,DEFAULT)
*/
declare #result nvarchar(4000)
select #param1 = dbo.formatValue(#param1)
,#param2 = dbo.formatValue(#param2)
,#param3 = dbo.formatValue(#param3)
,#param4 = dbo.formatValue(#param4)
,#param5 = dbo.formatValue(#param5)
select #param2 = cast(#param2 as nvarchar)
EXEC xp_sprintf #result OUTPUT,#format , #param1, #param2, #param3, #param4, #param5
return #result
end;
here's what I found with my experiments using the built-in
FORMATMESSAGE() function
sp_addmessage #msgnum=50001,#severity=1,#msgText='Hello %s you are #%d',#replace='replace'
SELECT FORMATMESSAGE(50001, 'Table1', 5)
when you call up sp_addmessage, your message template gets stored into the system table master.dbo.sysmessages (verified on SQLServer 2000).
You must manage addition and removal of template strings from the table yourself, which is awkward if all you really want is output a quick message to the results screen.
The solution provided by Kathik DV, looks interesting but doesn't work with SQL Server 2000, so i altered it a bit, and this version should work with all versions of SQL Server:
IF OBJECT_ID( N'[dbo].[FormatString]', 'FN' ) IS NOT NULL
DROP FUNCTION [dbo].[FormatString]
GO
/***************************************************
Object Name : FormatString
Purpose : Returns the formatted string.
Original Author : Karthik D V http://stringformat-in-sql.blogspot.com/
Sample Call:
SELECT dbo.FormatString ( N'Format {0} {1} {2} {0}', N'1,2,3' )
*******************************************/
CREATE FUNCTION [dbo].[FormatString](
#Format NVARCHAR(4000) ,
#Parameters NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
AS
BEGIN
--DECLARE #Format NVARCHAR(4000), #Parameters NVARCHAR(4000) select #format='{0}{1}', #Parameters='hello,world'
DECLARE #Message NVARCHAR(400), #Delimiter CHAR(1)
DECLARE #ParamTable TABLE ( ID INT IDENTITY(0,1), Parameter VARCHAR(1000) )
Declare #startPos int, #endPos int
SELECT #Message = #Format, #Delimiter = ','
--handle first parameter
set #endPos=CHARINDEX(#Delimiter,#Parameters)
if (#endPos=0 and #Parameters is not null) --there is only one parameter
insert into #ParamTable (Parameter) values(#Parameters)
else begin
insert into #ParamTable (Parameter) select substring(#Parameters,0,#endPos)
end
while #endPos>0
Begin
--insert a row for each parameter in the
set #startPos = #endPos + LEN(#Delimiter)
set #endPos = CHARINDEX(#Delimiter,#Parameters, #startPos)
if (#endPos>0)
insert into #ParamTable (Parameter) select substring(#Parameters,#startPos,#endPos)
else
insert into #ParamTable (Parameter) select substring(#Parameters,#startPos,4000)
End
UPDATE #ParamTable SET #Message = REPLACE ( #Message, '{'+CONVERT(VARCHAR,ID) + '}', Parameter )
RETURN #Message
END
Go
grant execute,references on dbo.formatString to public
Usage:
print dbo.formatString('hello {0}... you are {1}','world,good')
--result: hello world... you are good
At the moment this doesn't really exist (although you can of course write your own). There is an open connect bug for it: https://connect.microsoft.com/SQLServer/Feedback/Details/3130221, which as of this writing has just 1 vote.
Not exactly, but I would check out some of the articles on string handling (amongst other things) by "Phil Factor" (geddit?) on Simple Talk.
this is bad approach. you should work with assembly dll's, in which will do the same for you with better performance.