GO Depois de cada instrução T-SQL

34

Qual é o raciocínio por trás do uso da instrução GO após cada instrução SQL? Entendo que o GO sinaliza o final do lote e / ou permite a reputação das declarações, mas que vantagem ele tem ao usá-lo após cada declaração.

Estou curioso, pois muitas documentações da Microsoft etc. começaram a usá-lo após cada declaração ou talvez eu tenha começado a perceber.

Além disso, o que é considerado uma prática recomendada?

O idiota
fonte

Respostas:

51

Antes de responder quando usá-lo e por quê, é primeiro fundamental entender exatamente o que GOé e o que não é.

A palavra GO- chave é usada pelo SQL Server Management Studio e pelo SQLCMD para significar uma coisa e apenas uma: O final de um lote de instruções. Na verdade, você pode alterar o que você usa para finalizar lotes para algo diferente de "GO":

insira a descrição da imagem aqui

Essa captura de tela acima é uma opção configurável no SSMS.

Mas o que é um lote? Esta referência BOL diz o melhor:

Um lote é um grupo de uma ou mais instruções Transact-SQL enviadas ao mesmo tempo de um aplicativo para o SQL Server para execução.

Simples assim. É apenas uma maneira personalizada de um aplicativo (sim ... um aplicativo) enviar instruções para o SQL Server. Vamos ver um exemplo disso em aparência de aplicativo. Usarei o PowerShell para imitar o que um aplicativo faria para enviar instruções e lotes para o SQL Server:

$ConnectionString = "data source = SomeSQLInstance; initial catalog = AdventureWorks2012; trusted_connection = true; application name = BatchTesting;"

try {
    $SqlConnection = New-Object System.Data.SqlClient.SqlConnection($ConnectionString)
    $SqlCmd = New-Object System.Data.SqlClient.SqlCommand
    $SqlCmd.Connection = $SqlConnection

    # first batch of statements
    #
    $SqlCmd.CommandText = "
        select * from humanresources.department where departmentid = 1;
        select * from humanresources.department where departmentid = 2;
        select * from humanresources.department where departmentid = 3;
        select * from humanresources.department where departmentid = 4;"

    # execute the first batch
    #
    $SqlConnection.Open()
    $SqlCmd.ExecuteNonQuery()
    $SqlConnection.Close()

    # second batch of statements
    #
    $SqlCmd.CommandText = "
        select * from humanresources.department where departmentid = 5;
        select * from humanresources.department where departmentid = 6;
        select * from humanresources.department where departmentid = 7;
        select * from humanresources.department where departmentid = 8;"

    # execute the second batch
    #
    $SqlConnection.Open()
    $SqlCmd.ExecuteNonQuery()
    $SqlConnection.Close()
}
catch {
    $SqlCmd.Dispose()
    $SqlConnection.Dispose()
    Write-Error $_.Exception
}

Os comentários revelam isso, mas você pode ver acima que estamos enviando programaticamente dois lotes para o SQL Server. Vamos verificar isso, no entanto. Minha escolha aqui é usar Eventos Estendidos:

create event session BatchTesting
on server
add event sqlserver.sql_batch_starting
(
    set
        collect_batch_text = 1
    where
    (
        sqlserver.client_app_name = N'BatchTesting'
    )
),
add event sqlserver.sql_batch_completed
(
    set
        collect_batch_text = 1
    where
    (
        sqlserver.client_app_name = N'BatchTesting'
    )
),
add event sqlserver.sql_statement_starting
(
    set
        collect_statement = 1
    where
    (
        sqlserver.client_app_name = N'BatchTesting'
    )
),
add event sqlserver.sql_statement_completed
(
    set
        collect_statement = 1
    where
    (
        sqlserver.client_app_name = N'BatchTesting'
    )
)
add target package0.event_file
(
    set
        filename = N'<MyXelLocation>\BatchTesting.xel'
);
go

alter event session BatchTesting
on server
state = start;
go

