我从excel文件中获取ID和数量列表(数千个ID和相应的金额)。然后我需要检查数据库以查看每个ID是否存在以及是否检查以确保DB中的金额大于或等于excel文件中金额的金额。
问题是运行此select语句超过6000次并返回我需要的值需要很长时间。即使在1/2秒的时间内,所有选择也需要大约一个小时。 (我通常最多不会得到超过5个结果)
有更快的方法吗?
是否有可能以某种方式同时传递所有ID并只进行1次呼叫并获得大量收集?
我尝试过使用SqlDataReaders和SqlDataAdapters但它们似乎差不多(无论哪种方式都太长)
以下是如何工作的一般概念
for (int i = 0; i < ID.Count; i++)
{
SqlCommand cmd = new SqlCommand("select Amount, Client, Pallet from table where ID = @ID and Amount > 0;", sqlCon);
cmd.Parameters.Add("@ID", SqlDbType.VarChar).Value = ID[i];
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dataTable);
da.Dispose();
}
答案 0 :(得分:13)
而不是长in
列表(难以参数化并且有许多关于执行计划的低效率:编译时间,计划重用和计划本身),您可以通过一次性传递所有值表值参数。
有关详细信息,请参阅arrays and lists in SQL Server。
通常,我确保为表类型指定主键,并使用option (recompile)
来获取最合适的执行计划。
答案 1 :(得分:4)
将所有ID组合成一个大的IN
子句,如下所示:
select Amount, Client, Pallet from table where ID in (1,3,5,7,9,11) and Amount > 0;
答案 2 :(得分:2)
“我尝试过使用SqlDataReaders和SqlDataAdapters”
听起来您可能对其他API持开放态度。使用Linq2SQL或Linq2Entities:
var someListIds = new List<int> { 1,5,6,7 }; //imagine you load this from where ever
db.MyTable.Where( mt => someListIds.Contains(mt.ID) );
这在避免潜在的SQL注入漏洞方面是安全的,并且会生成“in”子句。但请注意,someListIds的大小可能太大,以至于生成的SQL查询超出了查询长度的限制,但涉及IN子句的任何其他技术也是如此。您可以通过将列表分成大块来轻松解决这个问题,并且仍然比每个ID的查询要好得多。
答案 3 :(得分:2)
使用它们,您可以将带有值的c#datatable传递给存储过程作为结果集/表,您可以加入并执行一个简单的操作:
SELECT *
FROM YourTable
WHERE NOT EXISTS (SELECT * FORM InputResultSet WHERE YourConditions)
答案 4 :(得分:1)
使用in运算符。您的问题非常常见,并且名称为N+1 performance problem
你从哪里获得身份证?如果它来自另一个查询,则考虑将它们分组为一个。
答案 5 :(得分:1)
不是对您拥有的每个ID执行单独的查询,而是执行一个查询以获取您要检查的每个ID 的数量(或者如果你有太多的ID放在一个查询中,然后将它们批量分成几千个。)
答案 6 :(得分:1)
将数据直接导入SQL Server。使用存储过程输出所需的数据。
如果必须在应用层使用它...使用xml数据类型传递给存储过程。
答案 7 :(得分:1)
您可以将excel文件中的数据作为表格导入SQL Server(使用导入数据向导)。然后,您可以在SQL服务器中执行单个查询,将此表连接到查找表,并在ID字段上加入。这个过程还有一些步骤,但它比将所有ID连接成更长的查询要简洁得多。
我在这里假设对服务器有一定的访问权限,但这是我通常所拥有的访问权限。我还假设这是一次性的任务。如果没有,则可以通过编程方式将数据导入SQL服务器
答案 8 :(得分:1)
IN
子句有限制,因此如果您采用这种方法,请确保批量大小一次用于处理X量的ID,否则您将遇到另一个问题
@Robertharvey已经注意到,如果没有很多ID并且没有发生任何事务,那么只需将所有ID一次性地拉入内存到类似对象的字典中并在那里处理它们。六千个值不是很多,一个选择可以在几秒钟内返回所有这些值。
请记住,如果另一个进程正在更新数据,则您的本地缓存版本可能过时。
答案 9 :(得分:1)
还有另一种处理方法,即制作ID的XML并将其传递给过程。这是程序代码。
IF OBJECT_ID('GetDataFromDatabase') IS NOT NULL
BEGIN
DROP PROCEDURE GetDataFromDatabase
END
GO
--Definition
CREATE PROCEDURE GetDataFromDatabase
@xmlData XML
AS
BEGIN
DECLARE @DocHandle INT
DECLARE @idList Table (id INT)
EXEC SP_XML_PREPAREDOCUMENT @DocHandle OUTPUT, @xmlData;
INSERT INTO @idList (id) SELECT x.id FROM OPENXML(@DocHandle, '//data', 2) WITH ([id] INT) x
EXEC SP_XML_removeDOCUMENT @DocHandle ;
--SELECT * FROM @idList
SELECT t.Amount, t.Client, t.Pallet FROM yourTable t INNER JOIN @idList x ON t.id = x.id and t.Amount > 0;
END
GO
--Uses
EXEC GetDataFromDatabase @xmlData = '<root><data><id>1</id></data><data><id>2</id></data></root>'
您可以在程序中添加任何逻辑。您也可以通过XML传递id,amount。您可以通过XML传递大量的ID。
答案 10 :(得分:0)
您可以选择整个结果集(或加入多个'有限'结果集)并将其全部保存到DataTable
然后您可以直接在数据表上进行选择和更新(如果需要)。然后重新插入新数据...不是超级高效的内存,但在批量工作时通常是非常好(并且只有)解决方案,需要它非常快。
因此,如果您有数千条记录,则可能需要几分钟才能将所有记录填充到DataTable
然后你可以像这样搜索你的表:
string findMatch = "id = value";
DataRow[] rowsFound = dataTable.Select(findMatch);
然后循环foreach (DataRow dr in rowsFound)
答案 11 :(得分:0)
SqlDataAdapter对象太重了。 首先,使用存储过程会更快。 其次,使用组操作,将此传递作为参数访问数据库侧的标识符列表,对这些参数运行查询,并返回处理结果。 它将快速有效,因为所有数据处理逻辑都位于数据库服务器一侧