128

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?

David Clarke
  • 12,888
  • 9
  • 86
  • 116
Pulsehead
  • 5,050
  • 9
  • 33
  • 37
  • 2
    Please consider changing the accepted answer. It's not possible that the answer you accepted actually worked for you as written. – Aaron Bertrand Sep 05 '14 at 00:53

7 Answers7

210

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)

Community
  • 1
  • 1
bdukes
  • 152,002
  • 23
  • 148
  • 175
  • Darn... in the time it took me to edit the post to make it more readable... you fixed my problem. Thanks mucho! – Pulsehead Oct 22 '08 at 15:00
  • 20
    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 – sergiom Mar 30 '10 at 15:09
  • 4
    You could use 'Select 1 from sys.schemas' to improve the performance. – vijaysylvester Nov 26 '12 at 09:17
  • 6
    @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...` – Aaron Bertrand Sep 04 '14 at 20:23
  • And yes, the other, higher-voted answer should be the one that is accepted. This doesn't work as written. – Aaron Bertrand Sep 04 '14 at 20:24
  • 1
    I've updated this answer to not be incorrect (i.e. to use the script from below http://stackoverflow.com/a/521271/2688) – bdukes Jan 30 '15 at 21:36
  • 3
    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. – bielawski May 13 '19 at 19:36
169

@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
Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
vfilby
  • 9,938
  • 9
  • 49
  • 62
5

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
Mark Schultheiss
  • 32,614
  • 12
  • 69
  • 100
  • 2
    `IF schema_id ('MySchemaName') IS NULL` works well, and seems a bit more convenient than the accepted answer. – BradC Oct 10 '19 at 15:58
  • Agree @BradC. For those, who gets exception: IF SCHEMA_ID('out') IS NULL EXEC('CREATE SCHEMA [out] AUTHORIZATION [out]'); – Juozas Sep 16 '20 at 11:45
2

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
benik9
  • 151
  • 4
1

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
Tom
  • 870
  • 11
  • 13
  • 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.* – user247702 Oct 24 '17 at 13:46
  • @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. – Tom Oct 25 '17 at 15:17
  • 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. – user247702 Oct 25 '17 at 15:21
  • @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. – Tom Oct 25 '17 at 15:21
0
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 :)

  • 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 – David Sopko Jul 14 '22 at 01:01
0

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

David Sopko
  • 5,263
  • 2
  • 38
  • 42