Pertanyaan SqlCommand () ExecuteNonQuery () memotong teks perintah


Saya sedang membangun utilitas penyebaran db kustom, saya perlu membaca file teks yang berisi skrip sql dan menjalankannya terhadap database.

Hal yang cukup mudah, sejauh ini sangat bagus.

Namun saya telah menemukan halangan, isi file yang dibaca berhasil dan seluruhnya, tetapi setelah diteruskan ke SqlCommand dan kemudian dieksekusi dengan SqlCommand.ExecuteNonQuery hanya sebagian dari skrip yang dijalankan.

Saya memecat Profiler dan menegaskan bahwa kode saya tidak melewatkan semua skrip.

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

16
2018-03-16 17:15


asal


Jawaban:


Yap, semua orang menyentuh masalah ini saat pertama kali mereka mengirim konten file skrip SQL ke database.

GO bukan perintah T-SQL. Ini adalah penanda akhir-batch yang diakui oleh semua alat SQL interaktif Microsoft (Management Studio, isql, osql). Untuk mengatasinya, Anda harus menulis parser Anda sendiri untuk memecah setiap blok teks dalam file tersebut GO pernyataan dan memberi mereka ke database sebagai perintah terpisah.

Bagaimana Anda mengimplementasikan parser terserah Anda. Ini bisa sederhana (baca di setiap baris pada satu waktu, mendeteksi garis yang terdiri dari apa-apa kecuali GO dan spasi) atau kompleks (menandai semua pernyataan dan bekerja apakah a GO adalah pernyataan asli atau sedikit teks di dalam string atau komentar multi-baris).

Secara pribadi saya memilih opsi pertama. Ini menangani 99% dari semua file SQL yang pernah Anda temui tanpa rewel. Jika Anda ingin pergi ke seluruh babi dan menulis sebuah tokeniser, saya yakin banyak orang telah melakukannya, hanya Google untuk itu.

Contoh:

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

35
2018-03-16 20:12



Saya menemukan kode di bawah ini saat mencari jawaban untuk masalah ini:

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

Kelebihan: Singkat dan sederhana untuk dipahami dan bekerja dengan sempurna untuk kebutuhan saya.

Cons: Ini kurang efisien daripada solusi berbasis Stream dan sensitif huruf (yaitu "GO" bukan "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();
}

5
2017-11-18 23:06



ExecuteNonQuery di SMO bekerja dengan batch:

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


2
2018-01-12 01:24



Jawaban berdasarkan komentar di bawah posting asli:

GO adalah penanda untuk Manajemen Studio / osql / isql. Ini memberitahu untuk mengirim batch perintah ke SQL Server. Dalam utilitas Anda, Anda harus membagi data input menggunakan GO sebagai pemisah dan mengirim setiap elemen secara individual (tanpa perintah GO)


1
2018-03-16 20:00



Ini yang kami gunakan :)

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
}

0
2017-07-22 09:26



Saya akhirnya menulis implementasi dari StringReader untuk melakukan ini.

Ini menangani:

  1. Melewati GO yang terkandung dalam komentar dash dash
  2. Melewatkan GO yang terkandung dalam komentar slash star
  3. Melewati GO yang terkandung dalam literal (yaitu tanda kutip tunggal)
  4. Melewati GO yang terkandung dalam nama kolom dll.

Oleh karena itu hanya akan mendeteksi kata kunci GO ketika digunakan sebagai seperator batch. Ini berarti membagi teks SQL dengan benar.

Ini juga menangani jika Anda telah menambahkan terminator sql (titik koma) ke kata GO

Anda dapat menemukan kode untuk itu sini:

Anda menggunakannya seperti ini:

using (var reader = new SqlCommandReader(scriptContents))
       {
            var commands = new List<string>();
            reader.ReadAllCommands(c => commands.Add(c));
            // commands now contains each seperated sql batch.
        }

0
2018-02-26 21:51