I am using the following script:
DECLARE #dbName NVARCHAR(20) = 'ABC';
EXEC ( N' USE ' + #dbName + '
GO
-- Create Schema
CREATE SCHEMA meta
GO
-- Create Log Table
CREATE TABLE meta.LogAudit(
[EventDate] [datetime] NOT NULL DEFAULT (getdate()),
[EventType] [nvarchar](64) NULL
)
GO
'
);
but it throws me the following error.
Msg 111, Level 15, State 1, Line 5
'CREATE SCHEMA' must be the first statement in a query batch.
Msg 102, Level 15, State 1, Line 22
Incorrect syntax near 'GO'.
Why is that?
--
Edit:
This answer seems to be answering my question:
dynamic sql error: 'CREATE TRIGGER' must be the first statement in a query batch
So it seems that in my situation I cannot program it dynamically. My whole code works in the following way:
USE DB
GO
CREATE SCHEMA SCH
GO
CREATE TABLE SCH.TABLE
GO
CREATE TRIGGER TRG
GO
So as SCHEMA and TRIGGER needs to be first query statement, it cannot be written in this way.
Try removing comma after [EventType] [nvarchar](64) NULL, and see if the error message changes.
So you have 2 problems:
As #Tanner has pointed out, you cannot use GO in dynamic SQL.
You still have that trailing comma in the meta.LogAudit table columns definition.
Try running this code:
DECLARE #dbName NVARCHAR(20) = 'ABC';
declare #sql nvarchar(max) = N'exec '+ #DBName + '..sp_executesql N''CREATE SCHEMA meta'''
execute(#sql)
declare #sql2 nvarchar(max) = '
-- Create Log Table
CREATE TABLE '+ #DBName + '.meta.LogAudit(
[EventDate] [datetime] NOT NULL DEFAULT (getdate()),
[EventType] [nvarchar](64) NULL
)'
exec sp_executesql #sql2,N''
It will allow you to programmatically create schema in the specified Database as opposite to using current database.
You can use following script for solution to your requirement with the help of unsupported undocumented stored procedure sp_MSForEachDB
EXEC sp_MSForEachDB '
Use [?];
IF ''[?]'' = ''[ABC]''
begin
-- Create Schema
exec sp_executesql N''CREATE SCHEMA meta''
end
'
EXEC sp_MSForEachDB '
Use [?];
IF ''[?]'' = ''[ABC]''
begin
-- Create Log Table
CREATE TABLE meta.LogAudit(
[EventDate] [datetime] NOT NULL DEFAULT (getdate()),
[EventType] [nvarchar](64) NULL,
)
end'
Related
I'm working with a third party tool that creates databases and tables. I would like a trigger on one of those tables being created. I thought I would try to create a server DDL trigger that fires when the table is created in a new database, which in turn creates a trigger on that table. I cannot add the trigger to the 'model' database because this table is created via the tool dynamically.
I've tried the following:
CREATE TRIGGER ddl_trig_createTable
ON ALL SERVER
FOR CREATE_TABLE
AS
DECLARE #databaseName varchar(255)
DECLARE #AffectedTables varchar(255)
SELECT #AffectedTables = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)')
IF (#AffectedTables IN ('DynamicallyCreatedTable'))
BEGIN
select #databaseName = CAST(eventdata().query('/EVENT_INSTANCE/DatabaseName[1]/text()') as NVarchar(128))
EXEC('CREATE TRIGGER ' + #databaseName + '.[dbo].[tgrDynamicTableTrigger]
ON
' + #databaseName + '.[dbo].[DynamicallyCreatedTable]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
-- trigger code here
END')
END
GO
Which produces the following error when the table is created;
Msg 166, Level 15, State 1, Line 1 'CREATE/ALTER TRIGGER' does not
allow specifying the database name as a prefix to the object name.
I tried changing the dynamic sql by replacing the fully qualified table name to attempt a 'use' statement:
--- 8< ---
EXEC('use ' + #databaseName + '
CREATE TRIGGER [dbo].[tgrDynamicTableTrigger]
ON
--- 8< ---
However, that produced the following error when the table was created:
Msg 111, Level 15, State 1, Line 2 'CREATE TRIGGER' must be the first
statement in a query batch.
Any ideas?
I'm using SQL Server 2014.
I believe that I have figured it out, thanks mostly to this answer.
Here's the code:
CREATE TRIGGER ddl_trig_createTable
ON ALL SERVER
FOR CREATE_TABLE
AS
DECLARE #statement nvarchar(max) = 'CREATE TRIGGER [dbo].[tgrDynamicTableTrigger]
ON
[dbo].[DynamicallyCreatedTable]
AFTER UPDATE
AS
BEGIN
-- trigger code here
END'
DECLARE #databaseName varchar(255)
DECLARE #AffectedTables varchar(255)
SELECT #AffectedTables = EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)')
IF (#AffectedTables IN ('DynamicallyCreatedTable'))
BEGIN
SET #databaseName = CAST(eventdata().query('/EVENT_INSTANCE/DatabaseName[1]/text()') as NVarchar(128))
DECLARE #sql NVARCHAR(MAX) = QUOTENAME(#databaseName) + '.sys.sp_executesql';
EXEC #sql #statement;
END
GO
As I have seen so far, people suggested using dynamic SQL.
For example:
How to pass schema as parameter to a stored procedure in sql server?
How to pass schema name as parameter in stored procedure
However, dynamic SQL has the risk of SQL injection. Hence, I want to know if there are any other safe alternatives?
Basically, this stored procedure that I am creating will be called at runtime. There will be 2 possible schemas to be passed in. And the table name will be passed in as well.
Something like below: (It does not work)
CREATE PROCEDURE [EFM].[usp_readApexTable]
#SCHEMANAME VARCHAR(20) = NULL,
#TABLENAME VARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM [#SCHEMANAME].[#TABLENAME];
END
GO
This is just an example of READ action. My plan is to create for CRUD, which requires 4 different stored procedures.
You can use QUOTENAME to avoid any SQL injection and build your dynamic query like the following:
CREATE PROCEDURE [EFM].[usp_readApexTable]
#SCHEMANAME VARCHAR(20) = NULL,
#TABLENAME VARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL VARCHAR(MAX)=N'SELECT * FROM '
+ QUOTENAME(#SCHEMANAME) + '.' + QUOTENAME(#TABLENAME)
EXEC (#SQL)
END
GO
Note: If you have any plan to add parameters also for your WHERE clause, in that case QUOTENAME will not help much, I suggest to to use sp_executesql by passing appropriate parameters used in WHERE clause.
Still you need to use QUOTENAME for schema and table name as SQL excepts it only as literal, you can't use variable names for table and schema.
For example.
declare #sql nvarchar(max)
set #sql = N'select * from ' + quotename(#SCHEMANAME ) + '.' + quotename(#TABLENAME )
+ ' where (City = #City)'
exec sp_executesql
#sql,
N'#City nvarchar(50)',
#City
You can find more details here
You need to use dynamic sql to do this operation
CREATE PROCEDURE [EFM].[usp_readApexTable]
#SCHEMANAME VARCHAR(20) = NULL,
#TABLENAME VARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sqlCommand nvarchar(MAX)
SET #sqlCommand='SELECT * FROM ['+#SCHEMANAME+'].['+#TABLENAME+'];'
--Create Your Temp Table where you can set the records after executing the dynamic query
CREATE TABLE #tmpTbl(
Column1 [datatype]
Column2 [datatype]
.
.
ColumnN
)
INSERT INTO #tmpTbl EXEC sp_executesql #sqlCommand --Copy data to #tmpTbl table
SELECT * FROM #tmpTbl
DROP TABLE #tmpTbl
END
GO
I want to create a stored procedure which I can pass a parameter to for the database name and it will create a view for me.
I am just trying to save some time by not writing the same statement over and over and over for a create vew.
Below is my syntax - how could this be modified to run in a stored procedure accepting a parameter?
Alter View dbo.ForceClose
As
SELECT DISTINCT(SessionID) As CountofSessionID
FROM Database1
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN ('JJones', 'MHill', 'RMort'))
Go
And in the stored procedure accept a parameter as the database name (I know this isn't valid just trying to show an example) -- call the stored procedure like so
exec dbo.Procedure 'DBName'
And the stored procedure would then look like
#DBName varchar(100)
Select blah blah FROM' + #DBName + '
You mean this?
CREATE PROCEDURE dbo.ForceClose
(
#DBNAME NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #SQL AS NVARCHAR(255)
#SQL='SELECT DISTINCT(SessionID) As CountofSessionID
FROM ['+#DBNAME+'].[SCHEMA].[TABLE_NAME]
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN (''JJones'', ''MHill'', ''RMort''))'
EXEC (#SQL)
END
And you can run it by:
EXEC ForceClose <DBNAME>
With the View:
CREATE PROCEDURE dbo.ForceClose
(
#DBNAME NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #SQL AS NVARCHAR(255)
#SQL='CREATE VIEW VIEW_'+DBNAME+' AS SELECT DISTINCT(SessionID) As CountofSessionID
FROM ['+#DBNAME+'].[SCHEMA].[TABLE_NAME]
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN (''JJones'', ''MHill'', ''RMort''))'
EXEC (#SQL)
END
And you can run it by:
SELECT * FROM VIEW_<DBNAME>
Im not sure what the create view is doing; the basic premise you want is dynamic SQL (and there are lots of good questions and answers on the subject. A really simple answer would be
declare #myparameter nvarchar(100)= 'master'
declare #myquery nvarchar(1000)
select #myquery = 'select * from ' + #myparameter + '.dbo.sysdatabases'
select #myquery
exec sp_executeSQL #myquery
Running this returns a list of the databases on your server.
If you want to create a view; would you not need to know the table to query? The basic technique is the same
declare #myparameter nvarchar(100)= 'master'
declare #myquery nvarchar(1000)
select #myquery = 'Create View myschema.vw' + #myparameter + ' as select * from ' + #myparameter + '.dbo.sysdatabases'
select #myquery
exec sp_executeSQL #myquery
If this is in an application the user permissions to do this would be very high for a general application; you might want to wrap it with an execute as permission to stop people doing too much damage.
Can i create schema in Store Procedure in SQL Server. I tried this
CREATE PROCEDURE makadmin.createCustomerSchema
(#tenant nvarchar(30) ,
#companyId int ,
#saleId bigint )
AS
Begin
create schema #tenant authorization createcustomerschema)
End
Is it possible or I am heading in wrong direction?
To issue CREATE statements from inside a stored procedure, use EXEC sp_executesql:
CREATE PROCEDURE createCustomerSchema
(#tenant nvarchar(30) ,
#companyId int ,
#saleId bigint )
AS
Begin
declare #sql nvarchar(4000) = 'create schema ' + #tenant + ' authorization createcustomerschema';
EXEC sp_executesql #sql
End
You can't do this since 'CREATE SCHEMA' must be the first statement in a query batch.
You can create schema as follows with dynamic sql, and it is better to check whether schema is already exist in the database.
DECLARE #cmd varchar(1000)
IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = #tenant)
BEGIN
SET #cmd='CREATE SCHEMA '+#tenant + ' authorization createcustomerschema';
EXEC (#cmd);
END;
I'm working on a SSIS package. I have an array with a list of names. Those names will be table names.
To create those tables, I loop a SQL task editor EXECUTING a stored procedure accepting a parameter (name of the table)
Code for the stored procedure:
ALTER PROCEDURE [dbo].[TLP.CreaTable]
(
#TableName VARCHAR(255)
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #SQL VARCHAR(4000)
SET #SQL = 'IF NOT EXISTS ( SELECT *
FROM sysobjects
WHERE id = OBJECT_ID(N''[dbo].' + #TableName + ''')
AND xtype in (N''U''))
CREATE TABLE [dbo].' + #TableName + '(
[Serial] [int] NOT NULL,
[Marc] [bit] NOT NULL,
[Sell] [bit] NOT NULL
) ON [PRIMARY]'
EXEC (#SQL)
END
The issue comes when one of the names in the array is a number. I must say all the names enter the stored procedure with []. I've created a breakpoint before each execution and when the table name is a number it brings an error inside the SSIS (Also for you to have a better idea Ive ran the stored procedure from the SQL Server Management Studio, and the error persists, Ive used the table name [3001] which is actually a real name that's causing the problem).
EXEC dbo.[TLP.CreaTable] [3001]
Msg 170, Level 15, State 1, Line 5 Line 5: Incorrect syntax near '.3001'.
If I do something like
EXEC dbo.[TLP.CreaTable] RANDOM_NAME
It works just perfectly
Apparently there's some '.' in the middle. But I just cant find it in the code.
Thanks
EDIT:
It was a matter of quotation. It worked in the SQL Server Management Studio. But when I tried it in the SSIS the error persisted. Tried modifying the C# code to add '[ ]' instead of just [] and it didn't work eiter.
The error inside the SSIS is as follows.
Error: 0xC002F210 at Create Table 030698, Execute SQL Task: Executing the query "EXEC dbo.[TLP.CreaTable] ?" failed with the following error: "An error occurred while extracting the result into a variable of type (DBTYPE_I4)".
Possible failure reasons: Problems with the query, "ResultSet"
property not set correctly, parameters not set correctly, or
connection not established correctly.
With breakpoints I watched the variable for the table name and it was ok '[3001]'
The calling inside the SQL Task Editor is
EXEC dbo.[TLP.CreaTable] ?
But somehow the EXEC dbo.[TLP.CreaTable] '[3001]' works in SQL Server Management Studio, but doesn't inside the SSIS.
Any idea? Thanks.
EDIT 2
Since Im pretty new to the forum, should I ask this second question in a new 'question'? Since the matter issued in the subject was solve.
Thanks a lot.
EDIT 3
Fixed. Parameter in CREATE TABLE SQL task editor was long, changed it to varchar and it went smooth.
Thanks
You could try wrapping #TableName with the QuoteName function which should escape the table name for you. Change that line to:
CREATE TABLE [dbo].' + QuoteName(#TableName) + '(
Edit: Might also be an idea to wrap the table name in square brackets, in case it's a reserved word:
CREATE TABLE [dbo].[' + QuoteName(#TableName) + '] (
try using ' around the table name
EXEC dbo.[TLP.CreaTable] '[3001]'
Add brackets around the
[' + #TableName + ']
So that the store procedure looks like this:
ALTER PROCEDURE [dbo].[TLP.CreaTable]
(
#TableName VARCHAR(255)
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #SQL VARCHAR(4000)
SET #SQL = 'IF NOT EXISTS ( SELECT *
FROM sysobjects
WHERE id = OBJECT_ID(N''[dbo].' + #TableName + ''')
AND xtype in (N''U''))
CREATE TABLE [dbo].[' + #TableName + '](
[Serial] [int] NOT NULL,
[Marc] [bit] NOT NULL,
[Sell] [bit] NOT NULL
) ON [PRIMARY]'
EXEC (#SQL)
END
You can't create a table name with a number directly. Try putting the table name in quotations: I.e.
EXEC [dbo].[TLP.CreaTable] ['100']