从用户获取 SQL 查询并 运行 它针对 SQL 服务器
Get SQL query from user and run it against SQL Server
我想用 C# 开发非常小的应用程序,它从用户那里获取一些 SQL 查询并针对指定的 SQL 服务器执行它。
SQL 服务器和数据库必须由用户指定,因此一切都可以改变。
我的问题是用户可以输入各种类型的 SQL 查询,并且它的每个国王都应该以自己的方式 运行。
例如
SELECT * FROM mytable
和
UPDATE mytable
SET city = "NY"
WHERE name = "tom"
不能以同样的方式执行。
我想我需要在我的代码中识别用户查询类型,有什么方法可以识别它或更好的方法来 运行 任何可能的查询吗?
(此答案假定您使用 System.Data.SqlClient
命名空间中可用的 类。)
最简单但并非万无一失的方法是检查第一个单词 - 如果它(不区分大小写)等于 SELECT
,你可以使用 ExecuteReader()
- 如果不是, 您可以使用 ExecuteNonQuery()
.
另一种选择就是ExecuteNonQuery()
。如果您将 SELECT 语句作为 CommandText
传递,这将 return -1。检测到这一点后,您可以调用 ExecuteReader()
。但是,其他查询如 BEGIN TRANSACTION
也会 return -1.
我会简单地给用户2个执行按钮,一个用于非查询,一个用于reader。
如果您信任用户编写 sql 语句,则可以信任用户知道要单击哪个按钮。
不尝试自己分析 sql 语句的原因很简单:
可以写成 exec stp_doesThisProcedureSelectsOrUpdates
之类的东西,所以你根本无法分辨...
您不需要在代码中创建不同的查询类型。 SqlCommand ExecuteReader 方法可用于所有 DML
和 DDL
语句。如果语句 return 没有结果,则 SqlDataReader Read 方法将为 return false。您还需要调用 SqlDataReader.NextResult 方法,直到它 return 对 return 多个语句的结果为假。
var reader = command.ExecuteReader();
do{
while(reader.Read())
{
//process resultset here
}
}while(reader.NextResult());
我会使用 Microsoft.SqlServer.TransactSql.ScriptDom
命名空间 (reference | download #1 - Nuget download #2 - Microsoft® SQL Server® 2014 Transact-SQL ScriptDom) 中的 TSqlPerser.ParseStatementList
方法:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using System.IO;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
TextReader rdr = new StreamReader(new MemoryStream(
Encoding.UTF8.GetBytes(
@"/* comment */
WITH CteUpdate
AS
(
SELECT * FROM Table1
WHERE Col1 = 1
)
UPDATE CteUpdate SET city = ""NY"" WHERE name = ""tom""")
));
TSql110Parser parser = new TSql110Parser(true);
IList<ParseError> errors;
StatementList stmtList = parser.ParseStatementList(rdr, out errors);
// Process errors
foreach(TSqlStatement stmt in stmtList.Statements)
{
Console.WriteLine("Statement type {0}", stmt.GetType());
if (stmt is SelectStatement)
{
//Process SELECT statment
}
else if (stmt is UpdateStatement)
{
//Process UPDATE statment
UpdateStatement stmtUpdate = (UpdateStatement)stmt;
NamedTableReference tabRef = (NamedTableReference)stmtUpdate.UpdateSpecification.Target;
Console.Write(" > UPDATE statement > target object {0}", tabRef.SchemaObject.BaseIdentifier.Value);
}
else //Process other statments
{
throw new NotImplementedException();
}
}
}
}
}
输出:
Statement type
Microsoft.SqlServer.TransactSql.ScriptDom.UpdateStatement > UPDATE
statement > target object CteUpdate
我想用 C# 开发非常小的应用程序,它从用户那里获取一些 SQL 查询并针对指定的 SQL 服务器执行它。
SQL 服务器和数据库必须由用户指定,因此一切都可以改变。 我的问题是用户可以输入各种类型的 SQL 查询,并且它的每个国王都应该以自己的方式 运行。
例如
SELECT * FROM mytable
和
UPDATE mytable
SET city = "NY"
WHERE name = "tom"
不能以同样的方式执行。
我想我需要在我的代码中识别用户查询类型,有什么方法可以识别它或更好的方法来 运行 任何可能的查询吗?
(此答案假定您使用 System.Data.SqlClient
命名空间中可用的 类。)
最简单但并非万无一失的方法是检查第一个单词 - 如果它(不区分大小写)等于 SELECT
,你可以使用 ExecuteReader()
- 如果不是, 您可以使用 ExecuteNonQuery()
.
另一种选择就是ExecuteNonQuery()
。如果您将 SELECT 语句作为 CommandText
传递,这将 return -1。检测到这一点后,您可以调用 ExecuteReader()
。但是,其他查询如 BEGIN TRANSACTION
也会 return -1.
我会简单地给用户2个执行按钮,一个用于非查询,一个用于reader。
如果您信任用户编写 sql 语句,则可以信任用户知道要单击哪个按钮。
不尝试自己分析 sql 语句的原因很简单:
可以写成 exec stp_doesThisProcedureSelectsOrUpdates
之类的东西,所以你根本无法分辨...
您不需要在代码中创建不同的查询类型。 SqlCommand ExecuteReader 方法可用于所有 DML
和 DDL
语句。如果语句 return 没有结果,则 SqlDataReader Read 方法将为 return false。您还需要调用 SqlDataReader.NextResult 方法,直到它 return 对 return 多个语句的结果为假。
var reader = command.ExecuteReader();
do{
while(reader.Read())
{
//process resultset here
}
}while(reader.NextResult());
我会使用 Microsoft.SqlServer.TransactSql.ScriptDom
命名空间 (reference | download #1 - Nuget download #2 - Microsoft® SQL Server® 2014 Transact-SQL ScriptDom) 中的 TSqlPerser.ParseStatementList
方法:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SqlServer.TransactSql.ScriptDom;
using System.IO;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
TextReader rdr = new StreamReader(new MemoryStream(
Encoding.UTF8.GetBytes(
@"/* comment */
WITH CteUpdate
AS
(
SELECT * FROM Table1
WHERE Col1 = 1
)
UPDATE CteUpdate SET city = ""NY"" WHERE name = ""tom""")
));
TSql110Parser parser = new TSql110Parser(true);
IList<ParseError> errors;
StatementList stmtList = parser.ParseStatementList(rdr, out errors);
// Process errors
foreach(TSqlStatement stmt in stmtList.Statements)
{
Console.WriteLine("Statement type {0}", stmt.GetType());
if (stmt is SelectStatement)
{
//Process SELECT statment
}
else if (stmt is UpdateStatement)
{
//Process UPDATE statment
UpdateStatement stmtUpdate = (UpdateStatement)stmt;
NamedTableReference tabRef = (NamedTableReference)stmtUpdate.UpdateSpecification.Target;
Console.Write(" > UPDATE statement > target object {0}", tabRef.SchemaObject.BaseIdentifier.Value);
}
else //Process other statments
{
throw new NotImplementedException();
}
}
}
}
}
输出:
Statement type Microsoft.SqlServer.TransactSql.ScriptDom.UpdateStatement > UPDATE statement > target object CteUpdate