Create Database and Table Conditionally - sql

I'm trying to write a small script to create a database if it doesn't exist, and create a table for that database if the table doesn't exist. What I have is this:
IF (db_id('db') is null) BEGIN
print 'Must create the database!';
CREATE DATABASE db;
END
USE db;
IF (object_id('test_table', 'U') is null) BEGIN
print 'Must create the table!';
CREATE TABLE test_table (
id int
);
END
I'm getting a strange error with this:
Database 'db' does not exist. Make sure that the name is entered correctly.
I'm guessing that it's parsing the script before running it and finding that 'db' doesn't exist, so it can't use it.
There must be a solution to this. Any help is appreciated.
SOLVED!
I realised 5 minutes after posting that the GO keyword solves the problem. Here is the fixed code:
IF (db_id('db') is null) BEGIN
print 'Must create the database!'
CREATE DATABASE db;
END
GO
USE db
IF (object_id('test_table', 'U') is null) BEGIN
print 'Must create the table!';
CREATE TABLE test_table (
id int
);
END
Sorry for wasting everyone's time.

SQL statements are parsed as one batch unless you break them apart. In SQL Server, you can use GO to do this. In both MySQL and SQL Server, you can use BEGIN and END.
If you want to commit the separate blocks to the database in different instances you can use BEGIN TRANS / COMMIT TRANS and START TRANSACTION / COMMIT for SQL Server and MySQL, respectively.

Something along the lines of Check if table exists in SQL Server would probably work (With a slight change)
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'TheSchema'
AND TABLE_NAME = 'TheTable'))
BEGIN
--Do Stuff
END

I might suggest using the built-in SQL syntax -
CREATE DATABASE name IF NOT EXISTS;
And subsequently
CREATE TABLE name(definition) IF NOT EXISTS;

Related

What is the process during re-naming and re-creating a MS-SQL table using stored procedure?

I have a table called myTable where continuous insertion is happening. I will rename that table by myTable_Date and create a new table, myTable through a Store Procedure.
I want to know what will happen during re-naming and re-creating the table, will it drop any packet?
SQL Server has sp_rename built in if you just want to change the name of a table.
sp_rename myTable, myTable_Date
Would change the name from myTable to myTable_Date
But it only changes the name reference in sys.Objects so make sure any references are altered and read the documentation about it :)
The Microsoft doc for it is HERE
When you rename the myTable to myTableDate, myTable won't exist anymore so if someone tries to insert something inside myTable it will fail.
When you create new myTable with the same name and columns everything will be fine and the insertion process will continue.
I suggest you to make a little script renaming the table and creating new one. Something like this:
sp_rename myTable, myTable_Date
GO
CREATE TABLE myTable(
-- Table definition
)
When you rename the table you will get warning like this: "Caution: Changing any part of an object name could break scripts and stored procedures." so you better create the new table fast.
Other option is you create a table exact like myTable and insert all data from myTable there and then can delete them from myTable. No renaming, no dropping and insertion process will not be interrupted.
I want to know what will happen during re-naming and re-creating the
table, will it drop any packet?
Inserts attempted after the table is renamed will err until the table is recreated. You can avoid that by executing the tasks in a transaction. Short term blocking will happen if an insert is attempted before the transaction is committed but no rows will be lost. For example:
CREATE PROC dbo.ReanmeMytableWithDate
AS
DECLARE #NewName sysname = 'mytable_' + CONVERT(nchar(8), SYSDATETIME(), 112);
SET XACT_ABORT ON;
BEGIN TRY;
BEGIN TRAN;
EXEC sp_rename N'dbo.mytable', #NewName;
CREATE TABLE dbo.mytable(
col1 int
);
COMMIT;
END TRY
BEGIN CATCH
THROW;
END CATCH;
GO
I don't know your use case for renaming tables like this but it seems table partitioning might be a better approach as #Damien_The_Unbeliever suggested. Although table partitioning previously required Enterprise Edition, the feature is available in Standard Edition beginning with SQL Server 2016 SP1 as well as Azure SQL Database.

How do I update triggers across multiple databases?

I have a query that I can select the databases from the sys.databases with the triggers that I wish to update. From there I can create a cursor. However when I go into the cursor to update my triggers using a dynamic db name #DatabaseExecuteName that is set to MyDatabaseName.dbo I receive the error ''CREATE/ALTER TRIGGER' does not allow specifying the database name as a prefix to the object name.' Because I am in a cursor I am not able to execute a USE MyDatabaseName ... GO, the GO statement is not allowed inside the CURSOR. I have tried SQLCMD MODE :setvar DatabaseName "MyDatabaseName" with USE [$(DatabaseName)]; to try to set the use database. I feel I am very close however my strength is not SQL queries. I could use some assistance on what I am missing.
You can nest EXEC calls so that you can use a USE and then execute a further statement and you don't need to use GO to seperate the batches. This is a complete script to demonstrate the technique:
create database DB1
go
create database DB2
go
use DB2
go
create table T1 (ID int not null)
go
create table T2 (ID int not null)
go
use DB1
go
exec('use DB2; exec(''create trigger T_T on T1 after insert as
insert into T2(ID) select i.ID from inserted i'')');
select DB_NAME()
insert into DB2..T1(ID) values (1),(2);
select * from DB2..T2
Which then shows that this connection is still in the DB1 database, but the trigger was successfully created on the T1 table within the DB2 database.
What you have to watch for is getting your quote-escaping correct.

