Come interrogare se esiste uno schema di database


98

Come parte del nostro processo di compilazione, eseguiamo uno script di aggiornamento del database mentre distribuiamo il codice in 4 ambienti diversi. Inoltre, poiché la stessa query verrà aggiunta fino a quando non rilasciamo una versione in produzione, deve essere in grado di essere eseguita più volte su un determinato database. Come questo:

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

Attualmente ho un'istruzione di creazione dello schema nello script di distribuzione / compilazione. Dove chiedo l'esistenza di uno schema?


2
Si prega di considerare di modificare la risposta accettata. Non è possibile che la risposta che hai accettato abbia effettivamente funzionato per te come scritta.
Aaron Bertrand,

Risposte:


165

Stai cercando sys.schemas ?

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

Nota che CREATE SCHEMAdeve essere eseguito nel proprio batch (secondo la risposta di seguito )


Maledizione ... nel tempo che mi ci è voluto per modificare il post per renderlo più leggibile ... hai risolto il mio problema. Grazie mucho!
Pulsehead

18
questo non funziona in SQL 2008 perché CREATE SCHEMA deve essere la prima istruzione in un batch, vedere il post di vfilby per una soluzione alternativa
sergiom

4
È possibile utilizzare "Seleziona 1 da sys.schemas" per migliorare le prestazioni.
vijaysylvester

4
@vijaysylvester No, questo è un mito. SQL Server ottimizza l'elenco delle colonne, quindi non importa cosa ci metti. Completamente ignorato. Vuoi una prova? PutSELECT 1/0...
Aaron Bertrand

1
Ho aggiornato questa risposta di non essere corretto (cioè utilizzare lo script dal basso stackoverflow.com/a/521271/2688 )
bdukes

157

@bdukes ha ragione per determinare se lo schema esiste, ma la dichiarazione di cui sopra non funzionerà in SQL Server 2005. CREATE SCHEMA <name>deve essere eseguita nel proprio batch. Una soluzione è eseguire l' CREATE SCHEMAistruzione in un exec.

Ecco cosa ho usato nei miei script di build:

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

funziona come un fascino! questo mi permette anche di mettere le mie dichiarazioni stampate e tutto il resto.
Tony

2

Questo è vecchio quindi mi sento in dovere di aggiungere: Per SQL SERVER 2008+ Funzionano tutti (per la parte selezionata), quindi utilizzarli EXECUTE('CREATE SCHEMA <name>')per crearli effettivamente con risultati negativi.

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

IF schema_id ('MySchemaName') IS NULLfunziona bene e sembra un po 'più conveniente della risposta accettata.
BradC

1

Solo per essere più "difensivo", la seguente versione genera un errore di conversione del tipo per tenere conto della possibilità (per quanto improbabile) di corrispondenze> 1 Schemasimili a come il codice di convalida spesso lancia intenzionalmente le eccezioni perché credo che sia buono e credo che lo sia "'best practice'" per tenere conto di tutti i possibili risultati di ritorno, per quanto improbabili e anche se si tratta solo di generare un'eccezione fatale perché gli effetti noti dell'interruzione dell'elaborazione sono generalmente migliori degli effetti a cascata sconosciuti di errori non intrappolati. Poiché è altamente improbabile, non ho pensato che valesse la pena di un Countcontrollo separato + Throwo Try- Catch- Throwper generare un errore fatale più user-friendly ma comunque un errore fatale.

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

Poi:

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

Suppongo che sia possibile avere più di uno schema corrispondente quando si utilizzano regole di confronto con distinzione tra maiuscole e minuscole, ma la "gestione degli errori" comporterà il seguente errore: Conversione non riuscita durante la conversione del valore varchar "ERROR" nel tipo di dati int.
user247702

@Stijn: Questo è "By Design" simile a come il codice di validazione è spesso intenzionalmente Throw Exception. Come hai detto tu, non è " 'probabile'" per accadere, quindi IMHO, non valeva la pena nel suo complesso Try- Catcho separati Countdi controllo per generare un errore fatale più user-friendly, ma a prescindere, avrei probabilmente vuole un errore fatale. Credo in e credo che sia "la migliore pratica" tenere conto di tutti i possibili risultati di ritorno per quanto improbabili e anche se si tratta solo di generare un'eccezione fatale perché gli effetti noti dell'arresto dell'elaborazione sono generalmente migliori degli effetti a cascata sconosciuti di un-trapped errori.
Tom

Sembra tutto a posto, non ero sicuro che fosse intenzionale :) La tua risposta potrebbe beneficiare di qualche spiegazione aggiuntiva, come hai appena fornito nel tuo commento.
user247702

@Stijn: Il mio piccolo cruccio è il comune non così " 'best practice'" di non controllare se un Select, Insert, Updateo DeleteStatement restituito / colpito più o meno del previsto # di righe però improbabili. Anche se ci sono Unique Indexattualmente garanzie che il numero previsto (cioè 1) di righe verrà restituito / influenzato, ciò potrebbe cambiare (accidentalmente o (miope) "'intenzionalmente'") in futuro.
Tom

1

Se il layout dei componenti lo consente, funziona anche questo.

SE ESISTE (SELEZIONA 1 DA sys.schemas WHERE name = 'myschema') ATTIVA NOEXEC 
partire
CREATE SCHEMA myschema
PARTIRE 
IMPOSTARE NOEXEC OFF - se è necessaria un'ulteriore elaborazione.
PARTIRE
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.