This presents an error of:
Msg 207, Level 16, State 1, Line 1
Invalid column name 'Testingwithdynamicsql'.
Now why is that, if I print my sql it appears valid to me, what do I alter to make it work?
Declare #tablename varchar(500), #sql varchar(max)
set #tablename = 'Testingwithdynamicsql'
set #sql = 'IF OBJECT_ID('+#tablename+', ''U'') IS NOT NULL
DROP TABLE ' + #tablename + ' '
Print #sql
exec(#sql)
The #tablename needs to be quoted
Declare #tablename varchar(500), #sql varchar(max)
set #tablename = 'Testingwithdynamicsql'
set #sql = 'IF OBJECT_ID('''+#tablename+''', ''U'') IS NOT NULL
DROP TABLE ' + #tablename + ' '
Print #sql
Related
As part of some administrative tasks, we have many tables that each need a trigger created. The trigger will set a flag and the date in the Audit database when an object has been modified. For simplicity, I have a table with all the objects that need triggers created.
I am trying to generate some dynamic sql to do this for each object, but I am getting this error:
'CREATE TRIGGER' must be the first statement in a query batch.
Here is the code to generate the sql.
CREATE PROCEDURE [spCreateTableTriggers]
AS
BEGIN
DECLARE #dbname varchar(50),
#schemaname varchar(50),
#objname varchar(150),
#objtype varchar(150),
#sql nvarchar(max),
#CRLF varchar(2)
SET #CRLF = CHAR(13) + CHAR(10);
DECLARE ObjectCursor CURSOR FOR
SELECT DatabaseName,SchemaName,ObjectName
FROM Audit.dbo.ObjectUpdates;
SET NOCOUNT ON;
OPEN ObjectCursor ;
FETCH NEXT FROM ObjectCursor
INTO #dbname,#schemaname,#objname;
WHILE ##FETCH_STATUS=0
BEGIN
SET #sql = N'USE '+QUOTENAME(#dbname)+'; '
SET #sql = #sql + N'IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'''+QUOTENAME(#schemaname)+'.[Tiud_'+#objname+'_AuditObjectUpdates]'')) '
SET #sql = #sql + N'BEGIN DROP TRIGGER '+QUOTENAME(#schemaname)+'.[Tiud_'+#objname+'_AuditObjectUpdates]; END; '+#CRLF
SET #sql = #sql + N'CREATE TRIGGER '+QUOTENAME(#schemaname)+'.[Tiud_'+#objname+'_AuditObjectUpdates] '+#CRLF
SET #sql = #sql + N' ON '+QUOTENAME(#schemaname)+'.['+#objname+'] '+#CRLF
SET #sql = #sql + N' AFTER INSERT,DELETE,UPDATE'+#CRLF
SET #sql = #sql + N'AS '+#CRLF
SET #sql = #sql + N'IF EXISTS(SELECT * FROM Audit.dbo.ObjectUpdates WHERE DatabaseName = '''+#dbname+''' AND ObjectName = '''+#objname+''' AND RequiresUpdate=0'+#CRLF
SET #sql = #sql + N'BEGIN'+#CRLF
SET #sql = #sql + N' SET NOCOUNT ON;'+#CRLF
SET #sql = #sql + N' UPDATE Audit.dbo.ObjectUpdates'+#CRLF
SET #sql = #sql + N' SET RequiresUpdate = 1'+#CRLF
SET #sql = #sql + N' WHERE DatabaseName = '''+#dbname+''' '+#CRLF
SET #sql = #sql + N' AND ObjectName = '''+#objname+''' '+#CRLF
SET #sql = #sql + N'END' +#CRLF
SET #sql = #sql + N'ELSE' +#CRLF
SET #sql = #sql + N'BEGIN' +#CRLF
SET #sql = #sql + N' SET NOCOUNT ON;' +#CRLF
SET #sql = #sql + #CRLF
SET #sql = #sql + N' -- Update ''SourceLastUpdated'' date.'+#CRLF
SET #sql = #sql + N' UPDATE Audit.dbo.ObjectUpdates'+#CRLF
SET #sql = #sql + N' SET SourceLastUpdated = GETDATE() '+#CRLF
SET #sql = #sql + N' WHERE DatabaseName = '''+#dbname+''' '+#CRLF
SET #sql = #sql + N' AND ObjectName = '''+#objname+''' '+#CRLF
SET #sql = #sql + N'END; '+#CRLF
--PRINT(#sql);
EXEC sp_executesql #sql;
FETCH NEXT FROM ObjectCursor
INTO #dbname,#schemaname,#objname;
END
CLOSE ObjectCursor ;
DEALLOCATE ObjectCursor ;
END
If I use PRINT and paste the code to a new query window, the code executes without any problem.
I have removed the GO statements as this was also giving errors.
What am I missing?
Why am I getting an error using EXEC(#sql); or even EXEC sp_executesql #sql;?
Is this something to do with the context within EXEC()?
Many thanks for any help.
If you use SSMS (or other similar tool) to run the code produced by this script, you will get exactly the same error. It could run all right when you inserted batch delimiters (GO), but now that you don't, you'll face the same issue in SSMS too.
On the other hand, the reason why you cannot put GO in your dynamic scripts is because GO isn't a SQL statement, it's merely a delimiter recognised by SSMS and some other tools. Probably you are already aware of that.
Anyway, the point of GO is for the tool to know that the code should be split and its parts run separately. And that, separately, is what you should do in your code as well.
So, you have these options:
insert EXEC sp_execute #sql just after the part that drops the trigger, then reset the value of #sql to then store and run the definition part in its turn;
use two variables, #sql1 and #sql2, store the IF EXISTS/DROP part into #sql1, the CREATE TRIGGER one into #sql2, then run both scripts (again, separately).
But then, as you've already found out, you'll face another issue: you cannot create a trigger in another database without running the statement in the context of that database.
Now, there are 2 ways of providing the necessary context:
1) use a USE statement;
2) run the statement(s) as a dynamic query using EXEC targetdatabase..sp_executesql N'…'.
Obviously, the first option isn't going to work here: we cannot add USE … before CREATE TRIGGER, because the latter must be the only statement in the batch.
The second option can be used, but it will require an additional layer of dynamicity (not sure if it's a word). It's because the database name is a parameter here and so we need to run EXEC targetdatabase..sp_executesql N'…' as a dynamic script, and since the actual script to run is itself supposed to be a dynamic script, it, therefore, will be nested twice.
So, before the (second) EXEC sp_executesql #sql; line add the following:
SET #sql = N'EXEC ' + #dbname + '..sp_executesql N'''
+ REPLACE(#sql, '''', '''''') + '''';
As you can see, to integrate the contents of #sql as a nested dynamic script properly, they must be enclosed in single quotes. For the same reason, every single quotation mark in #sql must be doubled (e.g. using the REPLACE() function, as in the above statement).
a trigger creation must be done on its own execution batch. You are inside a procedure so you wont be able to create it.
I suggest adding the #sql into a temp table and then once the proc is finish generating all of the statements, loop this temp table to execute them and create the triggers
This approach is a much better approach when using dynamic sql within dynamic sql - side effect: no need to replace quotes and a like:
DECLARE
#originalsql NVARCHAR(4000) = N' ..... '
, #stmt NVARCHAR(200) = 'otherdatabase.dbo.sp_executesql #stmt = #sql'
, #params NVARCHAR(200) = '#sql NVARCHAR(4000)'
EXECUTE sp_executesql #stmt=#stmt, #params=#params, #sql = #originalsql
I'm trying run this procedure with generic parameters.
If I can't delete because some foreign key, it should update the row.
But when I execute it, still running forever and don't complete the process, any Idea?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[stp_Batch_Registros_Desativados_Excluir]
#table_name VARCHAR(100),
#id int
AS
BEGIN
DECLARE #column VARCHAR(100),
#sql VARCHAR(300);
SET #column = (SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMNPROPERTY(OBJECT_ID(TABLE_NAME),COLUMN_NAME,'IsIdentity') = 1
AND TABLE_NAME = #table_name);
BEGIN TRY
select #sql = 'DELETE ' + #table_name + ' WHERE ' + #column + ' = ' + CONVERT(VARCHAR,#id);
exec(#sql);
END TRY
BEGIN CATCH
select #sql = 'UPDATE ' + #table_name + ' SET fg_excluido = 2 WHERE ' + #column + ' = ' + CONVERT(VARCHAR,#id) ;
exec(#sql);
END CATCH;
END
This seems to work fine for me. Do you have triggers, cascading foreign keys, etc.?
USE tempdb;
GO
CREATE TABLE dbo.fooblat1
(
id INT IDENTITY(1,1) PRIMARY KEY,
x VARCHAR(1),
fg_excluido TINYINT NOT NULL DEFAULT 1
);
CREATE TABLE dbo.fooblat2
(
blatid INT NOT NULL FOREIGN KEY REFERENCES dbo.fooblat1(id)
);
INSERT dbo.fooblat1(x) SELECT 'a';
INSERT dbo.fooblat2 SELECT SCOPE_IDENTITY();
INSERT dbo.fooblat1(x) SELECT 'b';
GO
Then:
CREATE PROCEDURE [dbo].[proc]
#table_name VARCHAR(100),
#id INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #column NVARCHAR(100),
#sql NVARCHAR(MAX);
SELECT #column = name
FROM sys.columns
WHERE [object_id] = OBJECT_ID(#table_name)
AND is_identity = 1;
BEGIN TRY
SET #sql = 'DELETE [' + #table_name
+ '] WHERE [' + #column + '] = '
+ CONVERT(VARCHAR(12),#id);
EXEC sp_executesql #sql;
END TRY
BEGIN CATCH
SET #sql = 'UPDATE [' + #table_name
+ '] SET fg_excluido = 2 WHERE '
+ #column + ' = ' + CONVERT(VARCHAR(12),#id);
EXEC sp_executesql #sql;
END CATCH;
END
GO
Then:
EXEC [dbo].[proc] 'fooblat1', 1; -- should update
GO
EXEC [dbo].[proc] 'fooblat1', 2; -- should delete
GO
SELECT * FROM dbo.fooblat1;
GO
Cleanup:
DROP PROC [dbo].[proc];
DROP TABLE dbo.fooblat2;
DROP TABLE dbo.fooblat1;
GO
is there any method how to make MySQL function that receives table name or field name ?
something like this :
CREATE PROCEDURE delete_row(the_id INT UNSIGNED , #table_name )
BEGIN
IF ....... THEN
BEGIN
DELETE FROM #table_name WHERE id = the_id ;
.............
END
END
I tested it with string (SET #table_name="table_name"), but it doesn't works.
declare varchar(max) #mySQL
set #mySQL = 'DELETE FROM ' + #tablename + 'WHERE id = ' + Convert(varchar, #the_id)
sp_executeSQL #mySQL
and to make this work on MySQL (as the commenters pointed out) it should look like this:
mysql> prepare stmt from -> 'DELETE FROM ' + #tablename + 'WHERE id = ' + Convert(varchar, #the_id)
mysql> execute stmt;
I have a stored procedure, and I would like to assign the number of rows of that table to a variable and later use that variable.
I am calling the procedure like:
EXEC TEST.dbo.myProc nameOfTable
The procedure is something like:
CREATE PROCEDURE myProc #table_name varchar(1024) AS
BEGIN
DECLARE #Nval INT
/* SOME INSTRUCTIONS */
SELECT #Nval = COUNT(*) FROM #table_name
END
When executing I am getting an error:
Msg 156, Level 15, State 1, Procedure nLQ, Line 57
Incorrect syntax near the keyword 'FROM'.
How would I assign the variable #Nval?
You can't parameterise a table name like that, FROM #table_name. Only way is to execute dynamic TSQL.
Before you do that, read: The Curse and Blessings of Dynamic SQL
try this
ALTER PROCEDURE [dbo].[sp_tablenametest]
#table_name varchar(50),
#PMId int,
#ValueEq int
AS
BEGIN
SET NOCOUNT ON;
DECLARE #cmd AS NVARCHAR(max)
SET #cmd = N'SELECT * FROM ' + #table_name +
' WHERE Column1 = ''' + #PMId + '''' +
' AND Column2= ''' + #ValueEq + ''''
EXEC sp_executesql #cmd
END
I want to run a diagnostic report on our SQL Server 2008 database server.
I am looping through all of the databases, and then for each database, I want to look at each table. But, when I go to look at each table (with tbl_cursor), it always picks up the tables in the database 'master'.
I think it's because of my tbl_cursor selection :
SELECT table_name FROM information_schema.tables WHERE table_type = 'base table'
How do I fix this?
Here's the entire code:
SET NOCOUNT ON
DECLARE #table_count INT
DECLARE #db_cursor VARCHAR(100)
DECLARE database_cursor CURSOR FOR
SELECT name FROM sys.databases where name<>N'master'
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #db_cursor
WHILE ##Fetch_status = 0
BEGIN
PRINT #db_cursor
SET #table_count = 0
DECLARE #table_cursor VARCHAR(100)
DECLARE tbl_cursor CURSOR FOR
SELECT table_name FROM information_schema.tables WHERE table_type = 'base table'
OPEN tbl_cursor
FETCH NEXT FROM tbl_cursor INTO #table_cursor
WHILE ##Fetch_status = 0
BEGIN
DECLARE #table_cmd NVARCHAR(255)
SET #table_cmd = N'IF NOT EXISTS( SELECT TOP(1) * FROM ' + #table_cursor + ') PRINT N'' Table ''''' + #table_cursor + ''''' is empty'' '
--PRINT #table_cmd --debug
EXEC sp_executesql #table_cmd
SET #table_count = #table_count + 1
FETCH NEXT FROM tbl_cursor INTO #table_cursor
END
CLOSE tbl_cursor
DEALLOCATE tbl_cursor
PRINT #db_cursor + N' Total Tables : ' + CAST( #table_count as varchar(2) )
PRINT N'' -- print another blank line
SET #table_count = 0
FETCH NEXT FROM database_cursor INTO #db_cursor
END
CLOSE database_cursor
DEALLOCATE database_cursor
SET NOCOUNT OFF
The problem is because you're actually always running the INFORMATION_SCHEMA.TABLES query under the master db context.
You'd need to convert the tbl_cursor block into dynamic SQL in order to fully qualify the query with the DB name.
e.g.
SELECT table_name FROM YourDatabase.INFORMATION_SCHEMA.TABLES WHERE....
is essentially what you need to be executing for that cursor.
It's easier to use table variables so you can add rows to #tablist using another dynamic SQL statement
SET NOCOUNT ON
DECLARE #table_count INT
DECLARE #dblist TABLE (DBName VARCHAR(100))
DECLARE #tablist TABLE (TableName VARCHAR(100))
DECLARE #dbname varchar(100), #tabname varchar(100)
INSERT #dblist
SELECT name FROM sys.databases where name<>N'master'
SELECT TOP 1 #dbname = DBName FROM #dblist
WHILE ##ROWCOUNT <> 0
BEGIN
INSERT #tablist (tableName)
EXEC ('SELECT table_name FROM ' + #dbname + '.information_schema.tables WHERE table_type = ''base table'' ')
SELECT TOP 1 #tabname = tableName FROM #tablist
WHILE ##ROWCOUNT <> 0
BEGIN
--do my stuff
DELETE #tablist WHERE tableName = #tabname
SELECT TOP 1 #tabname = tableName FROM #tablist
END
DELETE #dblist WHERE DBName = #dbname
SELECT TOP 1 #dbname = DBName FROM #dblist
END
You might have to create dynamic SQL. because information_schema will fetch objects only from the current active database against which you are running this query.
you can try sys.objects