如何将参数传递给 C# 中的直通查询?

How can I pass parameters to a pass-through query in c#?

我有一个 C# 程序,它使用来自链接服务器的 OPENQUERY() 到 select。此传递查询接受一个参数。为了防止 SQL 注入,我想以类似于 SqlCommand.Parameters.AddWithValue 的方式传递此参数,但是 OPENQUERY() 不接受变量。

到目前为止,我一直在使用 SqlCommand 将参数传递给针对 SQL 服务器中的表的查询 运行。但是我还需要访问 Oracle 链接服务器。有什么方法可以添加这个参数而不用把它连接成一个字符串吗?

string query = "SELECT * FROM OPENQUERY(linked_server, 
'SELECT * FROM User.Table WHERE col1 = @parameter1 ')";

编辑: 我没有在远程 Oracle 服务器上创建存储过程的权限。因此,对存储过程执行 Sp_executesql 对我来说似乎不是直接的答案。

从性能的角度来看,这不是最佳方法,但您可以在 SQL Server 方面进行过滤:

string query = "SELECT * 
                FROM OPENQUERY(linked_server, 'SELECT * FROM User.Table') s
                WHERE col1 = @parameter1";

编辑:

来自How to pass a variable to a linked server query

When you query a linked server, you frequently perform a pass-through query that uses the OPENQUERY, OPENROWSET, or OPENDATASOURCE statement.

This article provides three examples of how to pass a variable to a linked server query.

To pass a variable to one of the pass-through functions, you must build a dynamic query.

方法一:

Pass Basic Values

When the basic Transact-SQL statement is known, but you have to pass in one or more specific values, use code that is similar to the following sample:

DECLARE @TSQL varchar(8000), @VAR char(2)
SELECT  @VAR = 'CA'
 
SELECT  @TSQL = 'SELECT * FROM OPENQUERY(MyLinkedServer
                  ,''SELECT * FROM pubs.dbo.authors WHERE state = ''''' 
                  + @VAR + ''''''')'
EXEC (@TSQL)

方法二:

Use the Sp_executesql Stored Procedure

To avoid the multi-layered quotes, use code that is similar to the following sample:

DECLARE @VAR char(2)
SELECT  @VAR = 'CA'
EXEC MyLinkedServer.master.dbo.sp_executesql
   N'SELECT * FROM pubs.dbo.authors WHERE state = @state',
   N'@state char(2)',
   @VAR

在你的例子中:

DECLARE @parameter1 <your_type> = ?;

EXEC linked_server.master.dbo.sp_executesql
     @'SELECT * FROM User.Table WHERE col1 = @parameter1 '
    ,N'@parameter1 <your_type>'
    ,@parameter1;

如果你需要在本地做一些其他操作:

DECLARE @parameter1 <your_type> = ?;
CREATE #temp(col_name <type>, ...);

INSERT INTO #temp(col_name)    
EXEC linked_server.master.dbo.sp_executesql
     @'SELECT col_name1,... FROM User.Table WHERE col1 = @parameter1 '
    ,N'@parameter1 <your_type>'
    ,@parameter1;

SELECT *
FROM #temp
-- JOIN any local table (SQL Server's side)
-- WHERE any_condition;

这是创建和执行动态查询的最佳示例。 https://www.codeproject.com/Articles/20815/Building-Dynamic-SQL-In-a-Stored-Procedure。我认为它可以帮助你。

为了能够获得一些 SQL 服务器查询缓存,最好使用 sp_executesql 存储过程。

它的设计喜欢将参数传递给查询,有利于安全(SQL 注入预防)以及当您需要考虑 implied conversions(真正的性能杀手)时的性能。

你可以简单地调用

using (SqlCommand cmd = _con.CreateCommand())
{
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.CommandText = "sp_executesql";
     cmd.Parameters.Add("sel_query", myQuery);
     return cmd.ExecuteReader();
}  

myQuery 变量可以包含任何有效的 TSQL 并且可以根据您的喜好复杂化。只需确保其中包含的内容对于您的服务器以及您要连接的服务器而言是有意且安全的。

在网上搜索可以找到一些动态示例,请查看this example。然而,您可能会退后一步,喝杯咖啡并思考,也许您想将其他服务器 table 定义与您的代码分离。

您可以考虑在您的服务器中生成一个查询链接服务器的视图,然后您只需要查询一个本地对象,在存储过程和函数上也能很好地工作。

解耦还允许您查询您的客户端没有驱动程序的其他数据库。我已经使用这种方法在 DB2、Oracle 等上创建了这样的视图,而无需在客户端系统上安装驱动程序。

有一个 EXEC 调用,特别是对于链接服务器(参见 docs):

EXEC( 'SELECT * FROM User.Table WHERE col1 = ?', '<param>' ) AT linked_server

你可以把所有这些都放在 string query 中,这样你就可以安全地避免 SQL 注入到 SELECT 语句中,尽管你可能会得到 [=11] 的语法错误=]声明。