我正在尝试使用EntityFramework确定如何 计算 表中匹配的行。
问题是每行可能有多兆字节的数据(在二进制字段中)。当然SQL会是这样的:
SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1';
我可以加载所有行,然后找到Count with:
var owner = context.MyContainer.Where(t => t.ID == '1');
owner.MyTable.Load();
var count = owner.MyTable.Count();
但这非常低效。有更简单的方法吗?
我的真实数据有点深,我会使用卡车 项 > - 我不希望卡车离开,除非其中至少有一个项。
我的尝试如下所示。我没有得到的部分是CASE_2永远不会访问数据库服务器(MSSQL)。
var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID));
if (truck == null)
return "Invalid Truck ID: " + truckID;
var dlist = from t in ve.Truck
where t.ID == truckID
select t.Driver;
if (dlist.Count() == 0)
return "No Driver for this Truck";
var plist = from t in ve.Truck where t.ID == truckID
from r in t.Pallet select r;
if (plist.Count() == 0)
return "No Pallets are in this Truck";
#if CASE_1
/// This works fine (using 'plist'):
var list1 = from r in plist
from c in r.Case
from i in c.Item
select i;
if (list1.Count() == 0)
return "No Items are in the Truck";
#endif
#if CASE_2
/// This never executes any SQL on the server.
var list2 = from r in truck.Pallet
from c in r.Case
from i in c.Item
select i;
bool ok = (list.Count() > 0);
if (!ok)
return "No Items are in the Truck";
#endif
#if CASE_3
/// Forced loading also works, as stated in the OP...
bool ok = false;
foreach (var pallet in truck.Pallet) {
pallet.Case.Load();
foreach (var kase in pallet.Case) {
kase.Item.Load();
var item = kase.Item.FirstOrDefault();
if (item != null) {
ok = true;
break;
}
}
if (ok) break;
}
if (!ok)
return "No Items are in the Truck";
#endif
CASE_1产生的SQL通过 sp_executesql 传输,但是:
SELECT [Project1].[C1] AS [C1]
FROM ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(cast(1 as bit)) AS [A1]
FROM [dbo].[PalletTruckMap] AS [Extent1]
INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID]
INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID]
WHERE [Extent1].[TruckID] = '....'
) AS [GroupBy1] ) AS [Project1] ON 1 = 1
[我真的没有卡车,司机,托盘,箱子或物品;正如您从SQL中看到的那样,Truck-Pallet和Pallet-Case关系是多对多的 - 尽管我认为这并不重要。我的真实对象是无形的,难以描述,所以我更改了名称。]
答案 0 :(得分:108)
查询语法:
var count = (from o in context.MyContainer
where o.ID == '1'
from t in o.MyTable
select t).Count();
方法语法:
var count = context.MyContainer
.Where(o => o.ID == '1')
.SelectMany(o => o.MyTable)
.Count()
两者都生成相同的SQL查询。
答案 1 :(得分:42)
我想你想要像
这样的东西var count = context.MyTable.Count(t => t.MyContainer.ID == '1');
(编辑以反映评论)
答案 2 :(得分:14)
据我了解,所选答案仍会加载所有相关测试。根据这个msdn博客,有一个更好的方法。
具体地
using (var context = new UnicornsContext())
var princess = context.Princesses.Find(1);
// Count how many unicorns the princess owns
var unicornHaul = context.Entry(princess)
.Collection(p => p.Unicorns)
.Query()
.Count();
}
答案 3 :(得分:9)
好吧,即使SELECT COUNT(*) FROM Table
效率也会相当低,特别是在大型表上,因为SQL Server实际上除了执行全表扫描(群集索引扫描)之外什么也做不了。
有时候,知道数据库中的大概行数就足够了,在这种情况下,这样的语句就足够了:
SELECT
SUM(used_page_count) * 8 AS SizeKB,
SUM(row_count) AS [RowCount],
OBJECT_NAME(OBJECT_ID) AS TableName
FROM
sys.dm_db_partition_stats
WHERE
OBJECT_ID = OBJECT_ID('YourTableNameHere')
AND (index_id = 0 OR index_id = 1)
GROUP BY
OBJECT_ID
这将检查动态管理视图,并在给定特定表的情况下从中提取行数和表大小。它通过总结堆的条目(index_id = 0)或聚集索引(index_id = 1)来实现。
速度快,易于使用,但不保证100%准确或最新。但在许多情况下,这“足够好”(并且减轻了服务器的负担)。
也许那对你也有用吗?当然,要在EF中使用它,您必须将其包装在存储过程中或使用直接的“执行SQL查询”调用。
马克
答案 4 :(得分:9)
这是我的代码:
IQueryable<AuctionRecord> records = db.AuctionRecord;
var count = records.Count();
确保变量定义为IQueryable,然后当您使用Count()方法时,EF将执行类似
的操作select count(*) from ...
否则,如果记录被定义为IEnumerable,则生成的sql将查询整个表并计算返回的行数。
答案 5 :(得分:3)
使用实体上下文的 ExecuteStoreQuery 方法。这样可以避免下载整个结果集并反序列化为对象以进行简单的行计数。
int count;
using (var db = new MyDatabase()){
string sql = "SELECT COUNT(*) FROM MyTable where FkId = {0}";
object[] myParams = {1};
var cntQuery = db.ExecuteStoreQuery<int>(sql, myParams);
count = cntQuery.First<int>();
}
答案 6 :(得分:2)
我认为这应该有用......
var query = from m in context.MyTable
where m.MyContainerId == '1' // or what ever the foreign key name is...
select m;
var count = query.Count();