有点难过。我面前的任务是采用存储在字符串中的SQL查询(假设它现在是一个有效的查询);存储"选择列表"在字符串数组中,每个数组元素与选择列表项相关联;最后存储" from子句"在它自己的字符串变量中。我觉得这个练习是我自己编写的SQL查询解析器,但我不知道SQL Server如何解析查询。任何有助于资源的方向都会非常受欢迎。
我已经能够使用蛮力方法解决问题,我将执行以下操作:
1 - 通过启动循环查找from子句,该循环查找单词"的后续实例。从"直到索引位置后面的所有内容都可以使用" select *"选择列表
private string GetFromClause(string selectstatement,string connectionString)
{
string teststatement = selectstatement;
int startindex = GetFirstFromIndex(teststatement);
while (startindex != -1)
{
teststatement=teststatement.Substring(startindex);
if (DoesSelectWork(string.Format("select * {0}", teststatement)
, connectionString))
return teststatement;
else
startindex = GetNextFromIndex(teststatement);
}
throw new ReportException("Could not find From Clause");
}
2-删除现在找到的" from子句"从传递的查询;使用.split(',')将剩余的字符串放在数组中。现在遍历数组并使用找到的" from子句"测试每个数组元素。如果测试通过,我们有一个有效的选择列表项;如果不是,我们想要将此元素与下一个数组元素组合,并继续这样做直到测试通过(处理将逗号引入选择列表语法的转换语句等)
private string[] GetSelectFields(string query,
string fromclause,
string connectionString)
{
int index = query.IndexOf(fromclause);
string fromless = (index < 0)
? query
: query.Remove(index, fromclause.Length);
fromless = fromless.Substring(fromless.IndexOf("SELECT") + "SELECT".Length);
List<string> finalselect = new List<string>();
string[] selectFields = fromless.Split(',');
index = 0;
string currentString = string.Empty;
while (index<selectFields.Length)
{
currentString += selectFields[index];
if (DoesSelectWork(string.Format("select {0} {1}"
, currentString, fromclause), connectionString))
{
finalselect.Add(currentString);
currentString = string.Empty;
}
else {
currentString += ",";
}
index++;
}
return finalselect.ToArray();
}
答案 0 :(得分:2)
作为一般规则,您无法使用简单的搜索方法解析SQL,规则过于复杂。你需要一个完整的词法分析器和语法。但是,自SQL Server 2012起,您就拥有了Transact-SQL语言服务选项,这就是Visual Studio用于解析T-SQL而不使用后端服务器的工具。您可以通过C#来利用它
Microsoft.SqlServer.Management.SqlParser.Parser.Parse()
答案 1 :(得分:2)
如果要在不使用数据库的情况下验证SQL语法,TSql100Parser
类将适用于这种情况。
免责声明,此帖子借用Code to validate SQL Scripts
借来的代码虽然非常简单易用。如果它返回null,则解析它时没有错误。
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;
}
}