I'm writing a script to create a bunch of tables in SQL Server. As I write the script, I want to create and delete the database. The problem is that the I get an error saying that the object already exists.
Here is my example script
DECLARE #db_name varchar(20);
DECLARE #command varchar(100);
SET #db_name='testdb';
SET #command='DROP DATABASE ' + #db_name
IF EXISTS(SELECT * FROM sys.databases WHERE name=#db_name)
exec(#command)
SET #command='CREATE DATABASE ' + #db_name
EXEC(#command)
--listing databaes
SELECT name from master.dbo.sysdatabases
-- using our database
SET #command='USE ' + #db_name
EXEC(#command)
PRINT 'listing tables'
SET #command = 'SELECT table_name FROM ' + #db_name + '.INFORMATION_SCHEMA.TABLES WHERE table_type = "base TABLE"'
EXEC(#command)
CREATE TABLE stuff(
name VARCHAR(30) PRIMARY KEY NOT NULL,
weight INT,
quantity INT)
and the output I get is
name
------------
master
tempdb
model
msdb
testdb
SW
(6 rows affected)
listing tables
table_name
Error:
Msg 2714, Level 16, State 6, Server Orange, Line 22
There is already an object named 'stuff' in the database.
I run this on a Linux mint machine, a freshly installed SQL Server, and I use sqlcmd. I guess I can put a drop/delete command before the creating the table, but this shouldn't happen to begin with. What is going on here?
When you execute a USE statement from dynamic SQL, the database context reverts back to the original database context (master?) when the executed batch completes. You'll need to add a USE to the CREATE TABLE script and execute it using dynamic SQL too:
SET #command = N'USE' + QUOTENAME(#db_name) + N';
CREATE TABLE stuff(
name VARCHAR(30) PRIMARY KEY NOT NULL,
weight INT,
quantity INT);
';
bind the create table statement inside a object existence check. like this
IF OBJECT_ID('stuff') IS NULL
BEGIN
CREATE TABLE stuff(
name VARCHAR(30) PRIMARY KEY--NOT NULL is not needed as the primary key does not allow NULL,
weight INT,
quantity INT)
END
Related
I have two databases in SQL Server. Let's say they are DB1 and DB2.
When the system starts, the current database is always DB1. BTW, I have to use DB2 for another table.
For the reason, I want to give a table name as a variable like #tablename and want to select a database name for the #tablename. Would it be possible to pull the database name associated with #tablename?
Also, I want to save the database name to a variable like #databasename to print it out.
When I tried to find a database name from the code below, I could get the database name of the table, ExampleTable, among DB1 and DB2.
EXEC sys.sp_msforeachdb
'SELECT ''?'' DatabaseName, name FROM [?].sys.Tables WHERE Name = ''ExampleTable'''
However, I can't go forward to process how to make a code using a variable #table instead of a fixed table name, ExampleTable.
I will use list of tables to input #tablename into the query one by one from the list.
DECLARE #table sysname = 'TableNames';
DECLARE #database_name sysname = 'dbo';
DECLARE #DatabaseName VARCHAR(50)
-- tbl_01 in dbo.DB1
-- tbl_02 in dbo.DB2
-- tbl_03 in dbo.DB1
-- tbl_04 in dbo.DB2
/*
I need the code block
(1) To input a table using #table
(2) To save the database name to a variable like #database_name
EXEC sys.sp_msforeachdb
'SELECT ''?'' DatabaseName, name FROM [?].sys.Tables WHERE Name ='+#table
*/
Please help me to create a script for my work.
The below code contains a set of tools you can use to do what you want to do. I expect your requirements may change a bit as you do this, so that's why I'm giving you the tools first.
These tools are written as simple checks - easy to understand and relatively quick. They currently just do a SELECT 'Yes' if the database exists/etc - but of course you can change that as needed.
DECLARE #DBName_1 nvarchar(100) = N'DB1';
DECLARE #DBName_2 nvarchar(100) = N'DB2';
DECLARE #TableName nvarchar(100) = N'MyTable';
DECLARE #TableNameToCheck nvarchar(200);
-- Is the current database 'DB1'?
IF DB_Name() = #DBName_1 SELECT 'Yes' ELSE SELECT 'No';
-- Check if the database 'DB2' exists
IF DB_ID(#DBName_2) IS NOT NULL SELECT 'Yes' ELSE SELECT 'No';
-- Check if table is in first database
SET #TableNameToCheck = QUOTENAME(#DBName_1) + N'.[dbo].' + QUOTENAME(#TableName);
IF OBJECT_ID(#TableNameToCheck, 'U') IS NOT NULL SELECT 'Yes' ELSE SELECT 'No';
-- Check if table is in second database (note it uses #DBName_2)
SET #TableNameToCheck = QUOTENAME(#DBName_2) + N'.[dbo].' + QUOTENAME(#TableName);
IF OBJECT_ID(#TableNameToCheck, 'U') IS NOT NULL SELECT 'Yes' ELSE SELECT 'No';
Note that you do not actually need the variable #TableNameToCheck - you can just construct it within the OBJECT_ID() function e.g., OBJECT_ID(#DBName_2 + N'.dbo.' + #TableName , 'U'). However, it can be useful to set it first to check/ensure it's correct, and it has infinitesimal impact on performance.
For your actual task (recording whether the table exists in DB1 or DB2) you can do the following
DECLARE #DBName_1 nvarchar(100) = N'DB1';
DECLARE #DBName_2 nvarchar(100) = N'DB2';
DECLARE #TableName nvarchar(100) = N'MyTable';
DECLARE #DatabaseWithTable nvarchar(100);
IF OBJECT_ID(QUOTENAME(#DBName_1) + N'.[dbo].' + QUOTENAME(#TableName), 'U') IS NOT NULL
BEGIN
SET #DatabaseWithTable = #DBName_1;
END
ELSE IF OBJECT_ID(QUOTENAME(#DBName_2) + N'.[dbo].' + QUOTENAME(#TableName), 'U') IS NOT NULL
BEGIN
SET #DatabaseWithTable = #DBName_2;
END
ELSE
BEGIN
SET #DatabaseWithTable = N'Not Found';
END;
SELECT #DatabaseWithTable;
Edit: Added QUOTENAME() as per suggestion/comment from #HABO
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
I have db A and db B. At the beginning of a stored procedure I want to back up all rows from B.mytable to B.mytablebackup. The rest of the stored procedure runs against tables on db A (which gathers data and writes it to B.mytable).
So I check to see if B.mytablebackup exists
IF EXISTS(SELECT 1 FROM B.dbo.mytablebackup)
and if it does, the stored procedure does an
INSERT INTO B..mytablebackup SELECT * FROM B..mytable
If it doesn't exist it does a
SELECT * INTO B..mytablebackup from B..mytable
But when I execute the stored procedure I get the error
There is already an object named 'mytablebackup' in the database
I added a Print statement and execution is taking the "does not exist" branch of the IF.
What am I doing wrong?
For SQL Server, you should use system view sys.tables to check if table exists.
IF EXISTS(SELECT 1 FROM B.sys.tables WHERE name = 'mytablebackup')
OBJECT_ID can be used too:
IF OBJECT_ID('B.dbo.mytablebackup') IS NOT NULL
You can directly check from the given DB,SCHEMA and TABLE parameters (For dynamic database, schema and table use)
DECLARE #targetdatabase NVARCHAR(MAX),
#SchemaName NVARCHAR(MAX),
#TableName NVARCHAR(MAX)
DECLARE #TempTableName NVARCHAR(MAX) = QUOTENAME(#targetdatabase) + '.' +
QUOTENAME(#SchemaName) + '.' + QUOTENAME(#TableName)
IF OBJECT_ID(#TempTableName) IS NULL
BEGIN
PRINT #TempTableName
END
When I run the following code, I get an "invalid object name" error, any idea why?
I need to create a dynamically named temp table to be used in a stored procedure.
DECLARE #SQL NVARCHAR(MAX)
DECLARE #SessionID NVARCHAR(50)
SET #SessionID = 'tmp5l7g9q3l1h1n5s4k9k7e'
;
SET
#SQL = N' CREATE TABLE #' + #SessionID + ' ' +
N' (' +
N' CustomerNo NVARCHAR(5), ' +
N' Product NVARCHAR(3), ' +
N' Gross DECIMAL(18,8) ' +
N' )'
;
EXECUTE sp_executesql #SQL
;
SET
#SQL = N' SELECT * FROM #' + #SessionID
;
EXECUTE sp_executesql #SQL
Thanks!
WHY MESS WITH THE NAMES? Let SQL Server will manage this for you:
Temporary Tables in SQL Server
from the above link:
If the same routine is executed simultaneously by several processes,
the Database Engine needs to be able to distinguish between the
identically-named local temporary tables created by the different
processes. It does this by adding a numeric string to each local
temporary table name left-padded by underscore characters. Although
you specify the short name such as #MyTempTable, what is actually
stored in TempDB is made up of the table name specified in the CREATE
TABLE statement and the suffix. Because of this suffix, local
temporary table names must be 116 characters or less.
If you’re interested in seeing what is going on, you can view the
tables in TempDB just the same way you would any other table. You can
even use sp_help work on temporary tables only if you invoke them from
TempDB.
USE TempDB
go
execute sp_Help #mytemp
or you can find them in the system views of TempDB without swithching
databases.
SELECT name, create_date FROM TempDB.sys.tables WHERE name LIKE '#%'
You are doing it wrong!
Try:
exec(#SQL)
instead of:
EXECUTE sp_executesql #SQL
To use sp_executesql the variable must be inside #SessionID the quotes and it must be provided has input parameter. Check this for a full example!
You've to be aware that Dynamic SQL is a good port for SQL injections!
This syntax works
CREATE TABLE #SessionID (CustomerNo NVARCHAR(5), Product NVARCHAR(3), Gross DECIMAL(18,8));
Select COUNT(*) from #SessionID;
Drop Table #SessionID;
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']