Reputation: 21522
I wrote out my SQL and ran into an issue, see here.
So I'm asking the proper way to do what I want to do as apposed to, what I consider, a hack of using EXEC. If EXEC is the proper way, then I have issues with SQL but will use it.
I'm writing a SQL script to create/update a database. I have it structured like so:
Declare SafetyCheck=0
Declare DBVersion=0
Declare NewInstall=0
Declare DropTables=0
IF SafetyCheck=1
Begin
IF DropTables = 1
Drop all tables in the database
IF Config table doesn't exist
Begin
Create the Config Table
NewInstall = 1
End
IF Other Table doesn't exist
Create the other table
IF Other Table doesn't exist
Create the other table
DBVersion = CheckTheDBVersion
IF DBVersion is old
Begin
Alter tables to make them +1 version higher
Insert some stuff
End
DBVersion = CheckTheDBVersion
IF DBVersion is old
Begin
Alter tables to make them +1 version higher
Insert some stuff
End
IF NewInstall = 1
Begin
Insert default data
End
End
Logically the above should work, but it doesn't because of the issue I liked to above. So i have the EXEC or the GO option as far as I can tell. EXEC, as a said above, I think is a hack. Its executing a string as SQL, but I'm not using it in a dynamic manner, I have static sql sitting in it - just seems wrong to me. As far as I can tell GO will break it all to, as it resets the variables.
I have the option to re-write the SQL at this point so if I can do it the correct way, I would. I just don't know the correct way I guess.
EDIT (Example script that seems to be working):
I had to break up the SafetyCheck check in the sample above because I can't put a GO inside a IF statement. That made it a lot more ugly.
-- -- --
-- Run this once to create the initial tables
-- -- --
CREATE TABLE Config (
ID [int] IDENTITY(1,1) NOT NULL,
[Key] [nvarchar](255) NOT NULL,
Value [nvarchar](255) NOT NULL
)
ALTER TABLE Config ADD PRIMARY KEY (ID)
CREATE TABLE Roles (
ID [int] IDENTITY(1,1) NOT NULL,
Name [nvarchar](25) NOT NULL,
Created [DateTime] NOT NULL
)
ALTER TABLE Roles ADD PRIMARY KEY (ID)
-- -- --
-- Below, this is the script thats causing errors
-- -- --
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '#v')
DROP TABLE #v
CREATE TABLE #v (n char(30),v char(30))
INSERT INTO #v (n,v) VALUES ('RunSQL', '1') -- Set to 1 or this does nothing
INSERT INTO #v (n,v) VALUES ('DropTables', '0') -- Set to 1 to drop existing tables
-- dont do this for an upgrade
-- dont do this is there are custom tables
GO
IF (SELECT v FROM #v WHERE n='RunSQL') = 0
BEGIN
SELECT 'Nothing has been done. Read First, Run Later' AS Error
END
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
BEGIN
IF (SELECT v FROM #v WHERE n='DropTables') = 1
BEGIN
-- Remove all tables if they exist
EXEC sp_MSforeachtable @command1 = 'DROP TABLE ?'
END
END
GO
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
BEGIN
IF NOT EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Config')
BEGIN
CREATE TABLE Config (
ID [int] IDENTITY(1,1) NOT NULL,
[Key] [nvarchar](255) NOT NULL,
Value [nvarchar](255) NOT NULL
)
ALTER TABLE Config ADD PRIMARY KEY (ID)
-- Assume this is a new database
INSERT INTO Config ([Key], Value) VALUES ('DBVersion', '3')
END
IF NOT EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Roles')
BEGIN
CREATE TABLE Roles (
ID [int] IDENTITY(1,1) NOT NULL,
Name [nvarchar](25) NOT NULL,
Created [DateTime] NOT NULL,
ModifiedDate [DateTime] NOT NULL
)
ALTER TABLE Roles ADD PRIMARY KEY (ID)
END
END
GO
-- Lookup the current verision of the database
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
BEGIN
INSERT INTO #v (n,v) SELECT 'CMSDBVersion', Value FROM Config WHERE [Key]='DBVersion'
END
GO
-- Update old tables to be like the new ones
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
BEGIN
IF (SELECT COUNT(v) FROM #v WHERE n='CMSDBVersion') = 0
BEGIN
-- Add newer fields to exiting tables
ALTER TABLE Roles ADD ModifiedDate DateTime;
INSERT INTO Config ([Key], Value) Values ('DBVersion', '3')
END
END
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
BEGIN
INSERT INTO #v (n,v) SELECT 'CMSDBVersion', Value FROM Config WHERE [Key]='DBVersion'
END
GO
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
BEGIN
IF (SELECT v FROM #v WHERE n='CMSDBVersion') = '3'
BEGIN
-- Add/Update the standard rules
IF NOT EXISTS (SELECT * FROM Roles WHERE Name='CMS User Admin')
INSERT INTO Roles (Name, Created, ModifiedDate) VALUES ('CMS User Admin', GETDATE(), GETDATE())
END
END
GO
-- remove the temp table
DROP TABLE #v
Upvotes: 0
Views: 394
Reputation: 107736
One way to write it is to break all the parts into separate support SPs. Since each SP is compiled itself at the time it executes, there is no static check on the main SP for dependent objects and consistency across all support SPs, within a single batch-block (separated by GO).
Other than that, rewrite your query using dynamic SQL to effect DDL changes, or use GO statements to break them into batches, as you have discovered.
EDIT
The snippet you have shown doesn't work because:
When RunSQL = 0, this part doesn't run
-- Update old tables to be like the new ones
IF (SELECT v FROM #v WHERE n='RunSQL') = 1
Which causes ModifiedDate not to be added to Roles. When I changed RunSQL to 1, the next part doesn't work
IF (SELECT v FROM #v WHERE n='CMSDBVersion') = ''
Because the prior insert put nothing in the table (there is no record in Config)
INSERT INTO #v (n,v) SELECT 'CMSDBVersion', Value FROM Config WHERE [Key]='DBVersion'
GO
That whole block should just be
-- Update old tables to be like the new ones
IF NOT EXISTS (select * from syscolumns where name='ModifiedDate' and ID = OBJECT_ID('Roles'))
BEGIN
-- Add newer fields to exiting tables
ALTER TABLE Roles ADD ModifiedDate DateTime;
UPDATE Config SET Value = '3' WHERE [Key]='DBVersion'
END
Upvotes: 1