Como faço para consultar se existe um esquema de banco de dados

98

Como parte do nosso processo de construção, executamos um script de atualização do banco de dados à medida que implantamos o código em 4 ambientes diferentes. Além disso, como a mesma consulta será adicionada até colocarmos uma versão em produção, ela deve ser capaz de ser executada várias vezes em um determinado banco de dados. Como isso:

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

Atualmente, tenho uma instrução de esquema de criação no script de implantação / construção. Onde posso consultar a existência de um esquema?

Pulsehead
fonte
2
Considere alterar a resposta aceita. Não é possível que a resposta que você aceitou tenha funcionado para você da forma como foi escrita.
Aaron Bertrand,

Respostas:

165

Você está procurando por sys.schemas ?

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

Observe que o CREATE SCHEMAdeve ser executado em seu próprio lote (de acordo com a resposta abaixo )

bdukes
fonte
Droga ... no tempo que demorei para editar a postagem para torná-la mais legível ... você corrigiu meu problema. Obrigado mucho!
Pulsehead de
18
isso não funciona no SQL 2008 porque CREATE SCHEMA precisa ser a primeira instrução em um lote, consulte a postagem vfilby para obter uma solução alternativa
sergiom
4
Você pode usar 'Select 1 from sys.schemas' para melhorar o desempenho.
vijaysylvester
4
@vijaysylvester Não, isso é um mito. O SQL Server otimiza a lista de colunas, portanto, não importa o que você colocar lá. Totalmente ignorado. Quer uma prova? PutSELECT 1/0...
Aaron Bertrand
1
Eu atualizei esta resposta para não ficar incorreta (ou seja, para usar o script abaixo stackoverflow.com/a/521271/2688 )
bdukes
157

@bdukes acertou em cheio para determinar se o esquema existe, mas a instrução acima não funcionará no SQL Server 2005. CREATE SCHEMA <name>precisa ser executado em seu próprio lote. Uma solução alternativa é executar a CREATE SCHEMAinstrução em um exec.

Aqui está o que eu usei em meus scripts de construção:

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
vfilby
fonte
Funciona como um encanto! isso até me permite colocar minhas declarações de impressão e tudo mais.
Tony
2

Isso é antigo, então me sinto compelido a adicionar: Para SQL SERVER 2008+ Tudo isso funciona (para a parte selecionada) e, em seguida, use EXECUTE('CREATE SCHEMA <name>')para realmente criá-lo em resultados negativos.

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
fonte
IF schema_id ('MySchemaName') IS NULLfunciona bem e parece um pouco mais conveniente do que a resposta aceita.
BradC
1

Apenas para ser extra "defensivo", a versão a seguir gera um erro de conversão de tipo para levar em conta a possibilidade (embora improvável) de> 1 correspondência Schemasemelhante a como o código de validação muitas vezes intencionalmente lança exceções porque acredito que é bom e acredito que é "'prática recomendada'" para considerar todos os resultados de retorno possíveis, por mais improváveis ​​que sejam, e mesmo que seja apenas para gerar uma exceção fatal, porque os efeitos conhecidos de interromper o processamento geralmente são melhores do que os efeitos em cascata desconhecidos de erros não interceptados. Por ser altamente improvável, não acho que vale a pena fazer uma Countverificação separada + Throwou Try- Catch- Throwpara gerar um erro fatal mais amigável, mas ainda assim um erro fatal.

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

Então:

if @HasSchemaX = 1
begin
   ...
end -- if @HasSchemaX = 1
Tom
fonte
Suponho que seja possível ter mais de um esquema correspondente ao usar um agrupamento com distinção entre maiúsculas e minúsculas, mas seu "tratamento de erros" resultará no seguinte erro: Falha na conversão ao converter o valor varchar 'ERROR' para o tipo de dados int.
user247702
@Stijn: Isso é "por design" semelhante a como o código de validação costuma ser intencionalmente Throw Exception. Como você disse, não é "'provável'" que aconteça, então IMHO, não valeu um todo Try-Catch ou uma Countverificação separada para gerar um erro fatal mais amigável, mas independentemente, eu provavelmente gostaria de um erro fatal. Eu acredito e acredito que seja "a melhor prática" levar em consideração todos os resultados de retorno possíveis, por mais improváveis ​​que sejam, e mesmo que seja apenas para gerar uma exceção fatal, porque os efeitos conhecidos de interromper o processamento são geralmente melhores do que os efeitos em cascata desconhecidos do não-trapping erros.
Tom,
Parece que tudo está bem, não tenho certeza se foi intencional :) Sua resposta pode se beneficiar de alguma explicação adicional, como você acabou de dar em seu comentário.
user247702
@Stijn: Minha implicância é o comum não tão "" melhores práticas" de não verificar se um Select, Insert, Updateou DeleteDeclaração retornou / afetou mais ou menos do que o esperado # de linhas no entanto improváveis. Mesmo se houver (há)Unique Index atualmente garantindo o número esperado (ou seja, 1) de linhas a serem retornadas / afetadas, isso poderia mudar (acidentalmente ou (imprevisivelmente) "'intencionalmente'") no futuro.
Tom,
1

Se o layout dos componentes permitir, isso também funciona.

IF EXISTS (SELECT 1 FROM sys.schemas WHERE name = 'myschema') SET NOEXEC ON 
ir
CRIAR ESQUEMA meu esquema
IR 
SET NOEXEC OFF - se qualquer processamento adicional for necessário.
IR
Benik9
fonte