SQL SELECT dynamic from RDB$RELATION_NAME, to check tables integrity

I'm trying to create a stored procedure (or a a trigger, function, anything) to check if all the tables in a database is accessible.
My idea is to get all the tables in database, and then try to acess them with a simple select, if this succeeds for all the tables, everything is supposed to be ok.
I couldn't think of anything else to solve this problem, but I don't know how to do this, or all this intead.
1 - to get all the tables name I did:
SELECT RDB$RELATION_NAME TABLE
FROM RDB$RELATIONS
WHERE RDB$VIEW_BLR IS NULL
AND (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0)
ORDER BY TABLE
Now I just need to create the SELECT statement to each table, and run a query:
SELECT FIRST 1 * FROM [TABLE];
while it's ok, it continues, if all the tables are accessible, my database is ok.
Can anybody help me with this? Is this the correct aproach to solve this problem?
As a_horse_with_no_name commented, this is really strange request... if you see the table in the RDB$RELATIONS you can be pretty sure the table exists in the database. If the table is listed in the DB metadata but actually doesn't exist then the DB is corrupted and your idea to use select to check it's "accessibilty" is pointless... Also, the table might be there but the user might not have select right for it, IOW you need to take the user rights into account too.
Anyway, you can use the EXECUTE STATEMENT to execute dynamically built DSQL statement, something like
declare stmt varchar(1024);
declare ctab varchar(31);
BEGIN
FOR SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$VIEW_BLR IS NULL AND (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0)
INTO :ctab DO BEGIN
stmt = 'select ... from ' || ctab;
execute statement stmt;
END;
END
To check is the database corrupted you should use the gfix utility with -validate option.

SQL Server 2012 temp table OBJECT_ID issue

We have an issue upgrading to SQL Server 2012. I am using the following script to create temporary tables that used to work fine on SQL Server 2008 R2 but now it is generating an error with 2012:
if (OBJECT_ID( 'tempdb..#idstable') > 0)
truncate table #idstable
else
create table #idstable (id int not null)
the error thrown is
There is already an object named '#idstable' in the database.
This is obviously not thrown the first time I use the script (in the same transaction).
Any idea? Thank you!
In SQL Server 2012, #temp tables are created with a negative object_id, so your script won't work as is. The safest way is:
IF OBJECT_ID( 'tempdb..#idstable') IS NOT NULL
(I blogged about this here, and knew it would catch someone.)
Though your script is bound for failure anyway, if it is part of a single batch. The parser will not let you try and create the same #temp table twice.
Try this:
IF OBJECT_ID (N'tempdb..#idstable', N'U') IS NOT NULL
truncate table #idstable
else
create table #idstable (id int not null)
My dears,
This issue is due to the truncate statement. Truncate is used to delete all records preserving the table. Use drop table instead of truncate table and this will works fine ;-)

Create SQL Server tables and stored procedures in one script?

I have a SQL script that is setting up two database tables with their keys and constraints without any problem. I won't include the whole code but the 'skeleton' of it looks like this:
BEGIN
CREATE TABLE [table] (
)
CREATE TABLE [table2] (
)
ALTER TABLE table...
ALTER TABLE table2....
END
I am stuck trying to add stored procedures to this script though, ideally I would like to include this all within the same script. Could someone tell me how to include the following stored procedure into the above script?
CREATE PROCEDURE Test
#x int
AS
BEGIN
SELECT COUNT(*)
FROM table
END
GO
I have tried putting it towards the end of the script and have also tried with and without the BEGIN, END and GO tags but I keep getting an error that says 'incorrect syntax near PROCEDURE'.
Try it like this:
USE BDNAME
GO
BEGIN
CREATE TABLE [table] (
)
CREATE TABLE [table2] (
)
ALTER TABLE table...
ALTER TABLE table2....
END
USE BDNAME
GO
CREATE PROCEDURE Test
#x int
AS
BEGIN
SELECT COUNT(*)
FROM table
END
GO
Instead of using BEGIN END, put GO between all your Statements like Create, Alter. Also I would like to inform you that putting GO will create blocks in your script, so if you create some local variable in one block, it is not accessible in another.
CREATE Table Table1(
--Your Code
)
GO
CREATE PROCEDURE Test
#x int
AS
BEGIN
SELECT COUNT(*)
FROM Table1
END
GO
--Continue your script
Hope this helps.