问题描述
我需要将架构值动态添加到查询中。我试图按照我们通常使用值的方式构建它,但意识到它不能以相同的方式使用模式名称。
这就是我想要做的
sql = "SELECT Name FROM [@dbo].[Members]";
...
command.Parameters.Add("@dbo",sqlDbType.VarChar).Value = "dbo";
sql = $@"SELECT Name FROM [{parameter}].[Members]";
但我想禁止任何类型的 sql 注入,所以想继续使用上述参数化查询。
任何人都可以帮助实现这个想法吗?
TIA
解决方法
据我所知,您只能将参数传递给动态查询,而不是对象名称。您可以执行以下操作:
在看到 Panagiotis 的解决方案后更新。我们可以把存在的部分取出来。我不是 C# 专家,但我们想做类似的事情:
DECLARE @Table NVARCHAR(100);
DECLARE @Schema NVARCHAR(50);
SET @Schema = 'dbo'; --This part will come from C#
SET @Table = 'Tablename'; --This part will come from C#
DECLARE @sql NVARCHAR(MAX);
IF EXISTS(SELECT * FROM sys.tables WHERE name = @Table and schema_id = SCHEMA_ID(@Schema))
BEGIN
SET @sql = N'SELECT TOP 1 * FROM '+ QUOTENAME(@Schema) + '.' + QUOTENAME(@Table) +';';
PRINT @sql;
EXEC sp_executesql @sql;
END
ELSE
RAISERROR('Table or Schema doesn''t exist.',16,1);
在 IF EXISTS
中,您需要将表名和架构名作为参数传递给动态查询。
在实际查询中,您需要将表名和模式名串联起来。
这也将防止 sql 注入,如果表不存在,则会引发错误。
,架构不是参数。查询参数等效于例如 C# 中的函数参数。它们用于传递值。在 SQL 中,表和列等效于 C# 类型和属性。您不能按名称指定它们。 SQL 查询中的架构类似于 C# 中的命名空间。该表相当于一个类型。在 C# 中,仅仅因为 Sales.Record
和 Diagnostics.Record
具有相同的类型名称并不意味着这两种类型可以以相同的方式使用。
这个问题没有解释为什么动态传递架构名称。几乎可以肯定有更简单、更有效和更安全的方法来查询多个模式中的相似表,但解决方案将取决于实际问题。
如果效率不高,有一些技术可用于使此类动态查询安全。 我真的,真的尽量避免将架构视为值。
使用 QUOTENAME
一种选择是在 T-SQL
脚本中使用 QUOTENAME 来构建动态查询。至少这样,如果模式和表名错误,则会抛出语法错误:
sql = @"declare @sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(@dbo) + '.[Members]';
select @sql;
exec sp_executesql @sql;";
...
command.Parameters.Add("@dbo",SqlDbType.NVarChar,100).Value = "dbo";
QUOTENAME
会将 sys].schemas; PRINT ''x''; --
之类的内容转换为 [[sys]].schemas; PRINT 'x'; --]
。这将导致错误:
declare @sql nvarchar(max)= 'select * from [' +quotename('sys].schemas; PRINT ''x''; --')
select @sql
exec sp_executesql @sql;
--------
select * from [[sys]].schemas; PRINT 'x'; --]
Invalid object name '[sys].schemas; PRINT 'x'; --'.
使用此类脚本很容易出现引用错误。这可以提取到存储过程中:
CREATE PROCEDURE GetMemberNameBySchema
@dbo nvarchar(100)
as
declare @sql nvarchar(max)='SELECT Name FROM ' + QUOTENAME(@dbo) + '.[Members]';
exec sp_executesql @sql;
验证架构名称
在构造查询之前查询 sys.schema
以确保架构正确。假设您正在使用 Dapper(因此我不必编写所有 ADO.NET 代码):
var schema="dbo";
var isValid=connection.ExecuteScalar<bool?>(
"select 1 from sys.schema where name=@name",new {name=schema});
//isValid will be null if nothing is found
if(isValid ==true)
{
var names=connection.Query($"SELECT Name FROM [{schema}].[Members]");
...
}
这样做是安全的,因为第一个查询确保架构名称有效。