C# 如何从C准确地处理SQL的批处理分隔符#
对于,我想添加对批处理分隔符的支持 例如,如果用户键入: select 'GO' go select 1 as go Go select 100 选择“开始”开始选择1作为开始 去 选择100 我想返回三个结果集 很明显,我需要某种解析器,我希望这是一个已解决的问题,我可以插入它。(我不想编写完整的T-SQL解析器)C# 如何从C准确地处理SQL的批处理分隔符#,c#,sql,tsql,parsing,C#,Sql,Tsql,Parsing,对于,我想添加对批处理分隔符的支持 例如,如果用户键入: select 'GO' go select 1 as go Go select 100 选择“开始”开始选择1作为开始 去 选择100 我想返回三个结果集 很明显,我需要某种解析器,我希望这是一个已解决的问题,我可以插入它。(我不想编写完整的T-SQL解析器) 什么组件/演示代码可以实现将此批次拆分为3个部分?我不知道现有的解决方案(尽管我同意可能有一个)。我只想指出,您可能不需要编写完整的t-SQL解析器:您真正需要找到的只是引号之
什么组件/演示代码可以实现将此批次拆分为3个部分?我不知道现有的解决方案(尽管我同意可能有一个)。我只想指出,您可能不需要编写完整的t-SQL解析器:您真正需要找到的只是引号之外的单词“go”。也就是说,查找
GO
,并在过程中跟踪开盘和收盘报价。如果您找到一个匹配项,并且它不是在开始报价之后(在匹配的结束报价之前),那么它就是批处理分隔符。不用编写任何你称之为“解析器”的东西就可以很容易地做到这一点。在上面的例子中,你不能在换行符上拆分,测试每一行是否以“go”开头,然后在上面拆分脚本吗
在重读了几遍之后,这是一个非常丑陋的问题。查看脚本中的第一行,实际上没有命令分隔符(分号或换行符)。我不认为你有太多的选择,但实际分析整个事情
但是,在这条线的某个地方,无论如何都必须对其进行解析,对吗?也许您可以在内部或使用现有的解析器来实现这一点。根据您拥有的访问权限,您可以:
- 更改现有解析器的代码以理解“go”命令以执行并返回它所拥有的内容,然后再次运行
- 复制一份现有的解析代码,修改它以理解“go”命令,去掉解释器部分,然后只使用它分割块并馈送到真正的解析器
如果您的用户不能遵守这样的规则,他们真的应该从事编写SQL查询这一复杂得多的任务吗?我正在寻找相同问题的解决方案,但没有找到任何合适的解决方案(在我的情况下,使用SMO是不可接受的)。所以,我必须编写自己的解析器。这是:
static IEnumerable<string> ParseSqlBatch(Stream s)
{
if (s == null)
throw new ArgumentNullException();
StringBuilder sbSqlStatement = new StringBuilder();
Stack<string> state = new Stack<string>();
StreamReader sr = new StreamReader(s);
//initially search for "GO" or open tag of strings ('), comments (--, /*) or identifiers ([)
string pattern = @"(?>(?<=^\s*)go(?=\s*(--.*)?$)|''(?!')|(?<!')'|(?<!\[)\[|--(?=.*)?|/\*)";
//if open tag found search for close tag, then continue search
string patternCloseString = @"(?>''|'(?!'))";
string patternCloseIdentifier = @"(?>\]\]|\](?!\]))";
string patternComments = @"(?>\*/|/\*)";
Regex rx = new Regex(pattern, RegexOptions.IgnoreCase);
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
int ix = 0;
bool bBreak = false;
while (ix < line.Length && !bBreak)
{
Match m = rx.Match(line, ix);
if (!m.Success)
{
sbSqlStatement.Append(line.Substring(ix));
break;
}
int ix2 = m.Index;
string word = m.Value;
sbSqlStatement.Append(line.Substring(ix, ix2 - ix));
if (state.Count == 0)
{
if (string.Compare(word, "GO", true) == 0)
{
if (sbSqlStatement.Length > 0)
{
yield return sbSqlStatement.ToString();
sbSqlStatement = new StringBuilder();
break;
}
}
else
{
switch (word)
{
case "'":
rx = new Regex(patternCloseString);
break;
case "[":
rx = new Regex(patternCloseIdentifier);
break;
case "/*":
rx = new Regex(patternComments);
break;
case "--":
sbSqlStatement.Append(line.Substring(ix2));
bBreak = true;
continue;
}
if (word != "''")
state.Push(word);
}
}
else
{
string st = state.Peek();
switch (st)
{
case "'":
if (st == word)
state.Pop();
break;
case "[":
if (word == "]")
state.Pop();
break;
case "/*":
if (word == "*/")
state.Pop();
else if (word == "/*")
state.Push(word);
break;
}
if (state.Count == 0)
rx = new Regex(pattern, RegexOptions.IgnoreCase);
}
ix = ix2 + word.Length;
sbSqlStatement.Append(word);
}
sbSqlStatement.AppendLine();
}
if (sbSqlStatement.Length > 0)
yield return sbSqlStatement.ToString();
}
我希望它能帮助某些人。您可以非常轻松地将当前数据库更改为开放式SqlConnection:
connection.ChangeDatabase("YourDB");
例如:
private static void ConctDatabase(string connectionString)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
MessageBox.Show("Database: {0}", conn.Database);
conn.ChangeDatabase("Northwind");
MessageBox.Show("Database: {0}", conn.Database);
}
}
yerp这可能是一种工作,但它会变得棘手,例如:选择1作为go是有效的sql it管理studiojeez。。。这一款售价为12k注:微软提供了一款TSql100Parser,但。。。就我的一生而言,我不知道如何为它提供批处理标识符。。。我倾向于只做一个琐碎但非常有限的解决方案。这会处理带引号的标识符/双引号的字符串吗?@ArinTaylor,对于双引号的字符串-是(如果它不能处理语法正确的东西,请告诉我),对于带引号的标识符-我想,不会。它不会处理像
创建表“GO”这样的情况
。我总是使用[name]
-样式标识符,而从不使用“name”
-样式,而且代码不是为它设计的。但它也可以很容易地修改以支持引用的标识符。只需在代码中添加“
”,类似于对用于字符串的“
所做的操作。from,“Transact-SQL语句不能与GO命令占用同一行。但是,行可以包含注释。”所以这已经(几乎)正确了。我关心的是相反的情况:选择“\n go\n”;
(不能在注释中添加换行符)。
private static void ConctDatabase(string connectionString)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
MessageBox.Show("Database: {0}", conn.Database);
conn.ChangeDatabase("Northwind");
MessageBox.Show("Database: {0}", conn.Database);
}
}