2010-07-18 24 views
19

如何在使用.net 2.0和c#執行它們之前驗證sql腳本?驗證SQL腳本的代碼

如果sql無效,我想返回錯誤行。

+0

相關(但沒有C#角)http://stackoverflow.com/questions/3084387/how-can-i-programmatically-check-parse-the-validity-of-a-tsql-statement – 2010-07-18 16:03:34

+0

可能重複的[SQL遷移工具](http://stackoverflow.com/questions/3272894/sql-migration-tool) – 2010-07-18 16:06:04

+0

@ p.campbell然後將其標記爲重複。 – 2016-02-10 22:56:35

回答

39

如果要創建一個工具,允許用戶輸入手動一些SQL代碼,你要驗證的代碼中使用C#代碼輸入之前執行的SQL服務器上,您可以創建一個方法像這樣:

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; 
     } 
} 

由於2018年和新的數據庫版本,這可能是較新的版本:

using Microsoft.SqlServer.TransactSql.ScriptDom; 

(使用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有趣...不知道TSql100Parser類 – 2010-07-25 16:41:08

+0

除了從終結者電影中看到某種東西之外,還有+1,因爲我還沒有聽說過這個。這是直接鏈接到MSDN的未來問題查看器 - https://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.tsqlparser%28v=vs.100%29.aspx – Tommy 2016-02-10 22:59:51

+2

有一個nuget包,他找不到(或不可能引用本地)ScriptDom dll的:https://www.nuget.org/packages/Microsoft.SqlServer.TransactSql.ScriptDom/ – 2016-05-11 14:41:39

0

「有效」SQL意味着什麼?語法或結果?

驗證語法的唯一方法是在SQL Server中執行SQL。您是否考慮過在事務中運行SQL,然後在最後進行回滾?

Begin Transaction 

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

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

... 

Rollback 

MSDN Documentation回滾

+0

我該怎麼做?你能寫例子嗎?謝謝。 – 2010-07-18 15:15:38

+0

@Tamifist:使用TransactionScope並從不調用Transaction.Complete。這裏有很多關於TransactionScope的例子,但是要記住,你必須首先創建你的TransactionScope,並且裏面的SqlConnection。 – 2010-07-18 15:43:35

15

SSMS有這樣的一種方式。

如果您使用SQL事件探查器,您會看到它執行SET PARSEONLY ON,然後SQL和SET PARSEONLY OFF以及任何錯誤都會上升而不編譯或執行查詢。

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

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; 

NOEXEC

+0

我覺得'NO EXEC'會捕獲更多的東西。http://stackoverflow.com/questions/3084387/how-can-i-program-check-parse-the-validity-of-a-tsql-statement – 2010-07-18 16:07:32

+0

@Martin - 感謝您的信息 – 2010-07-18 16:11:22

+0

根據Martin Smith鏈接的帖子,您可能還想要嘗試SET FMTONLY ON。這將識別丟失的表格等等。 – cbp 2012-06-27 06:29:48

5

我知道這個問題是關於.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,嘗試從120改變110100並使用相應的解析器(分別爲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); 
    } 
} 

Additionaly,我檢查輸入的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); 
    } 
} 
+0

該文件夾中有三個DLL 。 – 2016-02-23 07:18:42

+0

@AndersLindén請你詳細描述一下這個問題嗎? – 2016-02-23 11:07:57

+0

嗯,我只是選擇了一個DLL,它工作。然而,選擇正確的火箭科學任務並非是一項任務。 – 2016-02-23 14:30:18