2010-07-18 20 views
19

¿Cómo puedo validar scripts sql antes de ejecutarlos usando .net 2.0 y C#?Código para validar scripts SQL

Si el sql no es válido, quiero devolver las filas de error.

+0

relacionada (pero sin el # ángulo C) http://stackoverflow.com/questions/3084387/how-can-i-programmatically-check-parse-the-validity-of-a-tsql-statement –

+0

posible duplicado de [herramienta de migración SQL] (http://stackoverflow.com/questions/3272894/sql-migration-tool) –

+0

@ p.campbell y marque este como un duplicado. –

Respuesta

39

Si va a crear una herramienta que permite que el usuario introduzca un código SQL a mano y desea validar el código introducido con el código C# antes de la ejecución en el servidor SQL, puede crear un método de esta manera:

using Microsoft.Data.Schema.ScriptDom; 
using Microsoft.Data.Schema.ScriptDom.Sql; 

public class SqlParser 
{ 
     public List<string> Parse(string sql) 
     { 
      TSql100Parser parser = new TSql100Parser(false); 
      IScriptFragment fragment; 
      IList<ParseError> errors; 
      fragment = parser.Parse(new StringReader(sql), out errors); 
      if (errors != null && errors.Count > 0) 
      { 
       List<string> errorList = new List<string>(); 
       foreach (var error in errors) 
       { 
        errorList.Add(error.Message); 
       } 
       return errorList; 
      } 
      return null; 
     } 
} 

a partir de 2018 y las nuevas versiones de bases de datos, esto podría ser una versión más nueva:

using Microsoft.SqlServer.TransactSql.ScriptDom; 

(descargue con npm: PM> Install-Package Microsoft.SqlServer.TransactSql.ScriptDom -Version 14.0.3811.1)

public bool IsSQLQueryValid(string sql, out List<string> errors) 
{ 
    errors = new List<string>(); 
    TSql140Parser parser = new TSql140Parser(false); 
    TSqlFragment fragment; 
    IList<ParseError> parseErrors; 

    using (TextReader reader = new StringReader(sql)) 
    { 
     fragment = parser.Parse(reader, out parseErrors); 
     if (parseErrors != null && parseErrors.Count > 0) 
     { 
      errors = parseErrors.Select(e => e.Message).ToList(); 
      return false; 
     } 
    } 
    return true; 
} 
+1

+1 interesante ... no sabía sobre la clase TSql100Parser –

+0

Fuera de esto sonando como algo de una película de terminador, +1 ya que no he oído hablar de esto tampoco . Aquí hay un enlace directo a MSDN para futuros lectores de preguntas: https://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.tsqlparser%28v=vs.100%29.aspx – Tommy

+2

Hay un paquete nuget para el que no se puede encontrar (o no es probable que haga referencia a local) ScriptDom dll's: https://www.nuget.org/packages/Microsoft.SqlServer.TransactSql.ScriptDom/ –

0

¿Qué significa SQL "válido"? La sintaxis o los resultados?

La única forma segura de validar la sintaxis es ejecutar SQL en SQL Server. ¿Ha considerado ejecutar el SQL en una transacción y luego realizar una reversión al final?

Begin Transaction 

--execute your code between the 'Begin Transaction' and the 'rollback' keywords. 
... 

--example 
Insert into mytable(ID)Values(2) 

... 

Rollback 

MSDN Documentation de rollback

+0

¿Cómo puedo hacerlo? ¿Puedes escribir un ejemplo? Gracias. –

+0

@Tamifist: utilice un TransactionScope y nunca llame a Transaction.Complete. Hay muchos ejemplos de TransactionScope aquí en stackoverflow (pero tenga en cuenta que primero debe crear su TransactionScope y dentro de eso SqlConnection. –

15

SSMS tiene una forma de hacer esto.

Si usa el Analizador de SQL verá que se ejecuta SET PARSEONLY ON, luego el SQL y luego SET PARSEONLY OFF y cualquier error se levanta sin compilar o ejecutar la consulta.

SET PARSEONLY ON; 
SELECT * FROM Table; --Query To Parse 
SET PARSEONLY OFF; 

PARSEONLY

que nunca han intentado esto desde C# pero no veo razón para que no debería funcionar, funciona desde SSMS después de todo.

Como Martin Smith señala en los comentarios se puede utilizar SET NOEXEC ON

MSDN dice lo siguiente acerca de ambos comandos.

Cuando SET NOEXEC es ON, SQL Server compila cada lote de Transact-SQL declaraciones, pero no las ejecuta. Cuando SET NOEXEC está desactivado, todos los lotes se ejecutan después de la compilación.

Cuando SET PARSEONLY está activado, SQL Server solo analiza la instrucción. Cuando SET PARSEONLY está desactivado, SQL Server compila y ejecuta la instrucción.

Eso indica que NOEXEC también compilará la consulta donde PARSEONLY no lo hará. Por lo tanto, NOEXEC puede detectar errores que PARSEONLY no. El uso es el mismo.

SET NOEXEC ON; 
SELECT * FROM Table; --Query To Parse 
SET NOEXEC OFF; 

NOEXEC

+0

Creo que 'NO EXEC' capta algunas cosas más. Http://stackoverflow.com/questions/3084387/how-can-i-programmatically-check-parse-the-validity-of-a-tsql-statement –

+0

@Martin - gracias por la información –

+0

Según la publicación vinculada por Martin Smith, es posible que también desee para intentar CONFIGURAR FMTONLY. Esto identificará las tablas que faltan, etc. – cbp

5

Sé que la pregunta estaba relacionada con .NET 2.0, pero puede ser interesante para alguien. La validación de consultas ha cambiado ligeramente en las últimas versiones de Microsoft SQL Server. El espacio de nombre es Microsoft.SqlServer.TransactSql.ScriptDom en lugar de Microsoft.Data.Schema.ScriptDom.

¿Dónde encontrar esta biblioteca?

ruta a la biblioteca es %programfiles(x86)%\Microsoft SQL Server\120\SDK\Assemblies Si no puede encontrar esta biblioteca y Microsoft SQL Server está instalado, trate de cambiar de 120 a 110 o 100 y utilizar el analizador correspondiente (o TSql110ParserTSql100Parser respectivamente).

¿Cómo utilizar?

Tengo dos extensiones: la primera extensión comprueba si la cadena de entrada es una consulta SQL válida y la segunda se puede utilizar para obtener errores de análisis.

using Microsoft.SqlServer.TransactSql.ScriptDom; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 

public static class SqlStringExtensions 
{ 
    public static bool IsValidSql(this string str) 
    { 
     return !str.ValidateSql().Any(); 
    } 

    public static IEnumerable<string> ValidateSql(this string str) 
    { 
     if (string.IsNullOrWhiteSpace(str)) 
     { 
      return new[] { "SQL query should be non empty." }; 
     } 
     var parser = new TSql120Parser(false); 
     IList<ParseError> errors; 
     using (var reader = new StringReader(str)) 
     { 
      parser.Parse(reader, out errors); 
     } 
     return errors.Select(err => err.Message); 
    } 
} 

Adicionalmente, puedo comprobar que la consulta SQL de entrada no es nulo o vacío, porque el analizador piensa que una cadena vacía es perfectamente válido (y no juzgo a ella).

¿Cómo puedo probar?

Hay tres pruebas de NUnit que muestran cómo puede usar estas extensiones.

using System.Collections.Generic; 
using System.Linq; 
using NUnit.Framework; 

[TestFixture] 
public class SqlStringExtensionsTests 
{ 
    [Test] 
    public void ValidateSql_InvalidSql_ReturnsErrorMessages() 
    { 
     // this example doesn't contain "," between the field names 
     string invalidSql = "SELECT /*comment*/ " + 
      "CustomerID AS ID CustomerNumber FROM Customers"; 
     IEnumerable<string> results = invalidSql.ValidateSql(); 
     Assert.AreNotEqual(0, results.Count()); 
    } 

    [Test] 
    public void IsValidSql_ValidSql_ReturnsTrue() 
    { 
     string validSql = "SELECT /*comment*/ " + 
      "CustomerID AS ID, CustomerNumber FROM Customers"; 
     bool result = validSql.IsValidSql(); 
     Assert.AreEqual(true, result); 
    } 

    [Test] 
    public void IsValidSql_InvalidSql_ReturnsFalse() 
    { 
     // this example doesn't contain "," between the field names 
     string invalidSql = "SELECT /*comment*/ "+ 
      " CustomerID AS ID CustomerNumber FROM Customers"; 
     bool result = invalidSql.IsValidSql(); 
     Assert.AreEqual(false, result); 
    } 
} 
+0

Hay tres archivos DLL en esa carpeta . –

+0

@ AndersLindén podría describir el problema en detalle? –

+0

Bueno, acabo de elegir una DLL y funcionó. Sin embargo, no es una tarea de ciencia de cohetes elegir la correcta. –

Cuestiones relacionadas