I am working on a script in a stored procedure to insert a new line that includes a getdate() value, and then select the line that has that same getdate() value. In practice, my classic ASP page will call one stored procedure after the other (the first being an insert, the next, the select).
When I construct these two queries in SQL Server Mgmt Studio, they work fine; however, once I put them into stored procedures, the select stored procedure throws an error
Conversion failed when converting date and/or time from character string
I'm not sure where the "character string" is, though, unless it is a reference to the dynamic SQL string.
Thanks for any leads. I've posted code below.
Here is the code I use in Mgmt Studio query, which works:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[001_test]
(
[ID] [INT] IDENTITY(1,1) NOT NULL,
[name] [NVARCHAR](50) NULL,
[dateadded] [DATETIME] NULL
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[001_test] ON
INSERT INTO [dbo].[001_test] ([ID], [name], [dateadded])
VALUES (1, N'toot', NULL), (2, N'hickory', NULL),
(3, N'orange', NULL)
SET IDENTITY_INSERT [dbo].[001_test] OFF
INSERT INTO [dbo].[001_test] ([name], [dateadded])
VALUES ('boy', GETDATE())
SELECT
[ID], [name], [dateadded]
FROM
[dbo].[001_test]
WHERE
dateadded = GETDATE()
GO
Here is the insert stored procedure, which does not throw the error. It performs the insert successfully:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[001_test_insert]
#Name VARCHAR(50)
AS
DECLARE #sql NVARCHAR(4000)
SELECT #sql = ' INSERT INTO [001_test] ([name], [dateadded])' +
' VALUES (#Name, GETDATE());'
EXEC sp_executesql #sql, N'#Name VARCHAR(50)', #Name
Here is the stored procedure that is throwing the error (in practice, this stored procedure would be called from a classic ASP page, just after the insert stored procedure). Would the GETDATE() need to be cast for the datetime datatype?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[001_test_select]
AS
SET NOCOUNT OFF
DECLARE #sql NVARCHAR(4000)
SELECT #sql = ' SELECT ID, Name, dateadded ' +
' FROM [001_test]' +
' WHERE dateadded = ' + GETDATE() + ';'
EXEC sp_executesql #sql
Because you are using dynamic sql, and you can't concat the string with the datetime returned by getdate(). You can define a parameter in your dynamic sql:
DECLARE #current_dt datetime = getdate();
SELECT #sql = ' SELECT ' +
' ID ' +
' ,Name ' +
' ,dateadded ' +
' FROM [001_test]' +
' WHERE ' +
' dateadded = #current_dt';
EXEC sp_executesql #sql, '#current_dt datetime', #current_dt;
You don't need dynamic sql here at all. I would simplify this procedure and remove the complication when it isn't needed. Something like this.
CREATE PROCEDURE [dbo].[001_test_insert]
(
#Name varchar(50)
) AS
set nocount on;
INSERT INTO [001_test]
(
[name]
,[dateadded]
)
VALUES
(
#Name
, getdate()
)
Related
I am trying to create a script in SQL Server 2017 that copies the values from one table to another in different schemas using the Stored Procedure of sp_MSForEachTable. So far I have been able to get the strings and when I print it it launches the query that I expect, but it throws problems like
'CREATE TRIGGER' must be the first statement in a query batch
and
Incorrect syntax near 'GO'.
A basic code structure is as follows:
EXEC sp_MSForEachTable
#precommand = 'use bd1',
#command1 =
'
DECLARE #tabla VARCHAR (MAX) = SUBSTRING(''?'', CHARINDEX(''.'', ''?'')+1, LEN(''?''));
SET #tabla = REPLACE(#tabla,''['','''');
SET #tabla = REPLACE(#tabla,'']'','''');
IF EXISTS (SELECT * FROM sys.objects WHERE [name] = N''TR_cloneField_''+#tabla+'''' AND [type] = ''TR'')
BEGIN
DROP TRIGGER [prueba_bd].[TR_cloneField_''+#tabla+'']
END
',
#command2 = '
DECLARE #tabla VARCHAR (MAX) = SUBSTRING(''?'', CHARINDEX(''.'', ''?'')+1, LEN(''?''));
SET #tabla = REPLACE(#tabla,''['','''');
SET #tabla = REPLACE(#tabla,'']'','''');
PRINT(#tabla);
DECLARE #sql VARCHAR (MAX);
CREATE TRIGGER [TR_cloneField_''+#tabla+'']
ON ?
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
SET #sql = ''
INSERT INTO schema2.''+#tabla+''
(
[field1] ,
[field2] ,
)
SELECT * FROM ?'';
EXEC(#sql)
END
GO
',
#whereand = 'and upper(schema_name(schema_id)) = ''schema2'''
GO
I appreciate any help
EDIT: Thanks Bill.
Yes, that was the problem. Being a T-SQL I could not put the Go. So what I did was put the entire Trigger command into a dynamic T-SQL, and I only run it on each cycle.
I have a scenario that #PRIVILEGEID will have multiple value and #ROLEID will be same every time for those privilege Id.
So I have to insert the data into table.
Below is the Code For Procedure:-
ALTER PROCEDURE [dbo].[ADD_ROLE] (
#ROLENAME varchar(50),
#PRIVILEGEID int )
AS
BEGIN
DECLARE #QUERY varchar(1000);
DECLARE #ROLEID int;
SET #ROLEID = ( SELECT Max(ROLEID)
FROM ( SELECT
Max(CREATED_DATE) AS MAX_DATE,
ROLEID
FROM ROLE
WHERE ROLENAME = #ROLENAME
--(ROlename can be changed dynamically, take 'Manager' as example as of now.)
AND CREATED_DATE IS NOT NULL
GROUP BY ROLEID ) X );
--PRINT #ROLEID
SET #QUERY = 'INSERT INTO [ROLES_PRIVILEGES] (ROLEID,PRIVILEGEID) VALUES (''' + Cast(#ROLEID AS varchar) + ''',''' + Cast(#PRIVILEGEID AS varchar) + ''')';
EXECUTE ( #QUERY );
END;
Now #PRIVILEGEID will have a dynamic list of multiple values for the fixed #ROLEID and My issue is I can pass only one #PRIVILEGEID at one time.
Exec ADD_ROLE 'BACKOFFICE',#PRIVILEGEID #PRIVILEGEID=[2, 3, 4, 5, 6]
-- (How to pass multiple value for PRIVILEGEID )
I have tried While loop also but not sure how to implement it.
Please help.
Here is an example where I take a comma delimited string and dump into a table. This process works by converting it into xml and using xmlqry to parse. I like it because I feel its easier to read and looks less convoluted.
Let me know if it works for you.
USE yourDatabase
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[_parsedelimited]
(
#str varchar(max)
)
AS
BEGIN
if object_id('tempdb..#Emails') is not null drop table #Emails;
CREATE TABLE #Emails (Email varchar(2500));
Declare #x XML;
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml);
INSERT INTO #Emails (Email)
select
t.value('.', 'varchar(50)') as inVal
from #x.nodes('/A') as x(t);
SELECT * FROM #Emails;
drop table #Emails;
END
GO
Consider the following:
declare #role varchar(20) = 'BACKOFFICE',
#PRIVILEGEID varchar(100) = '2,3,4,5,6';
select #role, *
from string_split(#PRIVILEGEID, ',');
Using that select, you should be able to insert it into whatever table you'd like.
I am having a procedure with two parameters and a table with two columns defined as para1 and para2.
Now, there are around 5000 rows in a table and I want to run the procedure with using the columns values are input parameters.
I can run the procedure with static parameters but don't know how to do it using parameters in table.
You have two options - looping or using dynamic T-SQL statement.
Here is example using the latter:
DECLARE #DataSource TABLE
(
[Param01] INT
,[Param02] VARCHAR(12)
);
INSERT INTO #DataSource ([Param01], [Param02])
VALUES (1, 'a2')
,(2, 'a2')
,(3, 'a4')
,(4, 'a0');
DECLARE #DynamictTSQLStatement NVARCHAR(MAX);
SELECT #DynamictTSQLStatement =
STUFF
(
(
SELECT CHAR(10) + 'EXEC [dbo].[my_stored_procedured] #Param01 = ' + CAST([Param01] AS VARCHAR(12)) + ', #Param02 = "' + [Param02] + '";'
FROM #DataSource
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
);
EXEC sp_executesql #DynamictTSQLStatement;
For a loop, the things are pretty the same as in all language. Read a row, get current parameters values, execute the routine and continue with the next row until all of the are read.
I used below codes and it works
create procedure [procedurename]
as
declare #field1 varchar(15)
declare #field2 varchar(15)
declare cur CURSOR LOCAL for
select field1, field2 from donor_update_list a
open cur
fetch next from cur into #field1, #field2
while ##FETCH_STATUS = 0
BEGIN
--execute your sproc on each row
exec uspYourSproc #field1, #field2
fetch next from cur into #field1, #field2
END
close cur
deallocate cur
just create your procedure like:
CREATE PROCEDURE s_proc_name #p1 nvarchar(30) = NULL, #p2 nvarchar(60) = NULL
AS
--your code
after that you can for example create a string by doing a query on your table like:
select 's_proc_name ', columnP1, ' ', columnP2
from tableName
This will return an output with you possible input.
after that you can execute the lines you want manually just copy pasting or you can put this in a temp table like:
select 's_proc_name ', columnP1, ' ', columnP2
into #tmp
from tableName
and execute programmatically each line just by using a loop on the table and execute the output string with EXEC(#record)
By developing app. I need a dynamic stored procedure, where I am calling a table name (#pxxx), and I also ned pass same parameters - here I have 2 (datum, por).
It is working well, passing tablename as #pxxx - o.k. Also when parameters datum,por are type -int or varchar, or char or combination of it. Int I change to varchar by CONVERT - procedure go well. Well ig going calling from C#.
Problem is when I change parameter -datum to datetime. It is not working. Changing date by "convert" to varchar not helped. It is not functional in sql management studio. I need to solve a problem, because I need in table datum [datetime],
I can't change table definition, datetime is needed.
I looked for almost every questions about passing parameters and dynamic sql stored procedures, but I not find solution.
Table definition in MS SQL 2005:
CREATE TABLE [dbo].[p001](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[datum] [datetime] NULL,
[por] [int] NULL,
[cas] [nchar](5) NULL,
[max] [int] NULL,
[obsad] [int] NULL,
[blok] [bit] NULL
one of it ) ON [PRIMARY]
My stored procedure - not working
ALTER PROCEDURE [dbo].[SPinsertpxx2] (#pxxx nchar(4),#date datetime, #por int)
AS
DECLARE #sqlCommand varchar(500)
SET #sqlCommand = 'INSERT INTO '+ #pxxx +' ([datum],[por]) VALUES('
+CONVERT(VARCHAR,#date,112)+','+CONVERT(VARCHAR,#por,5)+')'
BEGIN
EXEC (#sqlCommand)
END
This stored procedure - working well but only withous datetime parameter.
ALTER PROCEDURE [dbo].[SPinsertpxxx] (#pxxx nchar(4),#cas nvarchar(10), #por int)
AS
DECLARE #sqlCommand varchar(500)
SET #sqlCommand = 'INSERT INTO '+ #pxxx +' ([cas],[por]) VALUES('
+CONVERT(VARCHAR,#cas)+','+CONVERT(VARCHAR,#por)+')'
BEGIN
EXEC (#sqlCommand)
END
Here is a working example of doing this using parameterzied dynamic sql. This helps prevent sql injection and allows you to use properly typed parameters.
create PROCEDURE [dbo].[SPinsertpxx2]
(
#pxxx nchar(4)
, #datum datetime
, #por int
) AS
DECLARE #sqlCommand nvarchar(max)
SET #sqlCommand = 'INSERT INTO '+ #pxxx +' ([datum],[por]) VALUES(#datum, #por)'
exec sp_executesql #sqlCommand, N'#datum datetime, #por int', #datum = #datum, #por = #por
Now to test this and see if it works.
exec SPinsertpxx2 'p001', '2016-01-01', 42
Does the table contain the correct values?
select * from p001
The code is as follows:
ALTER PROCEDURE dbo.pdpd_DynamicCall
#SQLString varchar(4096) = null
AS
Begin
create TABLE #T1 ( column_1 varchar(10) , column_2 varchar(100) )
insert into #T1
execute ('execute ' + #SQLString )
select * from #T1
End
The problem is that I want to call different procedures that can give back different columns.
Therefore I would have to define the table #T1 generically.
But I don't know how.
Can anyone help me on this problem?
Try:
SELECT into #T1 execute ('execute ' + #SQLString )
And this smells real bad like an sql injection vulnerability.
correction (per #CarpeDiem's comment):
INSERT into #T1 execute ('execute ' + #SQLString )
also, omit the 'execute' if the sql string is something other than a procedure
You can define a table dynamically just as you are inserting into it dynamically, but the problem is with the scope of temp tables. For example, this code:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE #T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO #T1 (Col1) VALUES ('This will not work.')
SELECT * FROM #T1
will return with the error "Invalid object name '#T1'." This is because the temp table #T1 is created at a "lower level" than the block of executing code. In order to fix, use a global temp table:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE ##T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO ##T1 (Col1) VALUES ('This will work.')
SELECT * FROM ##T1
Hope this helps,
Jesse
Be careful of a global temp table solution as this may fail if two users use the same routine at the same time as a global temp table can be seen by all users...
create a global temp table with a GUID in the name dynamically. Then you can work with it in your code, via dyn sql, without worry that another process calling same sproc will use it. This is useful when you dont know what to expect from the underlying selected table each time it runs so you cannot created a temp table explicitly beforehand. ie - you need to use SELECT * INTO syntax
DECLARE #TmpGlobalTable varchar(255) = 'SomeText_' + convert(varchar(36),NEWID())
-- select #TmpGlobalTable
-- build query
SET #Sql =
'SELECT * INTO [##' + #TmpGlobalTable + '] FROM SomeTable'
EXEC (#Sql)
EXEC ('SELECT * FROM [##' + #TmpGlobalTable + '] ')
EXEC ('DROP TABLE [##' + #TmpGlobalTable + ']')
PRINT 'Dropped Table ' + #TmpGlobalTable
INSERT INTO #TempTable
EXEC(#SelectStatement)
Try Below code for creating temp table dynamically from Stored Procedure Output using T-SQL
declare #ExecutionName varchar(1000) = 'exec [spname] param1,param2 '
declare #sqlStr varchar(max) = ''
declare #tempTableDef nvarchar(max) =
(
SELECT distinct
STUFF(
(
SELECT ','+a.[name]+' '+[system_type_name]
+'
' AS [text()]
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) a
ORDER BY a.column_ordinal
FOR XML PATH ('')
), 1, 1, '') tempTableDef
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) b
)
IF ISNULL(#tempTableDef ,'') = '' RAISERROR( 'Invalid SP Configuration. At least one column is required in Select list of SP output.',16,1) ;
set #tempTableDef='CREATE TABLE #ResultDef
(
' + REPLACE(#tempTableDef,'
','') +'
)
INSERT INTO #ResultDef
' + #ExecutionName
Select #sqlStr = #tempTableDef +' Select * from #ResultDef '
exec(#sqlStr)
DECLARE #EmpGroup INT =3 ,
#IsActive BIT=1
DECLARE #tblEmpMaster AS TABLE
(EmpCode VARCHAR(20),EmpName VARCHAR(50),EmpAddress VARCHAR(500))
INSERT INTO #tblEmpMaster EXECUTE SPGetEmpList #EmpGroup,#IsActive
SELECT * FROM #tblEmpMaster
CREATE PROCEDURE dbo.pdpd_DynamicCall
AS
DECLARE #SQLString_2 NVARCHAR(4000)
SET NOCOUNT ON
Begin
--- Create global temp table
CREATE TABLE ##T1 ( column_1 varchar(10) , column_2 varchar(100) )
SELECT #SQLString_2 = 'INSERT INTO ##T1( column_1, column_2) SELECT column_1 = "123", column_2 = "MUHAMMAD IMRON"'
SELECT #SQLString_2 = REPLACE(#SQLString_2, '"', '''')
EXEC SP_EXECUTESQL #SQLString_2
--- Test Display records
SELECT * FROM ##T1
--- Drop global temp table
IF OBJECT_ID('tempdb..##T1','u') IS NOT NULL
DROP TABLE ##T1
End
Not sure if I understand well, but maybe you could form the CREATE statement inside a string, then execute that String? That way you could add as many columns as you want.