135

As part of our build process we run a database update script as we deploy code to 4 different environments. Further, since the same query will get added to until we drop a release into production it has to be able to run multiple times on a given database. Like this:

IF NOT EXISTS (SELECT * FROM sys.tables WHERE object_id = OBJECT_ID(N'[Table]'))
BEGIN
  CREATE TABLE [Table]
  (...)
END

Currently I have a create schema statement in the deployment/build script. Where do I query for the existence of a schema?

1
  • 2
    Please consider changing the accepted answer. It's not possible that the answer you accepted actually worked for you as written. Commented Sep 5, 2014 at 0:53

7 Answers 7

219

Are you looking for sys.schemas?

IF NOT EXISTS (SELECT * FROM sys.schemas WHERE name = 'jim')
BEGIN
EXEC('CREATE SCHEMA jim')
END

Note that the CREATE SCHEMA must be run in its own batch (per the answer below)

Sign up to request clarification or add additional context in comments.

7 Comments

this doesn't work in SQL 2008 because the CREATE SCHEMA needs to be the first statement in a batch, see the vfilby post for a workaround
You could use 'Select 1 from sys.schemas' to improve the performance.
@vijaysylvester No, this is a myth. SQL Server optimizes away the column list so it doesn't matter what you put there. Completely ignored. Want proof? Put SELECT 1/0...
I've updated this answer to not be incorrect (i.e. to use the script from below stackoverflow.com/a/521271/2688)
While * vs 1 vs 1/0 may not make any performance difference there is a case where it does make a difference. From a security standpoint, all 3 choices require you to have access to all columns while naming a specific column causes only columns actually named in the query to be checked. So 'select col-with-access...' succeeds while 'select 1 ...' won't if any column of any table in the query is denied.
|
174

@bdukes is right on the money for determining if the schema exists, but the statement above won't work in SQL Server 2005. CREATE SCHEMA <name> needs to run in its own batch. A work around is to execute the CREATE SCHEMA statement in an exec.

Here is what I used in my build scripts:

IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = '<name>')
BEGIN
    -- The schema must be run in its own batch!
    EXEC( 'CREATE SCHEMA <name>' );
END

1 Comment

works like a charm! this even lets me put my print statements and everything.
6

This is old so I feel compelled to add: For SQL SERVER 2008+ These all work (for the select part), then use EXECUTE('CREATE SCHEMA <name>') to actually create it on negative results.

DECLARE @schemaName sysname = 'myfunschema';
-- shortest
If EXISTS (SELECT 1 WHERE SCHEMA_ID(@schemaName) IS NOT NULL)
PRINT 'YEA'
ELSE
PRINT 'NOPE'

SELECT DB_NAME() AS dbname WHERE SCHEMA_ID(@schemaName) IS NOT NULL -- nothing returned if not there

IF NOT EXISTS ( SELECT  top 1 *
                FROM    sys.schemas
                WHERE   name = @schemaName )
PRINT 'WOOPS MISSING'
ELSE
PRINT 'Has Schema'

SELECT SCHEMA_NAME(SCHEMA_ID(@schemaName)) AS SchemaName1 -- null if not there otherwise schema name returned

SELECT SCHEMA_ID(@schemaName) AS SchemaID1-- null if not there otherwise schema id returned


IF EXISTS (
    SELECT sd.SchemaExists 
    FROM (
        SELECT 
            CASE 
                WHEN SCHEMA_ID(@schemaName) IS NULL THEN 0
                WHEN SCHEMA_ID(@schemaName) IS NOT NULL THEN 1
                ELSE 0 
            END AS SchemaExists
    ) AS sd
    WHERE sd.SchemaExists = 1
)
BEGIN
    SELECT 'Got it';
END
ELSE
BEGIN
    SELECT 'Schema Missing';
END

2 Comments

IF schema_id ('MySchemaName') IS NULL works well, and seems a bit more convenient than the accepted answer.
Agree @BradC. For those, who gets exception: IF SCHEMA_ID('out') IS NULL EXEC('CREATE SCHEMA [out] AUTHORIZATION [out]');
3

If the layout of components allows it, this works too.

IF EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'myschema') SET NOEXEC ON 
go
CREATE SCHEMA myschema
GO 
SET NOEXEC OFF -- if any further processing is needed.
GO

Comments

2

