16

カスタム db 展開ユーティリティを構築しています。SQL スクリプトを含むテキスト ファイルを読み取り、データベースに対して実行する必要があります。

かなり簡単なもので、これまでのところとても良いです。

ただし、問題が発生しました。ファイルの内容は正常に完全に読み取られますが、SqlCommand に渡されてから SqlCommand.ExecuteNonQuery で実行されると、スクリプトの一部のみが実行されます。

Profiler を起動して、コードがすべてのスクリプトをパスしていないことを確認しました。

    private void ExecuteScript(string cmd, SqlConnection sqlConn, SqlTransaction trans)
    {

        SqlCommand sqlCmd = new SqlCommand(cmd, sqlConn, trans);
        sqlCmd.CommandType = CommandType.Text;
        sqlCmd.CommandTimeout = 9000000; // for testing
        sqlCmd.ExecuteNonQuery();

    }

    // I call it like this, readDMLScript contains 543 lines of T-SQL
    string readDMLScript = ReadFile(dmlFile);
    ExecuteScript(readDMLScript, sqlConn, trans);
4

6 に答える 6

35

そうです、SQL スクリプト ファイルの内容をデータベースに初めて送信するときに、誰もがこの障害にぶつかります。

GOは T-SQL コマンドではありません。これは、すべての Microsoft 対話型 SQL ツール (Management Studio、isql、osql) によって認識されるバッチ終了マーカーです。これを処理するには、独自のパーサーを作成して、ファイル内のGOステートメント間のテキストのすべてのブロックを分割し、それらを個別のコマンドとしてデータベースに供給する必要があります。

パーサーをどのように実装するかはあなた次第です。単純な場合 (一度に各行を読み取り、空白のみで構成される行を検出するGO) もあれば、複雑な場合もあります (すべてのステートメントをトークン化し、aGOが本物のステートメントなのか、文字列内のテキストまたは複数のテキストであるかを判断します)。行コメント)。

個人的には最初の選択肢を選びました。大騒ぎせずに遭遇する可能性が高いすべての SQL ファイルの 99% を処理します。独り占めしてトークンナイザーを作成したい場合は、Google だけで既に多くの人が作成していると思います。

例:

using(var reader = new SqlBatchReader(new StreamReader(dmlFile))) {
    string batch;
    while((batch = reader.ReadBatch()) != null) {
        var cmd = new SqlCommand(batch, conn, trans) { CommandType = CommandType.Text };
        cmd.ExecuteNonQuery();
    }
}

class SqlBatchReader : IDisposable {
    private TextReader _reader;
    public SqlBatchReader(TextReader reader) {
        _reader = reader;
    }
    /// <summary>
    /// Return the next command batch in the file, or null if end-of-file reached.
    /// </summary>
    public string ReadBatch() {
        // TODO: Implement your parsing logic here.
    }
}
于 2010-03-16T20:12:37.237 に答える
5

この問題に対する答えを探しているときに、次のコードを見つけました。

http://blogs.msdn.com/b/onoj/archive/2008/02/26/incorrect-syntax-near-go-sqlcommand-executenonquery.aspx

長所: 短くて理解しやすく、私のニーズにぴったり合っていました。

短所: Stream ベースのソリューションよりも効率が悪く、大文字と小文字が区別されます (つまり、"go" ではなく "GO")。

string[] commands = sql.Split(new string[]{"GO\r\n", "GO ", "GO\t"}, StringSplitOptions.RemoveEmptyEntries );
foreach (string c in commands)
{
    var command = new SqlCommand(c, masterConnection);
    command.ExecuteNonQuery();
}
于 2013-11-18T23:06:22.257 に答える
2

SMO の ExecuteNonQuery はバッチで動作します。

http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.database.executenonquery.aspx

于 2012-01-12T01:24:43.517 に答える
1

元の投稿の下のコメントに基づく回答:

GO は、Management Studio / osql / isql のマーカーです。コマンドのバッチを SQL Server に送信するように指示します。ユーティリティでは、GO を区切り文字として使用して入力データを分割し、各要素を個別に (GO コマンドを使用せずに) 送信する必要があります。

于 2010-03-16T20:00:16.513 に答える
0

これが私たちが使用するものです:)

public static class ExtensionMethodsSqlCommand
{
    #region Public

    private static bool IsGo(string psCommandLine)
    {
        if (psCommandLine == null)
            return false;
        psCommandLine = psCommandLine.Trim();
        if (string.Compare(psCommandLine, "GO", StringComparison.OrdinalIgnoreCase) == 0)
            return true;
        if (psCommandLine.StartsWith("GO", StringComparison.OrdinalIgnoreCase))
        {
            psCommandLine = (psCommandLine + "--").Substring(2).Trim();
            if (psCommandLine.StartsWith("--"))
                return true;
        }
        return false;
    }

    [System.Diagnostics.DebuggerHidden]
    public static void ExecuteNonQueryWithGos(this SqlCommand poSqlCommand)
    {
        string sCommandLong = poSqlCommand.CommandText;
        using (StringReader oStringReader = new StringReader(sCommandLong))
        {
            string sCommandLine;
            string sCommandShort = string.Empty;
            while ((sCommandLine = oStringReader.ReadLine()) != null)
                if (ExtensionMethodsSqlCommand.IsGo(sCommandLine))
                {
                    if (sCommandShort.IsNullOrWhiteSpace() == false)
                    {
                        if ((poSqlCommand.Connection.State & ConnectionState.Open) == 0)
                            poSqlCommand.Connection.Open();
                        using (SqlCommand oSqlCommand = new SqlCommand(sCommandShort, poSqlCommand.Connection))
                            oSqlCommand.ExecuteNonQuery();
                    }
                    sCommandShort = string.Empty;
                }
                else
                    sCommandShort += sCommandLine + "\r\n";
        }
    }

    #endregion Public
}
于 2013-07-22T09:26:51.233 に答える