我想知道一些事情。
我尝试检索2个文件。
一个是注册组2,一个注册组10。
所以该字段是Files.Group。
一个用户注册到组1和组10。
这是我用来检索文件的查询。
SELECT Files.Id, Files.Name, Files.Date, Files.Path, Files.[Group] FROM Files WHERE Files.[Group] = " + param + "ORDER BY Files.Id DESC"
Param是一个获取该组的cookie,创建一个像这样的链| 2。
这实际上不起作用..而且我不知道如何在两个组中传递查询。我应该昏迷吗?比如Files.Group = 2,10?
还是别的什么?传递2个参数?
答案 0 :(得分:5)
我没有完整的结构,因此我创建了以下简化版本:
CREATE TABLE [dbo].[Files]
(
[ID] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[Name] NVARCHAR(64) NOT NULL,
[Group] INT NOT NULL -- Probably have a non-unique index over this.
);
GO
INSERT INTO [dbo].[Files] ([Name], [Group]) VALUES (N'My File 1', 1);
INSERT INTO [dbo].[Files] ([Name], [Group]) VALUES (N'My File 2', 2);
INSERT INTO [dbo].[Files] ([Name], [Group]) VALUES (N'My File 3', 3);
INSERT INTO [dbo].[Files] ([Name], [Group]) VALUES (N'My File 4', 2);
INSERT INTO [dbo].[Files] ([Name], [Group]) VALUES (N'My File 5', 3);
INSERT INTO [dbo].[Files] ([Name], [Group]) VALUES (N'My File 6', 5);
您可以将拆分值插入到临时表中并对其使用WHERE EXISTS
- 可能会产生不错的性能。
-- This would be passed in from C#.
DECLARE @GroupsParam NVARCHAR(64) = N'2|3';
-- This is your SQL command, possibly a SPROC.
DECLARE @GroupsXML XML = N'<split><s>' + REPLACE(@GroupsParam, N'|', N'</s><s>') + '</s></split>';
-- Create an in-memory temp table to hold the temp data.
DECLARE @Groups TABLE
(
[ID] INT PRIMARY KEY
);
-- Insert the records into the temp table.
INSERT INTO @Groups ([ID])
SELECT x.value('.', 'INT')
FROM @GroupsXML.nodes('/split/s') as records(x);
-- Use a WHERE EXISTS; which should have extremely good performance.
SELECT [F].[Name], [F].[Group] FROM [dbo].[Files] AS [F]
WHERE EXISTS (SELECT 1 FROM @Groups AS [G] WHERE [G].[ID] = [F].[Group]);
SQL 2008有一个neat feature,您可以将表作为参数发送到数据库。显然,这只有using SqlCommands correctly(执行参数化SQL语句)才有效,与您的示例不同(将用户创建的值附加到SQL字符串 极 错误练习 - 学习如何使用参数) - 因为你需要传递DataTable
,你可以用一个简单的字符串值。
为了使用它,首先需要创建值类型:
CREATE TYPE [dbo].[IntList] AS TABLE
([Value] INT);
GO
接下来,我们将正确地执行操作并使用存储过程 - 因为这是一个静态查询,并且使用sproc(查询计划缓存)会有一些性能影响。
CREATE PROCEDURE [dbo].[GetFiles]
@Groups [dbo].[IntList] READONLY
AS BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
SELECT [F].[Name], [F].[Group] FROM [dbo].[Files] AS [F]
WHERE EXISTS (SELECT 1 FROM @Groups AS [G] WHERE [G].[Value] = [F].[Group]);
END
GO
接下来我们需要从C#中看到这个,这非常简单,因为我们可以创建一个表来进行调用。
public static void GetFilesByGroups(string groupsQuery)
{
GetFilesByGroups(groupsQuery.Split('|').Select(x => int.Parse(x)));
}
public static void GetFilesByGroups(params int[] groups)
{
GetFilesByGroups((IEnumerable<int>)groups);
}
public static void GetFilesByGroups(IEnumerable<int> groups)
{
// Create the DataTable that will contain our groups values.
var table = new DataTable();
table.Columns.Add("Value", typeof(int));
foreach (var group in groups)
table.Rows.Add(group);
using (var connection = CreateConnection())
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "[dbo].[GetFiles]";
// Add the table like any other parameter.
command.Parameters.AddWithValue("@Groups", table);
using (var reader = command.ExecuteReader())
{
// ...
}
}
}
请记住: 表值参数仅在SQL 2008及更高版本中受支持。
编辑:我想指出dknaack's answer与临时表方法之间的性能可能存在交叉点。对于一小组搜索组,他的意志可能会更快;对于大量搜索组,临时表方法可能会更快。表值参数几乎总是更快。这只是基于我对SQL查询引擎如何工作的了解的理论:临时表可能会进行合并或散列连接,TVP可能希望进行嵌套循环。我还没有完成任何剖析(并没有得到足够的支持以激励我这样做)所以我无法肯定地说。
答案 1 :(得分:3)
您应该使用SqlParameter
来阻止Sql注入。使用IN
语句传入您的组ID的逗号分隔列表。
// value from cookie
string groups = "2,10,99";
// Build where clause and params
List<string> where = new List<string>();
List<SqlParameter> param = new List<SqlParameter>();
foreach(string group in groups.Split(','))
{
int groupId = Int32.Parse(group);
string paramName = string.Format("@Group{0}", groupId);
where.Add(paramName);
param.Add(new SqlParameter(paramName, groupId));
}
// create command
SqlConnection myConnection = new SqlConnection("My ConnectionString");
SqlCommand command = new SqlCommand("SELECT Files.Id, Files.Name, Files.Date, " +
"Files.Path, Files.[Group] " +
"FROM Files " +
"WHERE Files.[Group] in (" + string.Join(",", param) + ")" +
"ORDER BY Files.Id DESC", myConnection);
command.Parameters.AddRange(param.ToArray());
答案 2 :(得分:1)
你可能(取决于你的数据库)正在使用它:
IN (2, 10)
而不是=
运算符。
请注意,使用像这样的字符串连接构造SQL可能会将代码暴露给SQL injection漏洞,并且使用正确参数化的SQL查询通常是更好的做法。但是,在您的情况下,如果您有不确定数量的参数,则在实践中难以实现。
答案 3 :(得分:1)
您需要在cookie中设置Param以创建类似于2,10的链。
然后,您需要使用=
,而不是使用in ()
:
SELECT Files.Id, Files.Name, Files.Date, Files.Path, Files.[Group] FROM Files WHERE Files.[Group] in (" + param + ") ORDER BY Files.Id DESC"
你错了另一件事是错过了param + "ORDER
部分的空格。