用于验证SQL脚本的代码

Vla*_*huk 22 .net c# sql sql-server

如何在使用.net 2.0和c#执行它们之前验证sql脚本?

如果sql无效,我想返回错误行.

Mos*_*azi 42

如果您正在创建一个允许用户手动输入一些sql代码的工具,并且您希望在sql server上执行之前验证使用C#代码输入的代码,则可以创建如下方法:

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;
        }
}
Run Code Online (Sandbox Code Playgroud)

截至2018年和新的数据库版本,这可能是更新的版本:

using Microsoft.SqlServer.TransactSql.ScriptDom;
Run Code Online (Sandbox Code Playgroud)

(使用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;
}
Run Code Online (Sandbox Code Playgroud)

  • 有一个nuget包找不到(或者不太可能引用本地)ScriptDom dll:https://www.nuget.org/packages/Microsoft.SqlServer.TransactSql.ScriptDom/ (3认同)

Chr*_*ver 17

SSMS有办法做到这一点.

如果您使用SQL事件探查器,您将看到它执行SET PARSEONLY ON,然后SQL,然后SET PARSEONLY OFF在不编译或执行查询的情况下提高任何错误.

SET PARSEONLY ON;
SELECT * FROM Table; --Query To Parse
SET PARSEONLY OFF; 
Run Code Online (Sandbox Code Playgroud)

PARSEONLY

我从未尝试过使用c#,但我认为它没有理由不起作用,毕竟它适用于SSMS.

正如马丁史密斯在评论中指出的那样,你可以使用 SET NOEXEC ON

MSDN对以下两个命令说了以下内容.

当SET NOEXEC为ON时,SQL Server编译每批Transact-SQL语句但不执行它们.当SET NOEXEC为OFF时,所有批次都在编译后执行.

当SET PARSEONLY为ON时,SQL Server仅解析该语句.当SET PARSEONLY为OFF时,SQL Server编译并执行该语句.

这表明NOEXEC将编译查询的地方PARSEONLY不会.所以NOEXEC可能会发现PARSEONLY没有的错误.用法是一样的.

SET NOEXEC ON;
SELECT * FROM Table; --Query To Parse
SET NOEXEC OFF; 
Run Code Online (Sandbox Code Playgroud)

NOEXEC


Ser*_*hyk 6

我知道这个问题是关于.NET 2.0的,但对某些人来说这可能很有趣.在最新版本的Microsoft SQL Server中,查询验证略有变化.名称空间Microsoft.SqlServer.TransactSql.ScriptDom代替Microsoft.Data.Schema.ScriptDom.

哪里可以找到这个图书馆?

库的路径是%programfiles(x86)%\Microsoft SQL Server\120\SDK\Assemblies 如果找不到此库并安装了Microsoft SQL Server,请尝试从更改120110100使用相应的解析器(TSql110ParserTSql100Parser分别).

如何使用?

我有两个扩展:第一个扩展检查输入字符串是否是有效的SQL查询,第二个扩展可用于从解析中获取错误.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,我检查输入的SQL查询是不是空或空,因为解析器认为空字符串是完全有效的(我不判断它).

怎么测试?

有三个NUnit测试,显示如何使用此扩展.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)