如何select一列的最大值并检索多个结果?
How to select the maximum value of a column and retrieve multiple results?
我正在努力了解如何在我的 ASP.NET Web API v2 项目中使用 Entity Framework 6 和 SQL 服务器构建我的 SQL 查询。
我的 table 结构:Computers
table 是我的根目录 table,用于我的软件清点的所有计算机。它包含被其他 table 用作外键的列 ComputerID
。
我可以使用以下查询检索一台计算机,包括我拥有的所有信息:
Computer computer = ComputersDbContext.Computers
... more includes....
.Include("Computer_Win_Installed_Software")
... more includes....
.Where(a => a.ComputerId == computerId)
.First(t => t.TenantId == tenantId);
如您所见,我有另一个名为 Computer_Win_Installed_Software
的 table,它存储系统上安装的所有软件。它看起来像这样:
IdentityKey | ComputerID (FK) | Softwarename | EntryTimestamp
------------+-----------------+------------------+--------------
1 | 1 | Some Software | 1547241345
2 | 1 | Another Software | 1547241345
EntryTimestamp
是一个 Unix 时间戳,它对每个清单 运行 都是唯一的,并且对于在 运行 上发现的所有软件都是相同的。现在,如果系统再次清点,table 将如下所示:
IdentityKey | ComputerID (FK) | Softwarename | EntryTimestamp
------------+-----------------+--------------------+---------------
1 | 1 | Some Software | 1547241345
2 | 1 | Another Software | 1547241345
3 | 1 | Some Software | 1886454564
4 | 1 | Another Software | 1886454564
5 | 1 | Even More Software | 1886454564
我想保留历史数据,所以我需要保留旧条目。
我的问题是我上面的 EF 查询将 return 结果对象中的所有这些条目。
如何更改我的查询,使其仅 returns:
IdentityKey | ComputerID (FK) | Softwarename | EntryTimestamp
------------+-----------------+--------------------+---------------
3 | 1 | Some Software | 1886454564
4 | 1 | Another Software | 1886454564
5 | 1 | Even More Software | 1886454564
我考虑过使用 2 个查询:
- 第一个查询:我检查
EntryTimestamp
的最大值
第二个查询:像这样:
Computer computer = ComputersDbContext.Computers
.Include("Computer_Win_Installed_Software")
.Where(a => a.ComputerId == computerId)
.Where(b => b.Computer_Win_Installed_Software.EntryTimestamp == EntryTimestamp)
.First(t => t.TenantId == tenantId);
但 Intellisense 立即对我尖叫 :D
我也想过只selecting列EntryTimestamp
的MAX()
;但我什至不能在查询代码中使用 b.Computer_Win_Installed_Software.EntryTimestamp
。
当我写:.Where(b => b.Computer_Win_Installed_Software
时,它不会将任何列列为可用选项。
我认为这是因为 EF 中的 Computer_Win_Installed_Software
class 是 ICollection<Computer_Win_Installed_Software>
类型。其他 table 与 Computers
table 都有一对多关系,这也是同样的问题。
另一个 table 与 Computers
table 具有 1 对 1 的关系,我可以在那里 select 所有列。
我超级困惑。
是的,我做了 google,但我找不到任何帮助我的东西。去这里的正确方法是什么?
编辑:添加模型
数据库上下文:
public class DbEntities : DbContext
{
public virtual DbSet<Agent> Agents { get; set; }
public virtual DbSet<Computer_Win_Installed_Software> ComputerWinInstalledSoftware { get; set; }
public DbSet<Computer> Computers { get; set; }
}
计算机的 EF 型号:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ProjectName
{
using System;
using System.Collections.Generic;
public partial class Computer
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Computer()
{
this.Computer_Win_Installed_Software = new HashSet<Computer_Win_Installed_Software>();
}
public int ComputerId { get; set; }
public int TenantId { get; set; }
public virtual Agent Agent { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Computer_Win_Installed_Software> Computer_Win_Installed_Software { get; set; }
}
}
Computer_Win_Installed_Software 的 EF 模型:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace KysoWebApi
{
using System;
using System.Collections.Generic;
public partial class Computer_Win_Installed_Software
{
public int ComputerId { get; set; }
public string SoftwareName { get; set; }
public Nullable<double> SoftwareVersion { get; set; }
public Nullable<bool> IsServerSoftware { get; set; }
public Nullable<int> SoftwareVendorId { get; set; }
public string SoftwareIs32or64bits { get; set; }
public int ComputerWinInstalledSoftwareEntryId { get; set; }
public string EntryTimestamp { get; set; }
public virtual Computer Computer { get; set; }
}
}
请试试这个:
var computer = db.ComputerWinInstalledSoftware
.Include("Computer")
.First(c => c.Computer.ComputerId == computerId
&& c.Computer.TenantId == tenantId
&& c.EntryTimestamp == EntryTimestamp ).Computer;
试试这个:
Computer computer = context.Computers
.Include(c => c.Computer_Win_Installed_Software.Join(
c.Computer_Win_Installed_Software.Where(a => a.ComputerId == computerId && a.Computer.TenantId == tenantId)
.GroupBy(s => s.Softwarename)
.Select(g => new { SoftwareName = g.Key, MaxDate = g.Max(r => r.EntryTimestamp) })
,o => o.EntryTimestamp
,i => i.MaxDate
, (o,i) => o))
.First(a => a.ComputerId == computerId && a.TenantId == tenantId);
我在您的实体定义中的任何地方都没有看到 TenantId
列,因此请确保将正确的字段放在那里。
这是在按最大日期加入记录。
更新:我做了一个小修正。它应该按软件名称分组。根据您的喜好更改分组,以便正确地唯一标识每个软件。
因此,对于给定的 Computer
,ICollection<Computer_Win_Installed_Software> Computer_Win_Installed_Software
总是会包含所有 EntryTimestampts
的相关行。您不能(轻松) 包含的实体。
因此,只有 return 个过滤的实体:
var computer = ComputersDbContext.Computers
.Where(a => a.ComputerId == computerId)
.First(t => t.TenantId == tenantId);
var cwis = computer
.Computer_Win_Installed_Software
.Where(b => b.EntryTimestamp == EntryTimestamp);
当然,我不确定这是不是你真正想要做的,你可能有 XY Problem.
您可以尝试不包括相关对象,而只查询它们吗?
var computer = ComputersDbContext.Computers
.Where(a => a.ComputerId == computerId)
.First(t => t.TenantId == tenantId);
.Where(b => b.Computer_Win_Installed_Software.EntryTimestamp == EntryTimestamp);
好的。经过大量研究后,我发现有几个 post 指出您不能在 include 语句中进行过滤,这是一种全有或全无的交易。最后过滤总是会产生奇怪的结果。
来源:
Entity Framework - Selective Condition on Included Navigation Property
Does Include load all related entities or the specified ones?
我找到了 2 个解决方案:
动态选择扩展:
https://github.com/thiscode/DynamicSelectExtensions
好像有EntityFramework+ (EF+)。我在 Whosebug 上见过 post 的开发人员,尽管我目前找不到他们。他的图书馆似乎能够做我想做的事,但普通的 EF 不能,而且似乎永远不会,因为这个功能请求可以追溯到几年前。
感谢大家尝试帮助我并投入时间。现在,我将进行多个查询,并可能稍后再回来查看,希望有更多经验 :D
我正在努力了解如何在我的 ASP.NET Web API v2 项目中使用 Entity Framework 6 和 SQL 服务器构建我的 SQL 查询。
我的 table 结构:Computers
table 是我的根目录 table,用于我的软件清点的所有计算机。它包含被其他 table 用作外键的列 ComputerID
。
我可以使用以下查询检索一台计算机,包括我拥有的所有信息:
Computer computer = ComputersDbContext.Computers
... more includes....
.Include("Computer_Win_Installed_Software")
... more includes....
.Where(a => a.ComputerId == computerId)
.First(t => t.TenantId == tenantId);
如您所见,我有另一个名为 Computer_Win_Installed_Software
的 table,它存储系统上安装的所有软件。它看起来像这样:
IdentityKey | ComputerID (FK) | Softwarename | EntryTimestamp
------------+-----------------+------------------+--------------
1 | 1 | Some Software | 1547241345
2 | 1 | Another Software | 1547241345
EntryTimestamp
是一个 Unix 时间戳,它对每个清单 运行 都是唯一的,并且对于在 运行 上发现的所有软件都是相同的。现在,如果系统再次清点,table 将如下所示:
IdentityKey | ComputerID (FK) | Softwarename | EntryTimestamp
------------+-----------------+--------------------+---------------
1 | 1 | Some Software | 1547241345
2 | 1 | Another Software | 1547241345
3 | 1 | Some Software | 1886454564
4 | 1 | Another Software | 1886454564
5 | 1 | Even More Software | 1886454564
我想保留历史数据,所以我需要保留旧条目。
我的问题是我上面的 EF 查询将 return 结果对象中的所有这些条目。
如何更改我的查询,使其仅 returns:
IdentityKey | ComputerID (FK) | Softwarename | EntryTimestamp
------------+-----------------+--------------------+---------------
3 | 1 | Some Software | 1886454564
4 | 1 | Another Software | 1886454564
5 | 1 | Even More Software | 1886454564
我考虑过使用 2 个查询:
- 第一个查询:我检查
EntryTimestamp
的最大值
第二个查询:像这样:
Computer computer = ComputersDbContext.Computers .Include("Computer_Win_Installed_Software") .Where(a => a.ComputerId == computerId) .Where(b => b.Computer_Win_Installed_Software.EntryTimestamp == EntryTimestamp) .First(t => t.TenantId == tenantId);
但 Intellisense 立即对我尖叫 :D
我也想过只selecting列EntryTimestamp
的MAX()
;但我什至不能在查询代码中使用 b.Computer_Win_Installed_Software.EntryTimestamp
。
当我写:.Where(b => b.Computer_Win_Installed_Software
时,它不会将任何列列为可用选项。
我认为这是因为 EF 中的 Computer_Win_Installed_Software
class 是 ICollection<Computer_Win_Installed_Software>
类型。其他 table 与 Computers
table 都有一对多关系,这也是同样的问题。
另一个 table 与 Computers
table 具有 1 对 1 的关系,我可以在那里 select 所有列。
我超级困惑。
是的,我做了 google,但我找不到任何帮助我的东西。去这里的正确方法是什么?
编辑:添加模型
数据库上下文:
public class DbEntities : DbContext
{
public virtual DbSet<Agent> Agents { get; set; }
public virtual DbSet<Computer_Win_Installed_Software> ComputerWinInstalledSoftware { get; set; }
public DbSet<Computer> Computers { get; set; }
}
计算机的 EF 型号:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ProjectName
{
using System;
using System.Collections.Generic;
public partial class Computer
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Computer()
{
this.Computer_Win_Installed_Software = new HashSet<Computer_Win_Installed_Software>();
}
public int ComputerId { get; set; }
public int TenantId { get; set; }
public virtual Agent Agent { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Computer_Win_Installed_Software> Computer_Win_Installed_Software { get; set; }
}
}
Computer_Win_Installed_Software 的 EF 模型:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace KysoWebApi
{
using System;
using System.Collections.Generic;
public partial class Computer_Win_Installed_Software
{
public int ComputerId { get; set; }
public string SoftwareName { get; set; }
public Nullable<double> SoftwareVersion { get; set; }
public Nullable<bool> IsServerSoftware { get; set; }
public Nullable<int> SoftwareVendorId { get; set; }
public string SoftwareIs32or64bits { get; set; }
public int ComputerWinInstalledSoftwareEntryId { get; set; }
public string EntryTimestamp { get; set; }
public virtual Computer Computer { get; set; }
}
}
请试试这个:
var computer = db.ComputerWinInstalledSoftware
.Include("Computer")
.First(c => c.Computer.ComputerId == computerId
&& c.Computer.TenantId == tenantId
&& c.EntryTimestamp == EntryTimestamp ).Computer;
试试这个:
Computer computer = context.Computers
.Include(c => c.Computer_Win_Installed_Software.Join(
c.Computer_Win_Installed_Software.Where(a => a.ComputerId == computerId && a.Computer.TenantId == tenantId)
.GroupBy(s => s.Softwarename)
.Select(g => new { SoftwareName = g.Key, MaxDate = g.Max(r => r.EntryTimestamp) })
,o => o.EntryTimestamp
,i => i.MaxDate
, (o,i) => o))
.First(a => a.ComputerId == computerId && a.TenantId == tenantId);
我在您的实体定义中的任何地方都没有看到 TenantId
列,因此请确保将正确的字段放在那里。
这是在按最大日期加入记录。
更新:我做了一个小修正。它应该按软件名称分组。根据您的喜好更改分组,以便正确地唯一标识每个软件。
因此,对于给定的 Computer
,ICollection<Computer_Win_Installed_Software> Computer_Win_Installed_Software
总是会包含所有 EntryTimestampts
的相关行。您不能(轻松)
因此,只有 return 个过滤的实体:
var computer = ComputersDbContext.Computers
.Where(a => a.ComputerId == computerId)
.First(t => t.TenantId == tenantId);
var cwis = computer
.Computer_Win_Installed_Software
.Where(b => b.EntryTimestamp == EntryTimestamp);
当然,我不确定这是不是你真正想要做的,你可能有 XY Problem.
您可以尝试不包括相关对象,而只查询它们吗?
var computer = ComputersDbContext.Computers
.Where(a => a.ComputerId == computerId)
.First(t => t.TenantId == tenantId);
.Where(b => b.Computer_Win_Installed_Software.EntryTimestamp == EntryTimestamp);
好的。经过大量研究后,我发现有几个 post 指出您不能在 include 语句中进行过滤,这是一种全有或全无的交易。最后过滤总是会产生奇怪的结果。
来源: Entity Framework - Selective Condition on Included Navigation Property
Does Include load all related entities or the specified ones?
我找到了 2 个解决方案: 动态选择扩展: https://github.com/thiscode/DynamicSelectExtensions
好像有EntityFramework+ (EF+)。我在 Whosebug 上见过 post 的开发人员,尽管我目前找不到他们。他的图书馆似乎能够做我想做的事,但普通的 EF 不能,而且似乎永远不会,因为这个功能请求可以追溯到几年前。
感谢大家尝试帮助我并投入时间。现在,我将进行多个查询,并可能稍后再回来查看,希望有更多经验 :D