Tudo o que esta sessão do XEvents está fazendo é capturar as instruções e lotes que são iniciados e concluídos a partir de um aplicativo chamado "BatchTesting"(se você observar minha cadeia de conexão no meu exemplo de código do PowerShell, é uma maneira rápida de observar um originador específico de eventos usando o "application nome "parâmetro da cadeia de conexão e filtragem disso).

Depois de executar o código do PowerShell para enviar esses lotes e instruções, vejo os seguintes resultados:

insira a descrição da imagem aqui

Como você pode ver na captura de tela, é claro como as instruções são divididas em dois lotes diferentes, também evidentes pelos meios que usamos para chamá-los. E se examinarmos batch_texta primeira ocorrência de sql_batch_starting, poderemos ver todas as instruções incluídas nesse lote:

    select * from humanresources.department where departmentid = 1;
    select * from humanresources.department where departmentid = 2;
    select * from humanresources.department where departmentid = 3;
    select * from humanresources.department where departmentid = 4;

Com essa explicação sobre o que é um lote, agora vem a resposta para sua pergunta sobre quando finalizar lotes. As regras para lotes são encontradas nesta referência BOL em relação aos lotes :

As instruções CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER e CREATE VIEW não podem ser combinadas com outras instruções em um lote. A instrução CREATE deve iniciar o lote. Todas as outras instruções a seguir nesse lote serão interpretadas como parte da definição da primeira instrução CREATE.

Uma tabela não pode ser alterada e, em seguida, as novas colunas referenciadas no mesmo lote.

Se uma instrução EXECUTE for a primeira em um lote, a palavra-chave EXECUTE não será necessária. A palavra-chave EXECUTE será necessária se a instrução EXECUTE não for a primeira no lote.

Da mesma forma, certos erros de tempo de execução (erros de compilação não permitem a execução de um lote) que ocorrem durante um lote podem causar comportamentos diferentes: abortar totalmente o lote ou continuar o lote e abortar apenas a declaração incorreta (a descrição acima O link fornece dois exemplos muito bons: um erro de estouro aritmético, por exemplo, interrompe a execução do lote, enquanto um erro de violação de restrição impede a conclusão da instrução atual, mas o lote continua em execução).

Como muitas coisas em nossa profissão, porém, a preferência pessoal será uma grande força motriz por trás de como você, como indivíduo e escritor do código T-SQL, finaliza lotes. Algumas pessoas definem explicitamente apenas lotes quando precisam (veja os requisitos acima) e outras finalizam lotes programaticamente 100% das vezes , mesmo quando estão executando apenas uma única instrução em uma janela de consulta no SSMS. A maioria das pessoas geralmente cai em algum lugar no meio desses dois limites. Pelo que vale, os terminadores de instruções têm os mesmos seguidores, com também muito poucos requisitos impostos. Grande parte de tudo isso é estilo de código , onde não é imposto (no SSMS e no SQLCMD).

Thomas Stringer
fonte
Talvez seja um comentário ingênuo, mas me parece que o SQL Server poderia ter determinado o que executar em lotes (da mesma maneira que muitas outras otimizações são tratadas pelo mecanismo de banco de dados). Por exemplo, usando as regras que você descreveu em vez de confiar no usuário para escrevê-las, o que pode estar sujeito a erros e parece adicionar inchaço desnecessário aos scripts.
Steve Chambers
11
@SteveChambers meu sentimento exatamente. A resposta diz "é simples assim" (um lote é um grupo de uma ou mais instruções Transact-SQL enviadas ao mesmo tempo de um aplicativo para o SQL Server para execução.) Mas não é. Existem combinações de instruções que eu poderia tentar enviar em um lote que falha. Por fim, acho que você precisa entender por que e como o envio de um lote é diferente do envio de um conjunto de instruções individuais - por isso, finalmente contribuí com minha própria resposta: stackoverflow.com/a/56370223/3714936 - que também fala com seu comentário .
youcantryreachingme