从HUGE数据库表中读取

时间:2011-07-25 13:00:18

标签: c# asp.net asp.net-mvc linq asp.net-mvc-3

我正在使用LINQ来访问我的数据库表。有些表可能有(数十万)记录。

使用如下语句从表中读取:

var records = db.Logs;

会使应用程序非常慢。它会一次加载所有记录。

我需要使用多个条件过滤结果,例如:

if (UserID != null)
{
    records = records.Where(r => r.User == UserID);
}

if (UserIP != null)
{
    records = records.Where(r => r.IP   == UserIP);
}

问题是我第一次从表中读取会给我带来所有记录,并会使应用程序变慢。

有没有办法在LINQ语句中创建条件(if,switch),就像我们以前的SQL语句一样?

这是多久以来创建的逻辑:

string sql = "SELECT * FROM Log WHERE 1=1";
if (UserID != null)
{
    sql += " AND User = '" + UserID + "'";
}

if (UserIP != null)
{
    sql += " AND IP = '" + UserIP + "'";
}

sqlCmd.query(sql);

8 个答案:

答案 0 :(得分:6)

实际上是这样的作业:

  var records = db.Logs;

不立即执行查询。它只准备稍后使用的数据结构。查询的执行仅在代码需要数据时发生,并且任何where子句都集成在查询中,从而阻止它返回整个表。

完全可以做这样的事情:

  var records = db.Logs;

  if (filter1) records = records.Where(r => r.Field1 == condition1);
  if (filter2) records = records.Where(r => r.Field2 == condition2);

这将以执行一个查询结束,其中包含(种类)动态where语句。

但是你应该定义正确的索引。

答案 1 :(得分:2)

我认为你错了。对db.Logs的调用应返回IQueryable - 这意味着在需要检索数据之前不会执行查询。例如,您访问Log类的属性或将您的集合转换为列表.ToList()

答案 2 :(得分:2)

什么是db.Logs?不是IEnumerable<T>IQueryable<T>吗?通常在Linq2SQL中,在您调用.ToArray.ToList之前不会执行查询,因此您可以先构建查询树。

答案 3 :(得分:1)

  

问题是我第一次从表中读取会给我带来所有记录,并会使应用程序变慢。

var records = db.Logs

不从表中读取。除了创建查询之外,您还没有做任何事情(它类似于创建SQL命令文本而不将命令文本发送到数据库执行)。打开允许您查看发送到数据库的SQL命令的功能,您将看到此行代码不会向数据库发送任何内容。事实上,这一行也没有:

records = records.Where(r => r.User == UserID);

这只是修改名为records的查询(您应该称之为recordsQuery)以在User上添加条件。直到您遍历查询,它才会实际发送到数据库执行。所以要么

records.ToList();

foreach(var record in records) {
    // something something
}

或许多其他执行查询的方式。

  

这是多久以来创建的逻辑:

我希望不是。你好注射攻击!

  

某些表可能有(数十万)记录。

成千上万的人没有。

答案 4 :(得分:1)

最好使用Paging,让你的数据做你想做的任何事情,然后再获得另一页。

答案 5 :(得分:0)

试试这个:

List<Records> records;

if (UserID != null)
{
    records = db.Logs.Where(r => r.User == UserID).ToList();
}
else
{
    records = db.Logs.ToList();
}

答案 6 :(得分:0)

假设您正在使用SQL Server,那么数十万行远非庞大。

回到主题,如果你传递IQueryable<T>,那么每次枚举时都会执行SQL。如果您的旧代码是正确的并且您的新代码很慢,这很可能是您的问题。您可以通过调用ToList(),将其转换为IEnumerable<T>并将所有数据放入内存来避免这种情况。

最后,您可以通过调用.Skip(PageSize * PageIndex).Take(PageSize)来分页数据。

答案 7 :(得分:-1)

如果使用lambda表达式,则在使用它之前不会执行查询,例如转换为列表或枚举所有数据。

var filtered = (from l in db.Logs
where l => l.User == UserID
select l).ToList();