Как запустить файл .sql с несколькими операциями GO в Visual Studio c # windows form? [Дубликат]

Сначала начнем с некоторых определений:

Интерфейс n. Набор всех сигнатур, определенных операциями объекта, называется интерфейсом к объекту

Тип n. Конкретный интерфейс

. Простым примером интерфейса, как определено выше, были бы все методы объекта PDO, такие как query(), commit(), close() и т. д., в целом, не отдельно. Эти методы, т. Е. Его интерфейс, определяют полный набор сообщений, запросы, которые могут быть отправлены объекту.

Тип, определенный выше, является конкретным интерфейсом. Я буду использовать созданный интерфейс формы, чтобы продемонстрировать: draw(), getArea(), getPerimeter() и т. Д.

Если объект относится к типу базы данных, мы подразумеваем, что он принимает сообщения / запросы интерфейс базы данных, query(), commit() и т. д. Объекты могут быть разных типов. У вас может быть объект базы данных типа формы, если он реализует свой интерфейс, и в этом случае это будет подтип.

Многие объекты могут иметь много разных интерфейсов / типов и реализовывать этот интерфейс иначе. Это позволяет нам заменять объекты, позволяя нам выбирать, какой из них использовать. Также известен как полиморфизм.

Клиент будет знать интерфейс, а не реализацию.

Таким образом, по сути программирование на интерфейс потребует создания некоторого типа абстрактного класса, такого как Shape, только с указанным интерфейсом, т.е. draw(), getCoordinates(), getArea() и т. д. И затем различные конкретные классы реализуют такие интерфейсы, как класс Circle, Square class, Triangle class. Следовательно, программа для интерфейса не является реализацией.

79
задан bluish 20 August 2015 в 13:53
поделиться

17 ответов

Использовать объекты управления SQL Server (SMO), которые понимают разделители GO. См. Мой блог здесь: http://weblogs.asp.net/jongalloway/Handling-_2200_GO_2200_-Separators-in-SQL-Scripts-2D00-the-easy-way

Пример кода:

public static void Main()    
{        
  string scriptDirectory = "c:\\temp\\sqltest\\";
  string sqlConnectionString = "Integrated Security=SSPI;" +
  "Persist Security Info=True;Initial Catalog=Northwind;Data Source=(local)";
  DirectoryInfo di = new DirectoryInfo(scriptDirectory);
  FileInfo[] rgFiles = di.GetFiles("*.sql");
  foreach (FileInfo fi in rgFiles)
  {
        FileInfo fileInfo = new FileInfo(fi.FullName);
        string script = fileInfo.OpenText().ReadToEnd();
        using (SqlConnection connection = new SqlConnection(sqlConnectionString))
        {
            Server server = new Server(new ServerConnection(connection));
            server.ConnectionContext.ExecuteNonQuery(script);
        }
   }
}

Если это не сработает для вас, см. Библиотеку Phil Haack, которая обрабатывает это: http://haacked.com/archive/2007/11/04/ а-библиотека-для-выполнения, SQL-скрипты-с-гоу-сепараторов-and.aspx

95
ответ дан Aristos 21 August 2018 в 13:16
поделиться
  • 1
    Как это можно интегрировать с транзакцией? Код генерирует исключение InvalidOperationException при создании ServerConnection с SqlConnection, на котором есть ожидающая транзакция. – benPearce 25 May 2009 в 22:51
  • 2
    Это решение работает, я просто хочу добавить, что если вы хотите использовать транзакции с объектом TransactionScope, вам просто нужно заручиться соединением с текущей внешней транзакцией. Проверьте мой ответ здесь: stackoverflow.com/a/18322938/1268570 – Jupaol 19 August 2013 в 21:57
  • 3
    отлично работает, но можем ли мы использовать SqlConnection.InfoMessage), чтобы увидеть результат в приложении C # или сохранить результат в файле txt, просто чтобы узнать, был ли сценарий успешно выполнен, потому что недавно с помощью sqlcmd, когда я выполнил файл сценария 150 мб на удаленном хосте, через 55 минут с этой ошибкой были сделаны некоторые строки, TCP Provider: An existing connection was forcibly closed by the remote host., communication link failure. , никакие из произведенных строк не могут быть известны, но меня беспокоят сообщения об ошибках при запуске созданного файла сценария базы данных. – stom 6 December 2015 в 09:42
  • 4
    Эти решения привели к сбою вашего кода, когда некоторые из SQL-Dll не установлены на компьютере. .NET использует некоторые DLL, встроенные в Windows. Отсутствие некоторых пакетов функций SQL (включая объекты Managment Objects) может помешать ошибкам что-то вроде «Microsoft.SqlServer.SqlClrProvider.dll» не найдено. Исправлена ​​ошибка (это непростая задача) следующей ошибкой будет «Microsoft.SqlServer.BathParser.dll» e.t.c Найти другое решение, обеспечивающее гибкость вашего приложения. – Alexandr Sargsyan 27 May 2016 в 06:35

