Creating a SQL Server trigger from a variable doesn't work - sql

When I run the below code, it runs and prints the SQL output perfectly.
However the last EXEC statement throws an error
Incorrect syntax near the keyword 'TRIGGER'
The code basically strings together some SQL to create a trigger for all existing tables.
When I manually take that PRINT output at the end and execute it in SSMS it works fine, but the EXEC in the code just won't run it.
DECLARE #sql AS NVARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + 'CREATE TRIGGER [tr_' + table_name +'] ON
[' + table_schema + '].[' + table_name + '] FOR INSERT, UPDATE, DELETE AS
SELECT 1 GO' + CHAR(13) + CHAR(10)
FROM INFORMATION_SCHEMA.TABLES
WHERE table_type = 'BASE TABLE'
PRINT #sql; -- output is correct and I can paste this and it works
EXEC sp_executesql #sql -- doesn't work

The SQL command to execute has to be a single batch. So, you can't use GO.
You better declare a CURSOR and execute the script for each table.

Related

MSSQL Dynamic SQL for table creation from view fails only when executed through job

I have a problem with my recurring table creation.
I have a bunch of views that need to be written into tables.
My approach is, that for every view there is, a table should be created and therefore I realized it with dynamic SQL. So that I don't have to touch the job every time I add a view.
My problem is, the code works fine as long as I execute it myself within SSSM.
As soon as I put it in a job and it is executed by schedule or by myself it fails. If I replace the dynamic SQL with the code it produces, the job fails as well. I even put the code into a stored procedure and just executed that from the job and that produced the same result.
The error states that it couldn't convert a nvarchar type into a datetime type.
I checked every view and ran the code for every view/table singly as well as all at once and there was no error.
Has anyone an idea what's wrong here?
Here is the dynamic SQL code that I use:
DECLARE #SQL varchar(max);
SELECT #SQL = COALESCE(#SQL + ' ', '') + 'IF OBJECT_ID(''' + REPLACE(name, 'qry_', 'tbl_') + ''', ''U'') IS NOT NULL DROP TABLE ' + QUOTENAME(REPLACE(name, 'qry_', 'tbl_')) + '; SELECT * INTO ' + QUOTENAME(REPLACE(name, 'qry_', 'tbl_')) + ' FROM ' + QUOTENAME(name) + ';'
FROM sys.views
WHERE LEFT(name, 4) = 'qry_'
EXEC (#sql);
This SQL is untested, however, this should greatly help you. Notice that I do each execution separately. Firstly, this does (actually) mean that if the dynamic SQL fails that it won't propagate out (this may not be intended, however). I also, though, have added PRINT statements so you can inspect the logs and see what was actually done; and what view was failed out. PRINT #SQL; is commented out, as (if I recall correctly), the Agent logs can only hold 8000 or 4000 characters, so printing the value of #SQL would over bloat it.
DECLARE #SQL nvarchar(MAX), #ViewName sysname;
DECLARE qry_views CURSOR FOR
SELECT [name]
FROM sys.views
WHERE [name] LIKE 'qry[_]%'
OPEN qry_views;
FETCH NEXT FROM qry_views
INTO #ViewName;
WHILE ##FETCH_STATUS = 0 BEGIN
PRINT 'Creating table from View ' + QUOTENAME(#ViewName);
SET #SQL = N'IF OBJECT_ID(''' + REPLACE(#ViewName, N'qry_', N'tbl_') + N''', ''U'') IS NOT NULL DROP TABLE ' + QUOTENAME(REPLACE(#ViewName, N'qry_', N'tbl_')) + N';' + NCHAR(10) +
N'SELECT *' + NCHAR(10) +
N'INTO ' + QUOTENAME(REPLACE(#ViewName, N'qry_', N'tbl_')) + NCHAR(10) +
N'FROM ' + QUOTENAME(#ViewName) + N';';
--PRINT #SQL;
EXEC sp_executesql #SQL;
FETCH NEXT FROM qry_views
INTO #ViewName;
END
CLOSE qry_views;
DEALLOCATE qry_views;
In the job, do you call a stored proc by using date literals, like:
exec my_proc #my_param='20180524'
? You can't do this. Assing the values to a var first, then use the var:
declare #my_value date ='20180524'
exec my_proc #my_param=#my_value

Create SQL Server trigger - dynamic SQL too long

Currently I am working on an audit trail using SQL Server triggers to identify inserts, updates and deletes on tables.
Tables can be created dynamically in the database, therefore when this happens I need to create the trigger dynamically.
Therefore at this point I call a stored procedure and pass in the table name.
CREATE PROCEDURE [dbo].[AUDIT_CreateTableTrigger]
#STR_TableName NVARCHAR(MAX)
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE #STR_Trig NVARCHAR(MAX) = ''
SET #STR_Trig = #STR_Trig + '
CREATE TRIGGER [dbo].[' + #STR_TableName + '_Audit] ON [dbo].[' + #STR_TableName + ']
WITH EXECUTE AS CALLER AFTER
INSERT, UPDATE, DELETE AS
BEGIN
-- do the insert stuff
-- update
-- + delete
END'
EXEC (#STR_Trig) -- then execute the sql
My issue is that I am noticing that the exec isn't reading the statement completely and cuts the procedure off.
I need a way of executing a long piece of SQL code (I have one solution, this involves splitting the dynamic SQL into 3 triggers i.e insert, update and delete to get around this, however would prefer to keep 1 trigger to handle all)
Any suggestions would be appreciated, Thanks
Got this issue fixed: Broke up the query see below for solution
DECLARE #sql1 NVARCHAR(4000) = '',
#sql2 NVARCHAR(4000) = '',
#sql3 NVARCHAR(MAX)
SET #sql1 += '
CREATE TRIGGER [dbo].[' + #STR_TableName + '_Audit] ON [dbo].[' + #STR_TableName + ']
WITH EXECUTE AS CALLER AFTER
INSERT, UPDATE, DELETE AS
BEGIN
BEGIN TRY
--sql query
'
SET #sql2 = '
--more sql query
END'
SET #sql3 = CAST(#sql1 AS nvarchar(MAX)) + CAST (#sql2 AS nvarchar(MAX))
EXEC sp_executesql #sql3

Dynamic Sql - Creating a database - Problems with syntax

I'm having issues with a dynamic SQL script in particular this bit:EXEC('
if db_id(''' + $(db) + ''') is null
BEGIN
CREATE DATABASE ' + $(db) + '
END
The if statement part seems to work fine, I know this because if the database exists then the create database line is not run but when it needs to run I just get syntax errors near that line.
I have also tried:
CREATE DATABASE ''' + $(db) + '''
with no luck
Any Ideas?
DECLARE #DB_NAME NVARCHAR(128) = N'Test_DB'
DECLARE #Sql NVARCHAR(MAX);
IF DB_ID(#DB_NAME) IS NULL
BEGIN
SET #Sql = N' CREATE DATABASE ' + QUOTENAME(#DB_NAME)
EXECUTE sp_executesql #Sql
END
Important Note
Make sure your database name is in accordance with the Rules for Regular Identifiers

Update data in a SQL Server temp table where the column names are unknown

In a stored procedure I dynamically create a temp table by selecting the name of applications from a regular table. Then I add a date column and add the last 12 months. The result looks like this:
So far so good. Now I want to update the data in columns by querying another regular table. Normally it would be something like:
UPDATE ##TempTable
SET [columName] = (SELECT SUM(columName)
FROM RegularTable
WHERE FORMAT(RegularTable.Date,'MM/yyyy') = FORMAT(##TempMonths.x,'MM/yyyy'))
However, since I don't know what the name of the columns are at any given time, I need to do this dynamically.
So my question is, how can I get the column names of a temp table dynamically while doing an update?
Thanks!
I think you can use something like the following.
select name as 'ColumnName'
from tempdb.sys.columns
where object_id = object_id('tempdb..##TempTable');
And then generate dynamic sql using something like the following.
DECLARE #tableName nvarchar(50)
SET #tableName = 'RegularTable'
DECLARE #sql NVARCHAR(MAX)
SET #sql = ''
SELECT #sql = #sql + ' UPDATE ##TempTable ' + CHAR(13) +
' SET [' + c.name + '] = (SELECT SUM([' + c.name + ']) ' + CHAR(13) +
' FROM RegularTable' + CHAR(13) +
' WHERE FORMAT(RegularTable.Date,''MM/yyyy'') = FORMAT(##TempMonths.x,''MM/yyyy''));' + CHAR(13)
from tempdb.sys.columns c
where object_id = object_id('tempdb..##MyTempTable');
print #sql
-- exec sp_executesql #sql;
Then print statement in above snippet shows that the #sql variable has the following text.
UPDATE ##TempTable
SET [Test Application One] = (SELECT SUM([Test Application One])
FROM RegularTable
WHERE FORMAT(RegularTable.Date,'MM/yyyy') = FORMAT(##TempMonths.x,'MM/yyyy'));
UPDATE ##TempTable
SET [Test Application Two] = (SELECT SUM([Test Application Two])
FROM RegularTable
WHERE FORMAT(RegularTable.Date,'MM/yyyy') = FORMAT(##TempMonths.x,'MM/yyyy'));
So now, you use sp_exec to execute the updates as follows (un-comment it from above snippet).
exec sp_executesql #sql;
If it's a 1 time UPDATE you can PRINT the dynamic SQL statement (as shown above) and then execute it in the SSMS Query Windows.
I recommend you use the print statement first to make sure the UPDATE statements generated are what you want, and then do the sp_executesql or run the printed UPDATE statement in the query window.

SQL dynamically create stored procedures?

I am trying to create a script to create/setup a group of stored procedures that will all be fairly similar.
So I am trying to loop through this code, changing the #DATABASE_NAME and #TableName when needed.
/* Start loop */
DECLARE #create_stored_procedure nvarchar(max)
SET #create_stored_procedure = N'
USE [' + #DATABASE_NAME + ']
CREATE PROCEDURE [dbo].[sproc_imp_' + #TableName + ']
AS
BEGIN
PRINT(''doing something'')
END'
EXEC sp_executesql #statement = #create_stored_procedure
/* End loop */
But I am getting errors saying
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
or
'CREATE/ALTER PROCEDURE' does not allow specifying the database name as a prefix to the object name.
All the solutions online suggest using GO, but that won't work in dynamic SQL.
Does anyone know a possible solution for SQL Server 2005?
I wouldn't call the solution intuitive, but apparently this works. I prefer the look of this one though.
Try with spiting USe DB and create procedure. Like this
DECLARE #create_store_procedure nvarchar(max)
SET #create_store_procedure = N'
USE [' + #DATABASE_NAME + '] '
EXEC sp_executesql #statement = #create_store_procedure
SET #create_store_procedure = N'
CREATE PROCEDURE [dbo].[sproc_imp_' + #TableName + ']
AS
BEGIN
PRINT(''doing something'')
END '
EXEC sp_executesql #statement = #create_store_procedure
This is working perfectly for me
I tried Nithesh's answer and that didn't work for me it ended up creating the store procedure in the master table. Zec's answer worked. Creating a dynamic query inside my dynamic query.
DECLARE #create_store_procedure nvarchar(max)
DECLARE #use_db nvarchar(max)
SET #create_store_procedure = N'
CREATE PROCEDURE [dbo].[sproc_imp_' + #TableName + ']
AS
BEGIN
PRINT(''doing something'')
END '
SET #use_db = N'
USE [' + #DATABASE_NAME + ']
DECLARE #sub_create_store_procedure nvarchar(max)
SET #sub_create_store_procedure = ''' + REPLACE(#create_store_procedure, CHAR(39), CHAR(39) + CHAR(39)) + '''
EXEC sp_executesql #statement = #sub_create_store_procedure
'
EXEC sp_executesql #statement = #use_db