Just to be extra "defensive", the following version generates a Type conversion error to account for the possibility (however unlikely) of > 1 matching Schema's similar to how validation code often intentionally Throw Exception's because I believe it's good to and I believe it's "'best practice'" to account for all possible return results however unlikely and even if it's just to generate a fatal exception because the known effects of stopping processing is usually better than unknown cascading effects of un-trapped errors. Because it's highly unlikely, I didn't think it's worth the trouble of a separate Count check + Throw or Try-Catch-Throw to generate a more user-friendly fatal error but still fatal error nonetheless.

SS 2005-:

declare @HasSchemaX bit
set @HasSchemaX = case (select count(1) from sys.schemas where lower(name) = lower('SchemaX')) when 1 then 1 when 0 then 0 else 'ERROR' end

SS 2008+:

declare @HasSchemaX bit = case (select count(1) from sys.schemas where lower(name) = lower('SchemaX')) when 1 then 1 when 0 then 0 else 'ERROR' end

Then:

if @HasSchemaX = 1
begin
   ...
end -- if @HasSchemaX = 1

4 Comments

I suppose it's possible to have more than one matching schema when you use a case sensitive collation, but your "error handling" will result in the following error: Conversion failed when converting the varchar value 'ERROR' to data type int.
@Stijn: That's "By Design" similar to how validation code often intentionally Throw Exception's. Like you said, it's not "'likely'" to happen, so IMHO, it wasn't worth a whole Try-Catch or separate Count check to generate a more user-friendly fatal error, but regardless, I would likely want a fatal error. I believe in and I believe it's "'best practice'" to account for all possible return results however unlikely and even if it's just to generate a fatal exception because the known effects of stopping processing is usually better than unknown cascading effects of un-trapped errors.
That all sounds fine, I wasn't sure whether it was intentional :) Your answer could benefit from some additional explanation, like you just gave in your comment.
@Stijn: My pet peeve is the common not-so "'best practice'" of not checking if a Select, Insert, Update or Delete Statement returned / affected more or less than the expected # of Rows however unlikely. Even if there is(are) Unique Index'es currently ensuring the expected # (i.e. 1) of Rows to be returned / affected, that could change (accidentally or (short-sightedly) "'intentionally'") in the future.
2
IF NOT EXISTS (SELECT TOP (1) 1 FROM [sys].[schemas] WHERE [name] = 'Person')
BEGIN
    EXEC ('CREATE SCHEMA [Person]')
END

IF NOT EXISTS (SELECT TOP (1) 1 FROM [sys].[tables] AS T
               INNER JOIN [sys].[schemas] AS S ON S.schema_id = T.schema_id
               WHERE T.[name] = 'Guests' AND S.[name] = 'Person')
BEGIN
    EXEC ('CREATE TABLE [Person].[Guests]
           (
                [GuestId] INT IDENTITY(1, 1) NOT NULL,
                [Forename] NVARCHAR(100) NOT NULL,
                [Surname] NVARCHAR(100) NOT NULL,
                [Email] VARCHAR(255) NOT NULL,
                [BirthDate] DATETIME2 NULL,
                CONSTRAINT [PK_Guests_GuestId] PRIMARY KEY CLUSTERED ([GuestId]),
                CONSTRAINT [UX_Guests_Email] UNIQUE([Email])
           )')
END

NOTICE: CREATE SCHEMA AND CREATE TABLE NEED COMPLETLY SEPARATED BATCH TO EXECUTE

TO MORE DESCRIPTION VISIT MICROSOFT DOCS WEBSITE :)

1 Comment

Putting the create table in it's own batch isn't necessary when tested on Microsoft SQL Azure (RTM) - 12.0.2000.8 May 12 2022 and using SSMS v18
2

As of SQL Server 2005 version 9.0 you can use the INFORMATION_SCHEMA.SCHEMATA view to check if the schema exists:

IF NOT EXISTS (
SELECT  SCHEMA_NAME
FROM    INFORMATION_SCHEMA.SCHEMATA
WHERE   SCHEMA_NAME =  '<schema name>' )
 
BEGIN
    EXEC sp_executesql N'CREATE SCHEMA <schema name>'  
END
GO

INFORMATION_SCHEMA views are the ISO standard and are generally preferable; these were adopted to make the syntax more consistent across different SQL database platforms.

Note that the CREATE SCHEMA must be run in its own batch

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.