Conversion failed when converting date and/or time from character string - SQL Server 2008 R2 - 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

Related

Just Exec SP works but Insert into table Exec SP goes in infinite loop

Question 1)
We are planning to turn off XP CMDSHELL in SQL. Is there any alternative to the method I am using below:
Question 2)
Below SP
1.Creates new SQL Job every time
2.Runs the CMD command passed to it from SP param
3.Finally SP return result
USE [Test]
GO
/****** Object: StoredProcedure [dbo].[CMDSHELL_ALTERNATIVE] Script Date: 5/8/2020 5:44:39 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[CMDSHELL_ALTERNATIVE]
#Command varchar(200),
#InvokedBySP varchar(100) = 'AdhocCalling'
AS
BEGIN
DECLARE #OutputFileName varchar(max)
DECLARE #OutputFilePath varchar(max)
DECLARE #JobOutcome int = 5
DECLARE #JobMessage varchar(max)
DECLARE #TodayDate DATE = GETDATE()
DECLARE #JobExecutionTime DATETIME
DECLARE #RandomeNumber BIGINT = CAST(RAND() * 1000000 AS BIGINT)
DECLARE #JobName varchar(100) = 'CMDSHELL_JOB_'+ CAST(#TodayDate AS VARCHAR)+'_'+CAST(#RandomeNumber AS VARCHAR)
SET #OutputFilePath ='C:\Users\OutputFiles\'
SET #OutputFileName = #JobName+'.txt'
DECLARE #FullOutputFilePathName varchar(max) = #OutputFilePath+#OutputFileName
DECLARE #jobId BINARY(16)
EXEC msdb.dbo.sp_add_job #job_name=#JobName,
#enabled=1,
#notify_level_eventlog=0,
#notify_level_email=2,
#notify_level_page=2,
#delete_level=0,
#category_name=N'[Uncategorized (Local)]',
#owner_login_name=N'sa', #job_id = #jobId OUTPUT
EXEC msdb.dbo.sp_add_jobserver #job_name=#JobName, #server_name = N'(local)'
EXEC msdb.dbo.sp_add_jobstep #job_name=#JobName, #step_name=N'step-1',
#step_id=1,
#cmdexec_success_code=0,
#on_success_action=1,
#on_fail_action=2,
#retry_attempts=0,
#retry_interval=0,
#os_run_priority=0, #subsystem=N'CmdExec',
#command=N'',
#database_name=N'master',
#output_file_name=N'',
#flags=0
EXEC msdb.dbo.sp_update_job #job_name=#JobName,
#enabled=1,
#start_step_id=1,
#notify_level_eventlog=0,
#notify_level_email=2,
#notify_level_page=2,
#delete_level=0,
#description=N'',
#category_name=N'[Uncategorized (Local)]',
#owner_login_name=N'sa',
#notify_email_operator_name=N'',
#notify_page_operator_name=N''
EXEC msdb.dbo.sp_update_jobstep
#job_name=#JobName,
#step_id=1 ,
#command=#Command,
#output_file_name=#FullOutputFilePathName
EXEC msdb.dbo.sp_start_job #JobName
SELECT distinct #JobOutcome = SJH.run_status
FROM msdb..sysjobhistory SJH, msdb..sysjobs SJ
WHERE SJH.job_id = SJ.job_id and SJ.Name = #JobName
WHILE (#JobOutcome != 3 AND #JobOutcome > 1)
BEGIN
print 'In a delay loop'
WAITFOR DELAY '00:00:02';
SELECT distinct #JobOutcome = SJH.run_status
FROM msdb..sysjobhistory SJH, msdb..sysjobs SJ
WHERE SJH.job_id = SJ.job_id and SJ.Name = #JobName
END
SET #JobExecutionTime = GETDATE()
SELECT #JobMessage = message FROM msdb..sysjobhistory SJH, msdb..sysjobs SJ
WHERE SJH.job_id = SJ.job_id and SJ.Name = #JobName
AND step_id = 1
IF #JobOutcome = 1
BEGIN
print 'Job successfull'
Declare #BulkInsertCommand varchar(max)
CREATE TABLE #temp (results NVARCHAR(755))
SET #BulkInsertCommand =
'BULK INSERT #temp
FROM ''' + #FullOutputFilePathName +'''
WITH
(
ROWTERMINATOR = ''\n'',
DataFileType=''widechar''
)'
EXEC (#BulkInsertCommand)
Select results from #temp
execute Test.dbo.CLR_CMDSHELL_LOGGING #JobName,#InvokedBySP,#Command,#JobExecutionTime,'Job Successfull',#JobMessage
END
ELSE
BEGIN
print 'Dynamic Job failed'
execute Test.dbo.CLR_CMDSHELL_LOGGING #JobName,#InvokedBySP,#Command,#JobExecutionTime,'Job Failed',#JobMessage
END
IF EXISTS (SELECT job_id FROM msdb.dbo.sysjobs_view WHERE name = #JobName)
EXEC msdb.dbo.sp_delete_job #job_name=#JobName
END
Now I run above SP using below code -
DECLARE #cmd VARCHAR(100)
SET #cmd = 'DIR /B C:\Users\Test'
--DECLARE #dirTable TABLE (oPut VARCHAR(max))
--insert #dirTable
EXEC [dbo].CMDSHELL_ALTERNATIVE #cmd
--select * from #dirTable
Result without Insert
enter image description here
Problem
Code above without insert works fine, returns result quickly. But when Insert statement is uncommented, SP runs forever.
enter image description hereenter code here
I can't figure out exactly where the blocking is. Any alternative/fix in above?

TSQL procedure call to PLSQL procedure : ORA-06502: PL/SQL: numeric or value error

I have a T-SQL procedure calling an Oracle procedure passing in some parameters.
My oracle procedure works fine and returns the output parameters as expected.
However when calling the oracle procedure from the T-SQL procedure, I have the following error. I can't understand why am I getting this. Varchars are being used in both procedures.
(1 row(s) affected) OLE DB provider "OraOLEDB.Oracle" for linked
server "ORA_LINK_SERVER" returned message "ORA-06502: PL/SQL: numeric
or value error ORA-06512: at "DBNAME.GETSHELLACNOANDINSTRUCTIONNO",
line 7".
(1 row(s) affected)
(1 row(s) affected) Msg 7215, Level 17, State 1, Procedure
USP_TXN_PF_CLIENT_URL_NEW, Line 56 Could not execute statement on
remote server 'ORA_LINK_SERVER'.
This is my T-SQL procedure
USE [TVE]
GO
/****** Object: StoredProcedure [CompanyName].[USP_TXN_PF_CLIENT_URL] Script Date: 12/29/2016 11:14:02 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--To test +
/*
Declare #ReturnString varchar(255)
EXEC [CompanyName].[USP_TXN_PF_CLIENT_URL_NEW] 'CompanyName','50585609',#ReturnString = #ReturnString OUTPUT
select #ReturnString
*/
/*
ALTER TABLE [dbo].[PF_CLIENT_URLS]
ADD [STATUS] VARCHAR(1) NULL
GO
SELECT * FROM [dbo].[PF_CLIENT_URLS]
*/
ALTER PROCEDURE [CompanyName].[USP_TXN_PF_CLIENT_URL_NEW]
(
#IN_PMancoID VARCHAR(10),
#IN_PInvestorRef VARCHAR(12),
#ReturnString varchar(255) OUTPUT
)
AS
BEGIN
DECLARE #runStr VARCHAR(2000)
DECLARE #URL varchar(36)
DECLARE #MessageID AS VARCHAR(40), #RecipientID AS VARCHAR(40), #FileName VARCHAR(40),#ResponseFileName VARCHAR(40), #Message VARCHAR(500),
#Cell VARCHAR(35), #OUT_ShellAccountNumber VARCHAR(40), #OUT_InstructionNumber VARCHAR(40)
DECLARE #ParamKeys VARCHAR(max), #ParamValues VARCHAR(max), #Return_ID INT, #Current_User_ID INT,
#ErrorNumber INT, #ErrorSeverity INT, #ErrorState INT, #ErrorLine INT,
#ErrorMessage nVARCHAR(2048), #LogInfo_ID INT, #ProcedureName NVARCHAR(126),
#ProfileName VARCHAR(50), #Recipients VARCHAR(max), #Subject VARCHAR(100), #Body VARCHAR(max)
Set #ProcedureName = 'YYYY.USP_PF_CLIENT_URL_NEW'
Set #ParamKeys = ''
Set #ParamValues = ''
set #URL = newID()
Set #ParamKeys = 'IN_PMancoID' + '|' +
'IN_PInvestorRef'
Set #ParamValues = ISNULL(CONVERT(VARCHAR(max),#IN_PMancoID),'NULL') + '|' +
ISNULL(CONVERT(VARCHAR(max),#IN_PInvestorRef),'NULL')
exec [DYNAMIC].[ss_StoredProcLogInfo_Save] #ProcName = #ProcedureName,
#ParameterKeys = #ParamKeys,
#ParameterValues = #ParamValues,
#User_ID = #Current_User_ID,
#Return_ID = #Return_ID OUTPUT
exec ('CALL DBNAME.GETSHELLACNOANDINSTRUCTIONNO(?,?,?)', #IN_PInvestorRef, #OUT_ShellAccountNumber, #OUT_InstructionNumber) AT ORA_LINK_SERVER
INSERT INTO [dbo].[TableName]([UserID],[MancoID],[Reference],[DateCreated],[LoginAttempts],[STATUS],[ShellAccount],[InstructionNumber])
values(ISNULL(#URL, ''),'CompanyName',ISNULL(#IN_PInvestorRef, ''),getdate(),0,'I', ISNULL(#OUT_ShellAccountNumber,''),ISNULL(#OUT_InstructionNumber,''))
set #ReturnString = #URL
--Select #ReturnString as ReturnString
END
And this is my oracle procedure. The link server is fine.
CREATE OR REPLACE
PROCEDURE GETSHELLACNOANDINSTRUCTIONNO
( IN_PInvestorRef IN VARCHAR2(12)
, OUT_ShellAccountNumber OUT VARCHAR2(40)
, OUT_InstructionNumber OUT VARCHAR2(40)
) AS
BEGIN
Select SHELL_ACCOUNT, INSTRUCTIONNUMBER into OUT_ShellAccountNumber, OUT_InstructionNumber
from pf
where client_id = IN_PInvestorRef;
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END GETSHELLACNOANDINSTRUCTIONNO;

Dynamic SQL reads a local variable as a Table Variable?

This is NOT about using a table variable - this is about using a local variable to carry a db address within a dynamic SQL cursor which theoretically would work as follows:
-- Assume the global variables #sql, AnalysisLocation, and #sp_executeSql have been declared.
ALTER PROCEDURE [dbo].[sp_AggregateCompliance_Report]
#clientID int,
#InvScrDBLocation nvarchar(250),
#JoinFilter nvarchar(max) = '',
#Criteria nvarchar(max) = '',
#Year int = NULL
as
declare #sql nvarchar(4000)
set #sql = '
IF EXISTS (SELECT * FROM sys.tables WHERE name = ''tmp_Aggregate_Compliance_counts'')
TRUNCATE TABLE tmp_Aggregate_Compliance_counts
ELSE
CREATE TABLE tmp_Aggregate_Compliance_counts (
pfc_fk_prv_pkid int,
RxYear int,
RxMonth int,
Compliance decimal (6,5))
' print #sql EXEC sp_executesql #sql
SET #Criteria = isnull(case when #Criteria like 'WHERE %' then 'AND '+substring(#criteria,7,len(#criteria)-6) else #Criteria end ,'')
SET #Year = isnull(#year, year(getdate())-1)
set #sql = '
DECLARE #fk_cli_pkid INT
, #ServerAndDB_for_pfcAppended nvarchar(100)
DECLARE client_set CURSOR FOR
SELECT DISTINCT mtx.fk_cli_pkid, SettingValue+ ''.dbo.pfc_appended''
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
JOIN prola7.Invoice_Screens.dbo.client_definition AS def
ON mtx.fk_cli_pkID = def.fk_cli_pkid
AND fk_lkSettings_pkID = 45
AND RecordStatus = 1
OPEN client_set
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
WHILE ##FETCH_STATUS = 0 BEGIN
INSERT INTO tmp_Aggregate_Compliance_counts (pfc_fk_prv_pkid, RxYear, RxMonth, Compliance)
SELECT pfc.pfc_fk_prv_pkid
, year(mtx.pfc_dateofservice) AS RxYear
, 0 AS RxMonth
, cast(mtx.Compliance as decimal (6,5))
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
LEFT OUTER JOIN #ServerAndDB_for_pfcAppended AS pfc
ON mtx.pp_clientfile = pfc.pp_clientfile
AND mtx.pp_mirror_pkid = pfc.pp_mirror_pkid
AND mtx.fk_cli_pkid = #fk_cli_pkid
'+#JoinFilter+'
WHERE pfc.pfc_status = 0
AND year(mtx.pfc_dateofservice) = '+cast(#Year as nvarchar)+'
'+#Criteria+'
GROUP BY pfc.pfc_fk_prv_pkid, year(mtx.pfc_dateofservice)
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
END
CLOSE client_set
DEALLOCATE client_set
' print #sql EXEC sp_executesql #sql
This creates no syntax errors when compiling the dynamic code, however when calling this procedure: Msg 1087, Level 15, State 2, Line 27
Must declare the table variable "#ServerAndDB_for_pfcAppended".
When I use this type of structure passing the location variable in as a global variable from outside the procedure it accepts it correctly, however as a local variable it seems to default to presuming I intend it to be a table variable.
I do NOT want to create a table variable. Is this an impossible structure?
The error is caused by the fact that you are attempting to have a parametrised table name. This is not possible, and whenever a table name should be a parameter, a dynamic query is used, basically like this:
SET #sql = 'SELECT … FROM ' + #tablename + ' WHERE …'
I think, in your situation the cursor should be taken out of the dynamic query, except for the part that uses the parametrised table name. Something like this should probably do:
ALTER PROCEDURE [dbo].[sp_AggregateCompliance_Report]
#clientID int,
#InvScrDBLocation nvarchar(250),
#JoinFilter nvarchar(max) = '',
#Criteria nvarchar(max) = '',
#Year int = NULL
as
declare #sql nvarchar(4000)
set #sql = '
IF EXISTS (SELECT * FROM sys.tables WHERE name = ''tmp_Aggregate_Compliance_counts'')
TRUNCATE TABLE tmp_Aggregate_Compliance_counts
ELSE
CREATE TABLE tmp_Aggregate_Compliance_counts (
pfc_fk_prv_pkid int,
RxYear int,
RxMonth int,
Compliance decimal (6,5))
' print #sql EXEC sp_executesql #sql
SET #Criteria = isnull(case when #Criteria like 'WHERE %' then 'AND '+substring(#criteria,7,len(#criteria)-6) else #Criteria end ,'')
SET #Year = isnull(#year, year(getdate())-1)
DECLARE #fk_cli_pkid INT
, #ServerAndDB_for_pfcAppended nvarchar(100)
DECLARE client_set CURSOR FOR
SELECT DISTINCT mtx.fk_cli_pkid, SettingValue+ ''.dbo.pfc_appended''
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
JOIN prola7.Invoice_Screens.dbo.client_definition AS def
ON mtx.fk_cli_pkID = def.fk_cli_pkid
AND fk_lkSettings_pkID = 45
AND RecordStatus = 1
OPEN client_set
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
WHILE ##FETCH_STATUS = 0 BEGIN
set #sql = '
INSERT INTO tmp_Aggregate_Compliance_counts (pfc_fk_prv_pkid, RxYear, RxMonth, Compliance)
SELECT pfc.pfc_fk_prv_pkid
, year(mtx.pfc_dateofservice) AS RxYear
, 0 AS RxMonth
, cast(mtx.Compliance as decimal (6,5))
FROM mtx_ComplianceAndEarlyRefill_tracking AS mtx
LEFT OUTER JOIN #ServerAndDB_for_pfcAppended AS pfc
ON mtx.pp_clientfile = pfc.pp_clientfile
AND mtx.pp_mirror_pkid = pfc.pp_mirror_pkid
AND mtx.fk_cli_pkid = #fk_cli_pkid
'+#JoinFilter+'
WHERE pfc.pfc_status = 0
AND year(mtx.pfc_dateofservice) = '+cast(#Year as nvarchar)+'
'+#Criteria+'
GROUP BY pfc.pfc_fk_prv_pkid, year(mtx.pfc_dateofservice)
' print #sql EXEC sp_executesql #sql
FETCH next FROM client_set
INTO #fk_cli_pkid, #ServerAndDB_for_pfcAppended
END
CLOSE client_set
DEALLOCATE client_set

How to get sp_executesql result into a variable?

I have a piece of dynamic SQL I need to execute, I then need to store the result into a variable.
I know I can use sp_executesql but can't find clear examples around about how to do this.
If you have OUTPUT parameters you can do
DECLARE #retval int
DECLARE #sSQL nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #tablename nvarchar(50)
SELECT #tablename = N'products'
SELECT #sSQL = N'SELECT #retvalOUT = MAX(ID) FROM ' + #tablename;
SET #ParmDefinition = N'#retvalOUT int OUTPUT';
EXEC sp_executesql #sSQL, #ParmDefinition, #retvalOUT=#retval OUTPUT;
SELECT #retval;
But if you don't, and can not modify the SP:
-- Assuming that your SP return 1 value
create table #temptable (ID int null)
insert into #temptable exec mysp 'Value1', 'Value2'
select * from #temptable
Not pretty, but works.
DECLARE #vi INT
DECLARE #vQuery NVARCHAR(1000)
SET #vQuery = N'SELECT #vi= COUNT(*) FROM <TableName>'
EXEC SP_EXECUTESQL
#Query = #vQuery
, #Params = N'#vi INT OUTPUT'
, #vi = #vi OUTPUT
SELECT #vi
DECLARE #tab AS TABLE (col1 VARCHAR(10), col2 varchar(10))
INSERT into #tab EXECUTE sp_executesql N'
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2
UNION ALL
SELECT 1 AS col1, 2 AS col2'
SELECT * FROM #tab
Return values are generally not used to "return" a result but to return success (0) or an error number (1-65K). The above all seem to indicate that sp_executesql does not return a value, which is not correct. sp_executesql will return 0 for success and any other number for failure.
In the below, #i will return 2727
DECLARE #s NVARCHAR(500)
DECLARE #i INT;
SET #s = 'USE [Blah]; UPDATE STATISTICS [dbo].[TableName] [NonExistantStatisticsName];';
EXEC #i = sys.sp_executesql #s
SELECT #i AS 'Blah'
SSMS will show this
Msg 2727, Level 11, State 1, Line 1
Cannot find index 'NonExistantStaticsName'.
If you want to return more than 1 value use this:
DECLARE #sqlstatement2 NVARCHAR(MAX);
DECLARE #retText NVARCHAR(MAX);
DECLARE #ParmDefinition NVARCHAR(MAX);
DECLARE #retIndex INT = 0;
SELECT #sqlstatement = 'SELECT #retIndexOUT=column1 #retTextOUT=column2 FROM XXX WHERE bla bla';
SET #ParmDefinition = N'#retIndexOUT INT OUTPUT, #retTextOUT NVARCHAR(MAX) OUTPUT';
exec sp_executesql #sqlstatement, #ParmDefinition, #retIndexOUT=#retIndex OUTPUT, #retTextOUT=#retText OUTPUT;
returned values are in #retIndex and #retText
Declare #variable int
Exec #variable = proc_name
DECLARE #ValueTable TABLE
(
Value VARCHAR (100)
)
SELECT #sql = N'SELECT SRS_SizeSetDetails.'+#COLUMN_NAME+' FROM SRS_SizeSetDetails WHERE FSizeID = '''+#FSizeID+''' AND SRS_SizeSetID = '''+#SRS_SizeSetID+'''';
INSERT INTO #ValueTable
EXEC sp_executesql #sql;
SET #Value='';
SET #Value = (SELECT TOP 1 Value FROM #ValueTable)
DELETE FROM #ValueTable
This worked for me:
DECLARE #SQL NVARCHAR(4000)
DECLARE #tbl Table (
Id int,
Account varchar(50),
Amount int
)
-- Lots of code to Create my dynamic sql statement
insert into #tbl EXEC sp_executesql #SQL
select * from #tbl
Here's something you can try
DECLARE #SqlStatement NVARCHAR(MAX) = ''
,#result XML
,#DatabaseName VARCHAR(100)
,#SchemaName VARCHAR(10)
,#ObjectName VARCHAR(200);
SELECT #DatabaseName = 'some database'
,#SchemaName = 'some schema'
,#ObjectName = 'some object (Table/View)'
SET #SqlStatement = '
SELECT #result = CONVERT(XML,
STUFF( ( SELECT *
FROM
(
SELECT TOP(100)
*
FROM ' + QUOTENAME(#DatabaseName) +'.'+ QUOTENAME(#SchemaName) +'.' + QUOTENAME(#ObjectName) + '
) AS A1
FOR XML PATH(''row''), ELEMENTS, ROOT(''recordset'')
), 1, 0, '''')
)
';
EXEC sp_executesql #SqlStatement,N'#result XML OUTPUT', #result = #result OUTPUT;
SELECT DISTINCT
QUOTENAME(r.value('fn:local-name(.)', 'VARCHAR(200)')) AS ColumnName
FROM #result.nodes('//recordset/*/*') AS records(r)
ORDER BY ColumnName
This was a long time ago, so not sure if this is still needed, but you could use ##ROWCOUNT variable to see how many rows were affected with the previous sql statement.
This is helpful when for example you construct a dynamic Update statement and run it with exec. ##ROWCOUNT would show how many rows were updated.
Here is the definition

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.