Estou tentando executar um arquivo de script SQL chamando
ExecuteNonQuery(somefile.sql)
e eu recebo uma exceção
Falha na execução da consulta do banco de dados SQL. O servidor SQL pode não estar em execução
Por favor ajude.
Eu tenho um método que executa alguns .sql
arquivos em C#. Ao executar o primeiro arquivo, ele funciona bem. Mas quando ele tenta executar o próximo arquivo (veja a condição else no ExecuteFileBasedOnSequence
método), ele lança a exceção. Eu tentei executar o SQL Script no SQL Server e ele funciona muito bem.
EDITAR:
Esta é a exceção interna:
ERRO [42000] [Microsoft][Driver ODBC SQL Server][SQL Server]Sintaxe incorreta perto de 'Go'
Mas o script funciona bem quando executado no SQL Server.
Aqui está o código C#:
private void ExecuteFileBasedOnSequence(string fileToExecute, bool executeSQLBySequence = true)
{
m_DBConnection.BeginTransaction();
if (executeSQLBySequence)
{
// Read each line of the file into a stringbuilder object to be executed individually (may throw, should just pitch out of this function)
using (StreamReader srSQL = new StreamReader(fileToExecute))
{
// Read each line of the file into a stringbuilder object to be executed individually (may throw, should just pitch out of this function)
string sqlLine;
StringBuilder sqlString = new StringBuilder();
while (!srSQL.EndOfStream)
{
sqlLine = srSQL.ReadLine();
if (string.IsNullOrEmpty(sqlLine) == false)
{
// We don't actually execute the "GO" lines but can use them to determine when to call the executenonquery function
if (string.Compare(sqlLine, "GO", true) == 0)
{
// Make sure we have something to execute
if (string.IsNullOrEmpty(sqlString.ToString()) == false)
{
m_DBConnection.ExecuteNonQuery(sqlString.ToString());
}
sqlString.Clear();
}
// Add the next line to the stringbuilder object
else
{
sqlString.AppendLine(sqlLine);
}
}
}
}
}
else
{
using (StreamReader srSQL = new StreamReader(fileToExecute))
{
if (m_DBConnection != null)
{
m_DBConnection.Open();
m_DBConnection.ExecuteNonQuery(srSQL.ReadToEnd()); //THIS ONE FAILS.
}
}
}
m_DBConnection.CommitTransaction();
}
Estou chamando as funções da seguinte maneira:
// This one works
ExecuteFileBasedOnSequence(@"Database\UpdateTriggersToSQL2017.sql");
// This one is failing
ExecuteFileBasedOnSequence(@"Database\UpgradePharmSpecScripts.sql", false);
Aqui está o arquivo SQL com falha ( UpgradePharmSpecScripts.sql
):
Use PharmSpecDB
Go
--PC1725E-590
IF NOT EXISTS ( SELECT 23 FROM [PharmSpecDB].[dbo].[tblActivityLogMaster] WHERE Lower(strActivityLog) Like 'user rights%' AND LOWER(strActivityLogDesc) Like 'user rights%')
Begin
ALTER TABLE [PharmSpecDB].[dbo].[tblActivityLogMaster] DISABLE TRIGGER I_TRG_ACTIVITYLOGMASTER
Insert into [PharmSpecDB].[dbo].[tblActivityLogMaster](strActivityLog,bDeleted,strActivityLogDesc) values ('User rights modified', 0, 'User rights modified')
ALTER TABLE [PharmSpecDB].[dbo].[tblActivityLogMaster] ENABLE TRIGGER I_TRG_ACTIVITYLOGMASTER
End
Editar: conforme sugerido, removi a linha Go
after Use PharmSpec
no script e recebo um novo erro:
ExecuteNonQuery: A propriedade de texto do comando não foi inicializada
Repita comigo para aqueles que estão atrás:
GO não faz parte da linguagem SQL
É um recurso de certas ferramentas usadas para separar vários lotes dentro de um arquivo, mas não é realmente parte do SQL em si. Você não pode enviá-lo para o SQL Server ou outros bancos de dados diretamente e esperar que eles o entendam.
Parece que você já entendeu isso, já que o código está procurando por esse texto na entrada. Mas claramente a busca existente não é suficiente. Você ainda pode ter espaço em branco extra na linha, por exemplo.
No mínimo, você deve aparar a entrada antes de fazer a verificação (suspeito que o problema seja que o
Go
inUpgradePharmSpecScripts.sql
tem um espaço extra no final). Mas também, esteja ciente de que o motivo pelo qualGO
pode aparecer em um arquivo é porque certas instruções não conseguem coexistir juntas. Você pode descobrir que uma transação nem sempre é apropriada e, às vezes, esses lotes DEVEM vir em conexões separadas.Outro problema que vejo nesse código é a
IsNullOrEmpty()
verificação no loop.Pode ser possível ter linhas em branco significativas .
Por exemplo, você pode ter um literal de string multilinha, onde o espaço em branco é importante, e agora você está pulando esse texto.
Para dar um exemplo, já que isso parece um processo para atualizar um banco de dados relacionado a farmácias em todas as versões de lançamento, imagine que você está fornecendo dados com cada lançamento sobre certos medicamentos, e esses dados são expandidos para incluir instruções de dosagem padrão, onde algumas instruções eventualmente crescem para cobrir vários parágrafos. Esse processo agora destruirá os separadores de parágrafo.
Isso é importante porque também afetará seu teste de separador de lote!
Se você tiver uma instrução SQL com um literal de string multilinha, onde uma das linhas é "GO", isso não é inteligente o suficiente para lidar com isso.
No final das contas, se fosse eu, para esse tipo de processo eu exigiria que cada lote tivesse seu próprio arquivo. A alternativa, se você quer algo que sempre funcione, é construir um analisador SQL completo. Caso contrário, você sempre estará brincando de whack-a-mole.
Uma opção final é enviar os arquivos pelo utilitário , em vez de executá-los primeiro pelo C#. Seu programa atualizador
sqlcmd
do C# ainda pode orquestrar esse processo, mas agora o utilitário sqlcmd pode lidar com os problemas de lote. Claro, isso significa executar sua atualização de uma estação com sqlcmd disponível, o que não é o caso no Windows por padrão.