首页 > 解决方案 > 从数据库中选择多个列

问题描述

我想从数据库中选择三列(用户名、浏览器和浏览器版本),但是当我使用第一个变量时,我会为每个用户获得多个结果。我只希望每个用户使用他使用的浏览器一次。使用第二个变量,我获得了一次用户,但没有浏览器。

var browserUser = db.Tracking.Include(t => t.Users).ToList();
var browserUserFiltered = browserUser.GroupBy(x => x.userIdFk).Select(g => g.First());


实际输出截图
数据库模型

标签: c#sql-serverentity-framework

解决方案


所以你有Users和的序列Trackings。everyUser有零个或多个Trackings,everyTracking完全属于一个User,使用外键UserIdFk。直接的一对多关系。

User                Tracking
Id | Name           Id | UserIdFk | <other properties>
 1 | Jan            10 |    1     | A
 2 | Piert          11 |    1     | B
                    12 |    2     | C

var browserUser = db.Tracking.Include(t => t.Users).ToList();

结果:一系列 3 个跟踪,每个跟踪都有他们的用户

Tracking: Id = 10, UserIdFk = 1; User: Id = 1; Name = Jan
Tracking: Id = 11, UserIdFk = 1; User: Id = 1; Name = Jan
Tracking: Id = 12, UserIdFk = 2; User: Id = 2, Name = Piert

进行分组:

var browserUserFiltered = browserUser.GroupBy(x => x.userIdFk)

结果:2个IGrouping的序列

Group Key: 1; Elements:
      Tracking: Id = 10, UserIdFk = 1; User: Id = 1; Name = Jan
      Tracking: Id = 11, UserIdFk = 1; User: Id = 1; Name = Jan
Group Key: 2; Elements:
      Tracking: Id = 12, UserIdFk = 2; User: Id = 2, Name = Piert

在 GroupBy 之后继续:

.Select(g => g.First());

结果:两个项目的序列,每组一个项目:

Tracking: Id = 10, UserIdFk = 1; User: Id = 1; Name = Jan
Tracking: Id = 12, UserIdFk = 2; User: Id = 2, Name = Piert

问题:为什么您认为结果中只有一项?


在一对多中,您可以采用两种方式:

  • 获取用户的跟踪信息,这意味着在内部完成了 Group-Join。每个用户只被转移到您的流程一次。没有跟踪数据的用户也在您的结果中。
  • 获取跟踪,每个都有其用户。在内部完成了内部连接。如果一个用户有多个 Tracking,相同的用户数据会被发送不止一次。根本没有提到没有任何跟踪数据的用户。

由你决定你想要什么。

使用 Select 而不是 Include

数据库查询中较慢的部分之一是将所选数据传输到您的进程。因此,明智的做法是限制所选数据的数量。

如果用户 4 有 1000 个 Tracking,那么每个 Tracking 都会有一个外键 UserId,其值等于 4。如果您使用 Include 选择数据,则相同的外键值将被发送 1000 次,而您已经知道它等于用户主键的值:4。真是浪费!

查询数据时,始终使用 Select 并仅选择您实际计划使用的属性。仅当您计划更新包含的项目时才使用 Include。

因此,要获取一些带有(某些)跟踪的用户,请执行以下查询:

var result = dbContext.Users
    .Where(user => ...)              // only if you don't want all Users
    .Select(user => new
    {
        // Select only the User data you actually plan to use
        Id = user.Id,
        Name = user.Name,
        ...

        Trackings = user.Trackings
            .Where(tracking => tracking.Date >= startDate) // only if you don't want all Trackings
            .Select(tracking => new
            {
                 // again: select only the properties you plan to use
                 Id = tracking.Id,
                 Name = tracking.Name,
                 ...
                 // not needed, you know the value: UserId = tracking.UserId
            })
            .ToList(),
    });

推荐阅读