Comment demander si un schéma de base de données existe


98

Dans le cadre de notre processus de construction, nous exécutons un script de mise à jour de la base de données lorsque nous déployons du code dans 4 environnements différents. De plus, étant donné que la même requête sera ajoutée jusqu'à ce que nous mettions une version en production, elle doit pouvoir s'exécuter plusieurs fois sur une base de données donnée. Comme ça:

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

Actuellement, j'ai une instruction de création de schéma dans le script de déploiement / construction. Où dois-je rechercher l'existence d'un schéma?


2
Veuillez envisager de modifier la réponse acceptée. Il n'est pas possible que la réponse que vous avez acceptée ait fonctionné pour vous telle qu'elle est écrite.
Aaron Bertrand

Réponses:


165

Cherchez-vous des sys.schemas ?

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

Notez que le CREATE SCHEMAdoit être exécuté dans son propre lot (selon la réponse ci-dessous )


Darn ... dans le temps qu'il m'a fallu pour éditer le message pour le rendre plus lisible ... vous avez résolu mon problème. Merci beaucoup!
Pulsehead

18
cela ne fonctionne pas dans SQL 2008 car le CREATE SCHEMA doit être la première instruction d'un lot, voir le post vfilby pour une solution de contournement
sergiom

4
Vous pouvez utiliser 'Select 1 from sys.schemas' pour améliorer les performances.
vijaysylvester

4
@vijaysylvester Non, c'est un mythe. SQL Server optimise la liste des colonnes afin que ce que vous y mettez n'a pas d'importance. Complètement ignoré. Vous voulez une preuve? PutSELECT 1/0...
Aaron Bertrand le

1
J'ai mis à jour cette réponse pour qu'elle ne soit pas incorrecte (c'est-à-dire pour utiliser le script ci-dessous stackoverflow.com/a/521271/2688 )
bdukes

157

@bdukes a raison pour déterminer si le schéma existe, mais l'instruction ci-dessus ne fonctionnera pas dans SQL Server 2005. CREATE SCHEMA <name>doit s'exécuter dans son propre lot. Une solution consiste à exécuter l' CREATE SCHEMAinstruction dans un exécutable.

Voici ce que j'ai utilisé dans mes scripts de construction:

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

fonctionne comme un charme! cela me permet même de mettre mes déclarations imprimées et tout.
Tony

2

Ceci est vieux, donc je me sens obligé d'ajouter: Pour SQL SERVER 2008+, tout cela fonctionne (pour la partie sélectionnée), puis utilisez EXECUTE('CREATE SCHEMA <name>')pour le créer réellement sur des résultats négatifs.

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 NULLfonctionne bien et semble un peu plus pratique que la réponse acceptée.
BradC

1

Juste pour être plus "défensif", la version suivante génère une erreur de conversion de type pour tenir compte de la possibilité (cependant improbable) de> 1 correspondance Schemasimilaire à la façon dont le code de validation lance souvent intentionnellement des exceptions parce que je pense que c'est bon et je crois que c'est "" meilleure pratique "" pour tenir compte de tous les résultats de retour possibles, même s'ils sont peu probables et même s'il s'agit simplement de générer une exception fatale, car les effets connus de l'arrêt du traitement sont généralement meilleurs que les effets en cascade inconnus d'erreurs non piégées. Parce que c'est hautement improbable, je ne pensais pas que cela valait la peine d'une Countvérification séparée + Throwou Try- Catch- Throwpour générer une erreur fatale plus conviviale, mais toujours une erreur 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

Ensuite:

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

Je suppose qu'il est possible d'avoir plus d'un schéma correspondant lorsque vous utilisez un classement sensible à la casse, mais votre "gestion des erreurs" entraînera l'erreur suivante: La conversion a échoué lors de la conversion de la valeur varchar 'ERROR' en type de données int.
user247702

@Stijn: C'est "By Design" similaire à la façon dont le code de validation est souvent intentionnel Throw Exception. Comme vous l'avez dit, ce n'est pas "" probable "", donc à mon humble avis, cela ne valait pas la peine d'un contrôle complet Try- Catchou séparé Countpour générer une erreur fatale plus conviviale, mais quoi qu'il en soit, je voudrais probablement une erreur fatale. Je crois et je pense que c'est "la meilleure pratique" de tenir compte de tous les résultats de retour possibles, même s'ils sont improbables et même si c'est juste pour générer une exception fatale, car les effets connus de l'arrêt du traitement sont généralement meilleurs que les effets en cascade inconnus de non piégé les erreurs.
Tom

Tout cela semble bien, je ne savais pas si c'était intentionnel :) Votre réponse pourrait bénéficier de quelques explications supplémentaires, comme vous venez de le donner dans votre commentaire.
user247702

@Stijn: Ma bête noire est la "meilleure pratique" courante, qui consiste à ne pas vérifier si un Select,Insert , Updateou Deletedéclaration est revenu / affecté plus ou moins que le nombre attendu de lignes mais peu probables. Même s'il y a (sont) Unique Indexactuellement garantissant le nombre attendu de lignes (c'est-à-dire 1) à renvoyer / à affecter, cela pourrait changer (accidentellement ou (à courte vue) "" intentionnellement "") dans le futur.
Tom

1

Si la disposition des composants le permet, cela fonctionne également.

IF EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'myschema') SET NOEXEC ON 
aller
CREER SCHEMA myschema
ALLER 
SET NOEXEC OFF - si un traitement supplémentaire est nécessaire.
ALLER
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.