我有两个相关的表,结构如下:
'患者':
{ Id = 1, Surname = Smith998 }
...
{ Id = 1000, Surname = Smith1000 }
,第二个是'接待':
{ PatientId = 1, ReceptionStart = 3/3/2017 1:14:00 AM }
{ PatientId = 1, ReceptionStart = 1/7/2016 1:14:00 AM }
...
{ PatientId = 1000, ReceptionStart = 1/23/2017 1:14:00 AM }
这些表不是来自数据库,而是使用以下示例代码生成的:
var rand = new Random();
var receptions = Enumerable.Range(1, 1000).SelectMany(pid => Enumerable.Range(1, rand.Next(0, 10)).Select(rid => new { PatientId = pid, ReceptionStart = DateTime.Now.AddDays(-rand.Next(1, 500)) })).ToList();
var patients = Enumerable.Range(1, 1000).Select(pid => new { Id = pid, Surname = string.Format("Smith{0}", pid) }).ToList();
问题是在2017年1月1日之前选择接受患者的最佳方式是什么?
因为我可以这样写:
var cured_receptions = (from r in receptions where r.ReceptionStart < new DateTime(2017, 7, 1) select r.PatientId).Distinct();
var cured_patients = from p in patients where cured_receptions.Contains(p.Id) select p;
但我不清楚'cured_receptions.Contains(p.Id)'代码到底是做什么的?它是否只是遍历搜索Id的所有患者,或者在数据库中使用类似索引的东西?可以使用cure_receptions.ToDictionary()或类似的东西来帮助吗?
答案 0 :(得分:0)
但我不清楚什么&#39; cured_receptions.Contains(p.Id)&#39;代码实际上呢?它是否只是遍历搜索Id的所有患者,或者在数据库中使用像索引这样的东西?
案例1:与数据库交互
如果您正在与数据库交互,那么在您通过调用ToList()
或通过迭代cured_patients
中的项目来执行第二个查询之前,不会向数据库发送任何查询。发送到数据库的查询将是这样的:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Surname] AS [Surname]
FROM [dbo].[Patients] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Receptions] AS [Extent2]
WHERE ([Extent2].[ReceptionStart] <
convert(datetime2, '2017-07-01 00:00:00.0000000', 121))
AND ([Extent2].[PatientId] = [Extent1].[Id])
)
它会使用任何指数吗?
如果PatientId
,Id
和ReceptionStart
被编入索引,那么数据库服务器将使用它们。
案例2:与内存中的项目互动
对于第一个查询,它将迭代所有receptions
,找到ReceptionStart
在给定日期之前的那些,选择PatientId
,然后删除任何重复的PatientId
(或多个)。
然后是下面的第二个查询:
var cured_patients =
from p in patients
where cured_receptions.Contains(p.Id)
select p;
将迭代patients
中的每个项目,并查看Id
中是否找到该项目的cured_receptions
。对于Id
中找到cured_receptions
的所有项目,它会选择这些项目。 Contains
只会返回true
或false
。
答案 1 :(得分:0)
重新开始假设内存中的所有内容......
在cured_receptions
调用之前,您的Contains
不会被评估,因此在该变量定义的末尾使用put .ToList()
会更有效率(大约快100倍) )。
LINQ没有“搜索” - Contains
正在进行搜索。如果你想使用像二进制搜索或更好的东西,哈希表,你必须创建它。如果您使用HashSet<int>
,那么您将获得另外47倍的加速。关闭Distinct
(因为HashSet
会处理)会节省15%。
在变量中记住常量而不是随时创建常量(new DateTime
...)可能会节省更多。即使大大增加您的随机数据也不会花费足够的时间来告诉HashSet
。
使用join
比初始查询更快,但您的查询与HashSet
相结合最快。
所以最快的代码是:
var cured_receptions = new HashSet<int>((from r in receptions where r.ReceptionStart < endDateTime select r.PatientId));
var cured_patients = from p in patients where cured_receptions.Contains(p.Id) select p;
注意:我使用LINQPad生成时序和示例数据。我更改了您的日期参数,因为您的值使大多数接待都匹配。
以下是我的LINQPad的代码:
var rand = new Random();
var begin = DateTime.Now;
var receptions = Enumerable.Range(1, 100000).SelectMany(pid => Enumerable.Range(1, rand.Next(0, 100)).Select(rid => new { PatientId = pid, ReceptionStart = begin.AddDays(-rand.Next(1, 180)) })).ToList();
var patients = Enumerable.Range(1, 100000).Select(pid => new { Id = pid, Surname = string.Format("Smith{0}", pid) }).ToList();
var startTime = Util.ElapsedTime;
var endDateTime = new DateTime(2017, 5, 1);
//var cured_receptions = (from r in receptions where r.ReceptionStart < new DateTime(2017, 5, 1) select r.PatientId).Distinct().ToList();
//var cured_receptions = (from r in receptions where r.ReceptionStart < new DateTime(2017, 5, 1) select r.PatientId).Distinct();
//var cured_receptions = new HashSet<int>((from r in receptions where r.ReceptionStart < new DateTime(2017, 5, 1) select r.PatientId).Distinct());
//var cured_receptions = new HashSet<int>((from r in receptions where r.ReceptionStart < endDateTime select r.PatientId).Distinct());
//var cured_receptions = new HashSet<int>((from r in receptions where r.ReceptionStart < new DateTime(2017, 5, 1) select r.PatientId));
var cured_receptions = new HashSet<int>((from r in receptions where r.ReceptionStart < endDateTime select r.PatientId));
var cured_patients = from p in patients where cured_receptions.Contains(p.Id) select p;
// var cured_patients = (from r in receptions
// where r.ReceptionStart < endDateTime
// join p in patients on r.PatientId equals p.Id
// select p).Distinct();
// var cured_patients = from p in patients
// join r in receptions on p.Id equals r.PatientId into rj
// where rj.Any(r => r.ReceptionStart < endDateTime)
// select p;
cured_patients.Count().Dump();
var endTime = Util.ElapsedTime;
(endTime - startTime).Dump("Elapsed");