Я выполнил это сегодня, загрузив мой SQL из текстового файла в одну строку. Затем я использовал функцию Split для разделения строки на отдельные команды, которые затем были отправлены на сервер по отдельности. Simples:)

Только что поняли, что вам нужно разделить на\nGO на случай, если буквы GO появятся в любом из ваших имен таблиц и т. Д. Угадайте, что мне повезло!

2
ответ дан Andy Dove 21 August 2018 в 13:16
поделиться

Если вы не хотите использовать SMO (что лучше, чем решение ниже, но я хочу дать альтернативу ...), вы можете разделить свой запрос с помощью этой функции.

:

  • Комментарий (например, --GO или / * GO * /)
  • Работает только на новой строке, как и в SSMS (пример / * test / * GO работает и выбирает 1 как go not
  • Строковое доказательство (пример print 'no go')
    private List<string> SplitScriptGo(string script)
    {
        var result = new List<string>();
        int pos1 = 0;
        int pos2 = 0;
        bool whiteSpace = true;
        bool emptyLine = true;
        bool inStr = false;
        bool inComment1 = false;
        bool inComment2 = false;
    
        while (true)
        {
            while (pos2 < script.Length && Char.IsWhiteSpace(script[pos2]))
            {
                if (script[pos2] == '\r' || script[pos2] == '\n')
                {
                    emptyLine = true;
                    inComment1 = false;
                }
    
                pos2++;
            }
    
            if (pos2 == script.Length)
                break;
    
            bool min2 = (pos2 + 1) < script.Length;
            bool min3 = (pos2 + 2) < script.Length;
    
            if (!inStr && !inComment2 && min2 && script.Substring(pos2, 2) == "--")
                inComment1 = true;
    
            if (!inStr && !inComment1 && min2 && script.Substring(pos2, 2) == "/*")
                inComment2 = true;
    
            if (!inComment1 && !inComment2 && script[pos2] == '\'')
                inStr = !inStr;
    
            if (!inStr && !inComment1 && !inComment2 && emptyLine
                && (min2 && script.Substring(pos2, 2).ToLower() == "go")
                && (!min3 || char.IsWhiteSpace(script[pos2 + 2]) || script.Substring(pos2 + 2, 2) == "--" || script.Substring(pos2 + 2, 2) == "/*"))
            {
                if (!whiteSpace)
                    result.Add(script.Substring(pos1, pos2 - pos1));
    
                whiteSpace = true;
                emptyLine = false;
                pos2 += 2;
                pos1 = pos2;
            }
            else
            {
                pos2++;
                whiteSpace = false;
    
                if (!inComment2)
                    emptyLine = false;
            }
    
            if (!inStr && inComment2 && pos2 > 1 && script.Substring(pos2 - 2, 2) == "*/")
                inComment2 = false;
        }
    
        if (!whiteSpace)
            result.Add(script.Substring(pos1));
    
        return result;
    }
    
2
ответ дан Bigjim 21 August 2018 в 13:16
поделиться

Это то, с чем я столкнулся, чтобы решить мою непосредственную проблему.

private void ExecuteBatchNonQuery(string sql, SqlConnection conn) {
    string sqlBatch = string.Empty;
    SqlCommand cmd = new SqlCommand(string.Empty, conn);
    conn.Open();
    sql += "\nGO";   // make sure last batch is executed.
    try {
        foreach (string line in sql.Split(new string[2] { "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries)) {
            if (line.ToUpperInvariant().Trim() == "GO") {
                cmd.CommandText = sqlBatch;
                cmd.ExecuteNonQuery();
                sqlBatch = string.Empty;
            } else {
                sqlBatch += line + "\n";
            }
        }            
    } finally {
        conn.Close();
    }
}

Он требует, чтобы команды GO были в их собственной строке и не обнаруживали комментарии блоков, поэтому такие вещи будут получить split и вызвать ошибку:

ExecuteBatchNonQuery(@"
    /*
    GO
    */", conn);
30
ответ дан Blorgbeard 21 August 2018 в 13:16
поделиться
  • 1
    Приятно, что я могу легко адаптировать его к SqlCe, если это необходимо - в другом коде используются классы и команды соединения Sql. – Blue Toque 1 August 2012 в 00:22
  • 2
    Я хочу запустить этот код с помощью SQL-скрипта с несколькими хранимыми процедурами внутри, но я немного смущен, где он читает SQL? Когда вы ссылаетесь на «последнюю партию», вы имеете в виду код SQL? И если да, как бы вы определили последнюю партию, и что, если бы я хотел запускать все партии не только последним? Слишком много вопросов, которые я знаю, но спасибо, если у вас есть время ответить. – user1676874 18 December 2013 в 17:54
  • 3
    Вы передаете SQL функции в виде строки: string sql - это весь скрипт. Когда я ссылаюсь на «пакет», я имею в виду кусок кода SQL между двумя «GO», заявления. Код добавляет GO в конец скрипта, чтобы код внутри foreach не пропускал последнюю партию, если вы не закончили свой скрипт с помощью GO. Таким образом, написанный код выполнит все SQL. – Blorgbeard 18 December 2013 в 21:33
  • 4
    Я создал метод расширения: внутренний статический класс SqlCommandHelper {внутренний статический void ExecuteBatchNonQuery (этот SqlCommand cmd, строка sql) – Rob Sedgwick 15 May 2014 в 16:05
  • 5
    Если вы хотите быть более эффективным, вы можете использовать StringBuilder sqlBatch. – Lii 19 March 2015 в 11:32

Вы можете использовать SQL Management Objects для выполнения этого. Это те же объекты, которые Management Studio использует для выполнения запросов. Я считаю, что Server.ConnectionContext.ExecuteNonQuery() выполнит то, что вам нужно.

9
ответ дан bluish 21 August 2018 в 13:16
поделиться

Если вы не хотите устанавливать объекты SMO, вы можете использовать инструмент gplex (см. этот ответ )

3
ответ дан Community 21 August 2018 в 13:16
поделиться

Слишком сложно:)

Создать массив строк str [], заменив GO на «, @»:

            string[] str ={
                @"
USE master;
",@"


CREATE DATABASE " +con_str_initdir+ @";
",@"
-- Verify the database files and sizes
--SELECT name, size, size*1.0/128 AS [Size in MBs] 
--SELECT name 
--FROM sys.master_files
--WHERE name = N'" + con_str_initdir + @"';
--GO

USE " + con_str_initdir + @";
",@"

SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Customers]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Customers](
    [CustomerID] [int] IDENTITY(1,1) NOT NULL,
    [CustomerName] [nvarchar](50) NULL,
 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 
(
    [CustomerID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"



SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[GOODS]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[GOODS](
    [GoodsID] [int] IDENTITY(1,1) NOT NULL,
    [GoodsName] [nvarchar](50) NOT NULL,
    [GoodsPrice] [float] NOT NULL,
 CONSTRAINT [PK_GOODS] PRIMARY KEY CLUSTERED 
(
    [GoodsID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"
SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Orders]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Orders](
    [OrderID] [int] IDENTITY(1,1) NOT NULL,
    [CustomerID] [int] NOT NULL,
    [Date] [smalldatetime] NOT NULL,
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [OrderID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"
SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[OrderDetails]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[OrderDetails](
    [OrderID] [int] NOT NULL,
    [GoodsID] [int] NOT NULL,
    [Qty] [int] NOT NULL,
    [Price] [float] NOT NULL,
 CONSTRAINT [PK_OrderDetails] PRIMARY KEY CLUSTERED 
(
    [OrderID] ASC,
    [GoodsID] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
",@"

SET ANSI_NULLS ON
",@"
SET QUOTED_IDENTIFIER ON
",@"
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[InsertCustomers]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
create PROCEDURE [dbo].[InsertCustomers]
 @CustomerName nvarchar(50),
 @Identity int OUT
AS
INSERT INTO Customers (CustomerName) VALUES(@CustomerName)
SET @Identity = SCOPE_IDENTITY()

' 
END
",@"
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_Orders_Customers]') AND parent_object_id = OBJECT_ID(N'[dbo].[Orders]'))
ALTER TABLE [dbo].[Orders]  WITH CHECK ADD  CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([CustomerID])
REFERENCES [dbo].[Customers] ([CustomerID])
ON UPDATE CASCADE
",@"
ALTER TABLE [dbo].[Orders] CHECK CONSTRAINT [FK_Orders_Customers]
",@"
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_OrderDetails_GOODS]') AND parent_object_id = OBJECT_ID(N'[dbo].[OrderDetails]'))
ALTER TABLE [dbo].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [FK_OrderDetails_GOODS] FOREIGN KEY([GoodsID])
REFERENCES [dbo].[GOODS] ([GoodsID])
ON UPDATE CASCADE
",@"
ALTER TABLE [dbo].[OrderDetails] CHECK CONSTRAINT [FK_OrderDetails_GOODS]
",@"
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_OrderDetails_Orders]') AND parent_object_id = OBJECT_ID(N'[dbo].[OrderDetails]'))
ALTER TABLE [dbo].[OrderDetails]  WITH CHECK ADD  CONSTRAINT [FK_OrderDetails_Orders] FOREIGN KEY([OrderID])
REFERENCES [dbo].[Orders] ([OrderID])
ON UPDATE CASCADE
ON DELETE CASCADE
",@"
ALTER TABLE [dbo].[OrderDetails] CHECK CONSTRAINT [FK_OrderDetails_Orders]


                "};


            for(int i =0; i<str.Length;i++)     
            {
                myCommand.CommandText=str[i];
                try
                {
                myCommand.ExecuteNonQuery();
                }
                catch (SystemException ee)
                {
                    MessageBox.Show("Error   "+ee.ToString());
                }

            }

Вот и все, наслаждайтесь.

-3
ответ дан grv 21 August 2018 в 13:16
поделиться
  • 1
    Есть ли шанс, что вы сможете объяснить, как это работает? – Born2BeMild 24 May 2012 в 14:22
  • 2
    Это работает только в том случае, если во время компиляции у вас есть сценарий SQL. – Blorgbeard 1 August 2012 в 00:35

Я также столкнулся с одной и той же проблемой, и я не мог найти другого пути, кроме разделения одной операции SQL в отдельных файлах, а затем выполнить все из них в последовательности.

Очевидно, проблема заключается не в списках команд DML, они могут быть выполнены без GO между ними; другая история с DDL (создание, изменение, падение ...)

2
ответ дан ila 21 August 2018 в 13:16
поделиться

Если вы не хотите идти по маршруту SMO, вы можете искать и заменять «GO» на «;» и запрос, как и вы. Обратите внимание, что soly возвращает последний набор результатов.

2
ответ дан jason saldo 21 August 2018 в 13:16
поделиться
  • 1
    Они выполняют ExecuteNonQuery. Это намного проще. – DaveMorganTexas 1 October 2013 в 12:56
  • 2
    Использование & quot; GO & quot; позволит вам снова определить те же самые переменные в следующей команде в пакете. Размещение точки с запятой не будет делать этого. – DDRider62 8 January 2016 в 17:50

У меня была та же проблема в java, и я решил ее с немного логики и регулярного выражения. Я считаю, что та же логика может быть применена. Сначала я читал из файла slq в память. Затем я применяю следующую логику. Это в значительной степени то, что было сказано ранее, но я считаю, что использование связанного слова regex безопаснее, чем ожидание новой строки char.

String pattern = "\\bGO\\b|\\bgo\\b";

String[] splitedSql = sql.split(pattern);
for (String chunk : splitedSql) {
  getJdbcTemplate().update(chunk);
}

Это в основном разбивает строку sql на массив строк sql. Регулярное выражение в основном обнаруживает полные слова «go» либо в нижнем регистре, либо в верхнем регистре. Затем вы выполняете различные запросы последовательно.

0
ответ дан jbrunodomingues 21 August 2018 в 13:16
поделиться
  • 1
    Осторожно: как вы разделите это? insert into books values ('1478355824', 'An Introduction To Programming in Go (paperback)', 9.00) – Blorgbeard 12 June 2014 в 09:51
  • 2
    Хорошая точка :-) В моей ситуации не было вставок данных. Просто создавал таблицы, хранимые процедуры и функции. Слово связанное было более полезным в моем конкретном случае, потому что оно также позаботилось «идти» в последней строке. – jbrunodomingues 10 July 2014 в 10:27

Ключевое слово сепаратора «GO» фактически используется самой средой управления SQL, так что он знает, где прекратить партии, которые он отправляет на сервер, и не передается на сервер SQL. Вы даже можете изменить ключевое слово в Management Studio, если хотите.

6
ответ дан John Hubert 21 August 2018 в 13:16
поделиться

Я ударил по этой же проблеме и в итоге просто решил ее с помощью простой замены строки, заменив слово GO на запятую (;)

. Кажется, что все работает нормально, выполняя скрипты, комментарии к строкам, комментарии блоков и команды GO

public static bool ExecuteExternalScript(string filePath)
{
    using (StreamReader file = new StreamReader(filePath))
    using (SqlConnection conn = new SqlConnection(dbConnStr))
    {
        StringBuilder sql = new StringBuilder();

        string line;
        while ((line = file.ReadLine()) != null)
        {
            // replace GO with semi-colon
            if (line == "GO")
                sql.Append(";");
            // remove inline comments
            else if (line.IndexOf("--") > -1)
                sql.AppendFormat(" {0} ", line.Split(new string[] { "--" }, StringSplitOptions.None)[0]);
            // just the line as it is
            else
                sql.AppendFormat(" {0} ", line);
        }
        conn.Open();

        SqlCommand cmd = new SqlCommand(sql.ToString(), conn);
        cmd.ExecuteNonQuery();
    }

    return true;
}
0
ответ дан Morvael 21 August 2018 в 13:16
поделиться
  • 1
    Он не будет работать для команд DDL, которые должны быть в их собственной партии. Например. create / alter table et al. – Blorgbeard 3 August 2015 в 10:32
  • 2
    Кроме того, вы, кажется, удаляете комментарии без всякой причины. Например, если бы были разбиты строки, содержащие --. – Blorgbeard 3 August 2015 в 10:34
  • 3
    Привет Blorgbeard - SQL Server 2012, похоже, обрабатывает заявления DDL в порядке. Сценарии, которые я использовал, должны были позволить мне перестроить всю структуру базы данных, очистить текущую структуру, создать таблицы, добавить индексы и т. Д. Я думал; также закончил партию? – Morvael 3 August 2015 в 14:36
  • 4
    Кроме того, удаление комментариев было связано с тем, что это приведет к созданию отдельной строки SQL, любой SQL после того, как комментарий будет прокомментирован, но я считаю, что если была содержащаяся строка - это не комментарий. – Morvael 3 August 2015 в 14:38
  • 5
    Ах, хорошо, просто посмотрел: «CREATE DEFAULT, CREATE FUNCTION, CREATE PROCEDURE, CREATE RULE, CREATE SCHEMA, CREATE TRIGGER и CREATE VIEW нельзя комбинировать с другими операторами в пакете. Оператор CREATE должен запустить пакет. Все остальные утверждения, которые следуют в этой партии, будут интерпретироваться как часть определения первого оператора CREATE. Таблица не может быть изменена, а затем новые столбцы, на которые ссылается одна и та же партия. & Quot; – Blorgbeard 3 August 2015 в 19:51

Основано на решении Blorgbeard.

foreach (var sqlBatch in commandText.Split(new[] { "GO" }, StringSplitOptions.RemoveEmptyEntries))
{
   sqlCommand.CommandText = sqlBatch;
   sqlCommand.ExecuteNonQuery();
}
4
ответ дан Ryan Penfold 21 August 2018 в 13:16
поделиться
  • 1
    new [] {«GO», «Go», «go», } – Andrew Veriga 12 August 2015 в 18:29
  • 2
    new [] {«GO», «Go», «go», «gO», } – Brandon Ward 14 February 2017 в 02:21
  • 3
    Работает до тех пор, пока у вас нет другого использования для двух букв в вашем коде, таких как GOTO-Statement или комментарии. – Patrik Eckebrecht 6 March 2017 в 13:39

используйте следующий метод для разбиения строки и выполнения партии с помощью пакета

using System;
using System.IO;
using System.Text.RegularExpressions;
namespace RegExTrial
{
    class Program
    {
        static void Main(string[] args)
        {
            string sql = String.Empty;
            string path=@"D:\temp\sample.sql";
            using (StreamReader reader = new StreamReader(path)) {
                sql = reader.ReadToEnd();
            }            
            //Select any GO (ignore case) that starts with at least 
            //one white space such as tab, space,new line, verticle tab etc
            string pattern="[\\s](?i)GO(?-i)";

            Regex matcher = new Regex(pattern, RegexOptions.Compiled);
            int start = 0;
            int end = 0;
            Match batch=matcher.Match(sql);
            while (batch.Success) {
                end = batch.Index;
                string batchQuery = sql.Substring(start, end - start).Trim();
                //execute the batch
                ExecuteBatch(batchQuery);
                start = end + batch.Length;
                batch = matcher.Match(sql,start);
            }

        }

        private static void ExecuteBatch(string command)
        { 
            //execute your query here
        }

    }
}
1
ответ дан Sriwantha Attanayake 21 August 2018 в 13:16
поделиться

Если вы не хотите использовать SMO, например, потому что вам нужно быть кросс-платформенным, вы также можете использовать класс ScriptSplitter из SubText.

Здесь реализация в C # & amp; VB.NET

Использование:

    string strSQL = @"
SELECT * FROM INFORMATION_SCHEMA.columns
GO
SELECT * FROM INFORMATION_SCHEMA.views
";

    foreach(string Script in new Subtext.Scripting.ScriptSplitter(strSQL ))
    {
        Console.WriteLine(Script);
    }

Если у вас возникли проблемы с многострочными комментариями в стиле c, удалите комментарии с помощью регулярного выражения:

static string RemoveCstyleComments(string strInput)
{
    string strPattern = @"/[*][\w\d\s]+[*]/";
    //strPattern = @"/\*.*?\*/"; // Doesn't work
    //strPattern = "/\\*.*?\\*/"; // Doesn't work
    //strPattern = @"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/ "; // Doesn't work
    //strPattern = @"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/ "; // Doesn't work

    // http://stackoverflow.com/questions/462843/improving-fixing-a-regex-for-c-style-block-comments
    strPattern = @"/\*(?>(?:(?>[^*]+)|\*(?!/))*)\*/";  // Works !

    string strOutput = System.Text.RegularExpressions.Regex.Replace(strInput, strPattern, string.Empty, System.Text.RegularExpressions.RegexOptions.Multiline);
    Console.WriteLine(strOutput);
    return strOutput;
} // End Function RemoveCstyleComments

Удаление однострочные комментарии находятся здесь:

https://stackoverflow.com/questions/9842991/regex-to-remove-single-line-sql-comments
3
ответ дан Stefan Steiger 21 August 2018 в 13:16
поделиться
  • 1
    этот класс рассматривает случай /* Go */? – cMinor 29 December 2015 в 22:05
  • 2
    @cMinor: Не в сплиттере, но вы можете удалить многострочные комментарии с регулярным выражением перед разбиением. – Stefan Steiger 7 December 2016 в 17:11

Чтобы избежать третьих сторон, регулярных выражений, накладных расходов на память и быстрой работы с большими сценариями, я создал собственный анализатор, основанный на потоках. Он

  • проверяет синтаксис до того, как
  • может распознавать комментарии с помощью - или / ** /
    -- some commented text
     /*
    drop table Users;
    GO
       */
    
  • может распознавать строковые литералы с символом 'или'
    set @s =
        'create table foo(...);
        GO
        create index ...';
    
  • сохраняет форматирование LF и CR
  • сохраняет блок комментариев в телах объектов (хранимые процедуры, представления и т. д.)
  • и другие конструкции, такие как
          gO -- commented text
    

Как использовать

    try
    {
        using (SqlConnection connection = new SqlConnection("Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=DATABASE-NAME;Data Source=SERVER-NAME"))
        {
            connection.Open();

            int rowsAffected = SqlStatementReader.ExecuteSqlFile(
                "C:\\target-sql-script.sql",
                connection,
                // Don't forget to use the correct file encoding!!!
                Encoding.Default,
                // Indefinitely (sec)
                0
            );
        }
    }
    // implement your handlers
    catch (SqlStatementReader.SqlBadSyntaxException) { }
    catch (SqlException) { }
    catch (Exception) { }

Считыватель сценариев SQL на основе потоков

class SqlStatementReader
{
    public class SqlBadSyntaxException : Exception
    {
        public SqlBadSyntaxException(string description) : base(description) { }
        public SqlBadSyntaxException(string description, int line) : base(OnBase(description, line, null)) { }
        public SqlBadSyntaxException(string description, int line, string filePath) : base(OnBase(description, line, filePath)) { }
        private static string OnBase(string description, int line, string filePath)
        {
            if (filePath == null)
                return string.Format("Line: {0}. {1}", line, description);
            else
                return string.Format("File: {0}\r\nLine: {1}. {2}", filePath, line, description);
        }
    }

    enum SqlScriptChunkTypes
    {
        InstructionOrUnquotedIdentifier = 0,
        BracketIdentifier = 1,
        QuotIdentifierOrLiteral = 2,
        DblQuotIdentifierOrLiteral = 3,
        CommentLine = 4,
        CommentMultiline = 5,
    }

    StreamReader _sr = null;
    string _filePath = null;
    int _lineStart = 1;
    int _lineEnd = 1;
    bool _isNextChar = false;
    char _nextChar = '\0';

    public SqlStatementReader(StreamReader sr)
    {
        if (sr == null)
            throw new ArgumentNullException("StreamReader can't be null.");

        if (sr.BaseStream is FileStream)
            _filePath = ((FileStream)sr.BaseStream).Name;

        _sr = sr;
    }

    public SqlStatementReader(StreamReader sr, string filePath)
    {
        if (sr == null)
            throw new ArgumentNullException("StreamReader can't be null.");

        _sr = sr;
        _filePath = filePath;
    }

    public int LineStart { get { return _lineStart; } }
    public int LineEnd { get { return _lineEnd == 1 ? _lineEnd : _lineEnd - 1; } }

    public void LightSyntaxCheck()
    {
        while (ReadStatementInternal(true) != null) ;
    }

    public string ReadStatement()
    {
        for (string s = ReadStatementInternal(false); s != null; s = ReadStatementInternal(false))
        {
            // skip empty
            for (int i = 0; i < s.Length; i++)
            {
                switch (s[i])
                {
                    case ' ': continue;
                    case '\t': continue;
                    case '\r': continue;
                    case '\n': continue;
                    default:
                        return s;
                }
            }
        }
        return null;
    }

    string ReadStatementInternal(bool syntaxCheck)
    {
        if (_isNextChar == false && _sr.EndOfStream)
            return null;

        StringBuilder allLines = new StringBuilder();
        StringBuilder line = new StringBuilder();
        SqlScriptChunkTypes nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
        SqlScriptChunkTypes currentChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
        char ch = '\0';
        int lineCounter = 0;
        int nextLine = 0;
        int currentLine = 0;
        bool nextCharHandled = false;
        bool foundGO;
        int go = 1;

        while (ReadChar(out ch))
        {
            if (nextCharHandled == false)
            {
                currentChunk = nextChunk;
                currentLine = nextLine;

                switch (currentChunk)
                {
                    case SqlScriptChunkTypes.InstructionOrUnquotedIdentifier:

                        if (ch == '[')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.BracketIdentifier;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '"')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.DblQuotIdentifierOrLiteral;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '\'')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.QuotIdentifierOrLiteral;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '-' && (_isNextChar && _nextChar == '-'))
                        {
                            nextCharHandled = true;
                            currentChunk = nextChunk = SqlScriptChunkTypes.CommentLine;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '/' && (_isNextChar && _nextChar == '*'))
                        {
                            nextCharHandled = true;
                            currentChunk = nextChunk = SqlScriptChunkTypes.CommentMultiline;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == ']')
                        {
                            throw new SqlBadSyntaxException("Incorrect syntax near ']'.", _lineEnd + lineCounter, _filePath);
                        }
                        else if (ch == '*' && (_isNextChar && _nextChar == '/'))
                        {
                            throw new SqlBadSyntaxException("Incorrect syntax near '*'.", _lineEnd + lineCounter, _filePath);
                        }
                        break;

                    case SqlScriptChunkTypes.CommentLine:

                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                        {
                            nextCharHandled = true;
                            currentChunk = nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            currentLine = nextLine = lineCounter;
                        }
                        else if (ch == '\n' || ch == '\r')
                        {
                            currentChunk = nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            currentLine = nextLine = lineCounter;
                        }
                        break;

                    case SqlScriptChunkTypes.CommentMultiline:

                        if (ch == '*' && (_isNextChar && _nextChar == '/'))
                        {
                            nextCharHandled = true;
                            nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            nextLine = lineCounter;
                        }
                        else if (ch == '/' && (_isNextChar && _nextChar == '*'))
                        {
                            throw new SqlBadSyntaxException("Missing end comment mark '*/'.", _lineEnd + currentLine, _filePath);
                        }
                        break;

                    case SqlScriptChunkTypes.BracketIdentifier:

                        if (ch == ']')
                        {
                            nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                            nextLine = lineCounter;
                        }
                        break;

                    case SqlScriptChunkTypes.DblQuotIdentifierOrLiteral:

                        if (ch == '"')
                        {
                            if (_isNextChar && _nextChar == '"')
                            {
                                nextCharHandled = true;
                            }
                            else
                            {
                                nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                                nextLine = lineCounter;
                            }
                        }
                        break;

                    case SqlScriptChunkTypes.QuotIdentifierOrLiteral:

                        if (ch == '\'')
                        {
                            if (_isNextChar && _nextChar == '\'')
                            {
                                nextCharHandled = true;
                            }
                            else
                            {
                                nextChunk = SqlScriptChunkTypes.InstructionOrUnquotedIdentifier;
                                nextLine = lineCounter;
                            }
                        }
                        break;
                }
            }
            else
                nextCharHandled = false;

            foundGO = false;
            if (currentChunk == SqlScriptChunkTypes.InstructionOrUnquotedIdentifier || go >= 5 || (go == 4 && currentChunk == SqlScriptChunkTypes.CommentLine))
            {
                // go = 0 - break, 1 - begin of the string, 2 - spaces after begin of the string, 3 - G or g, 4 - O or o, 5 - spaces after GO, 6 - line comment after valid GO
                switch (go)
                {
                    case 0:
                        if (ch == '\r' || ch == '\n')
                            go = 1;
                        break;
                    case 1:
                        if (ch == ' ' || ch == '\t')
                            go = 2;
                        else if (ch == 'G' || ch == 'g')
                            go = 3;
                        else if (ch != '\n' && ch != '\r')
                            go = 0;
                        break;
                    case 2:
                        if (ch == 'G' || ch == 'g')
                            go = 3;
                        else if (ch == '\n' || ch == '\r')
                            go = 1;
                        else if (ch != ' ' && ch != '\t')
                            go = 0;
                        break;
                    case 3:
                        if (ch == 'O' || ch == 'o')
                            go = 4;
                        else if (ch == '\n' || ch == '\r')
                            go = 1;
                        else
                            go = 0;
                        break;
                    case 4:
                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                            go = 5;
                        else if (ch == '\n' || ch == '\r')
                            foundGO = true;
                        else if (ch == ' ' || ch == '\t')
                            go = 5;
                        else if (ch == '-' && (_isNextChar && _nextChar == '-'))
                            go = 6;
                        else
                            go = 0;
                        break;
                    case 5:
                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                            go = 5;
                        else if (ch == '\n' || ch == '\r')
                            foundGO = true;
                        else if (ch == '-' && (_isNextChar && _nextChar == '-'))
                            go = 6;
                        else if (ch != ' ' && ch != '\t')
                            throw new SqlBadSyntaxException("Incorrect syntax was encountered while parsing go.", _lineEnd + lineCounter, _filePath);
                        break;
                    case 6:
                        if (ch == '\r' && (_isNextChar && _nextChar == '\n'))
                            go = 6;
                        else if (ch == '\n' || ch == '\r')
                            foundGO = true;
                        break;
                    default:
                        go = 0;
                        break;
                }
            }
            else
                go = 0;

            if (foundGO)
            {
                if (ch == '\r' || ch == '\n')
                {
                    ++lineCounter;
                }
                // clear GO
                string s = line.Append(ch).ToString();
                for (int i = 0; i < s.Length; i++)
                {
                    switch (s[i])
                    {
                        case ' ': continue;
                        case '\t': continue;
                        case '\r': continue;
                        case '\n': continue;
                        default:
                            _lineStart = _lineEnd;
                            _lineEnd += lineCounter;
                            return allLines.Append(s.Substring(0, i)).ToString();
                    }
                }
                return string.Empty;
            }

            // accumulate by string
            if (ch == '\r' && (_isNextChar == false || _nextChar != '\n'))
            {
                ++lineCounter;
                if (syntaxCheck == false)
                    allLines.Append(line.Append('\r').ToString());
                line.Clear();
            }
            else if (ch == '\n')
            {
                ++lineCounter;
                if (syntaxCheck == false)
                    allLines.Append(line.Append('\n').ToString());
                line.Clear();
            }
            else
            {
                if (syntaxCheck == false)
                    line.Append(ch);
            }
        }

        // this is the end of the stream, return it without GO, if GO exists
        switch (currentChunk)
        {
            case SqlScriptChunkTypes.InstructionOrUnquotedIdentifier:
            case SqlScriptChunkTypes.CommentLine:
                break;
            case SqlScriptChunkTypes.CommentMultiline:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Missing end comment mark '*/'.", _lineEnd + currentLine, _filePath);
                break;
            case SqlScriptChunkTypes.BracketIdentifier:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Unclosed quotation mark [.", _lineEnd + currentLine, _filePath);
                break;
            case SqlScriptChunkTypes.DblQuotIdentifierOrLiteral:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Unclosed quotation mark \".", _lineEnd + currentLine, _filePath);
                break;
            case SqlScriptChunkTypes.QuotIdentifierOrLiteral:
                if (nextChunk != SqlScriptChunkTypes.InstructionOrUnquotedIdentifier)
                    throw new SqlBadSyntaxException("Unclosed quotation mark '.", _lineEnd + currentLine, _filePath);
                break;
        }

        if (go >= 4)
        {
            string s = line.ToString();
            for (int i = 0; i < s.Length; i++)
            {
                switch (s[i])
                {
                    case ' ': continue;
                    case '\t': continue;
                    case '\r': continue;
                    case '\n': continue;
                    default:
                        _lineStart = _lineEnd;
                        _lineEnd += lineCounter + 1;
                        return allLines.Append(s.Substring(0, i)).ToString();
                }
            }
        }

        _lineStart = _lineEnd;
        _lineEnd += lineCounter + 1;
        return allLines.Append(line.ToString()).ToString();
    }

    bool ReadChar(out char ch)
    {
        if (_isNextChar)
        {
            ch = _nextChar;
            if (_sr.EndOfStream)
                _isNextChar = false;
            else
                _nextChar = Convert.ToChar(_sr.Read());
            return true;
        }
        else if (_sr.EndOfStream == false)
        {
            ch = Convert.ToChar(_sr.Read());
            if (_sr.EndOfStream == false)
            {
                _isNextChar = true;
                _nextChar = Convert.ToChar(_sr.Read());
            }
            return true;
        }
        else
        {
            ch = '\0';
            return false;
        }
    }

    public static int ExecuteSqlFile(string filePath, SqlConnection connection, Encoding fileEncoding, int commandTimeout)
    {
        int rowsAffected = 0;
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            // Simple syntax check (you can comment out these two lines below)
            new SqlStatementReader(new StreamReader(fs, fileEncoding)).LightSyntaxCheck();
            fs.Seek(0L, SeekOrigin.Begin);

            // Read statements without GO
            SqlStatementReader rd = new SqlStatementReader(new StreamReader(fs, fileEncoding));
            string stmt;
            while ((stmt = rd.ReadStatement()) != null)
            {
                using (SqlCommand cmd = connection.CreateCommand())
                {
                    cmd.CommandText = stmt;
                    cmd.CommandTimeout = commandTimeout;
                    int i = cmd.ExecuteNonQuery();
                    if (i > 0)
                        rowsAffected += i;
                }
            }
        }
        return rowsAffected;
    }
}
0
ответ дан Yargo 21 August 2018 в 13:16
поделиться
0
ответ дан Filip Cordas 1 November 2018 в 07:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: