从表

时间:2017-08-09 09:12:07

标签: c# sql entity-framework linq

我有一个包含数据库中用户的银行帐户类型的表,如下所示

BankAcctTypes

|  ID   |   UserID   |  AcctType   |   AcctNo  |
------------------------------------------------
|  1    |   User1    |  Single     |  1234     |
|  2    |   User2    |  Single     |  2345     |
|  3    |   User2    |  Joint      |  3456     |
|  4    |   User2    |  Joint      |  4567     |
|  5    |   User3    |  Joint      |  5678     |
|  6    |   User3    |  Joint      |  6789     |
|  7    |   User4    |  Single     |  7890     |
|  9    |   User4    |  Single     |  8901     |
|  10   |   User5    |  Joint      |  9012     |
------------------------------------------------

**用户可以拥有任意数量的联合和单一行为,也可以是任何组合。任何没有用户帐户的用户都没有在给定的表格中找到。

现在要求是根据以下规则从此表中获取所有用户及其银行帐户类型

  1. 如果只有single类型的银行帐户(一个或多个)返回Single
  2. 如果只有Joint类型的银行帐户(一个或多个)返回Joint
  3. 如果JointSingle(任意组合)的混合返回Joint
  4. ps:要返回的多个accts中的任何一个。没有限制

    预期结果:

    |  ID   |    UserID    | AcctType  | AcctNo | 
    ---------------------------------------------
    |  1    |   User1      |  Single   | xxxxxx |  
    |  2    |   User2      |  Joint    | xxxxxx | (any one of Joint acct)
    |  3    |   User3      |  Joint    | xxxxxx | 
    |  4    |   User4      |  Single   | xxxxxx | (any one of Joint acct)
    |  5    |   User5      |  Joint    | xxxxxx | 
    ---------------------------------------------
    

    请帮我构建LINQ语句/ SQL语句。

2 个答案:

答案 0 :(得分:1)

我根据您的示例数据创建了以下模型:

public enum AcctType
{
    Single,
    Joint
}

public class Account
{
    public int ID;
    public string UserID;
    public AcctType AcctType;
    public int AcctNo;
}

此查询应该适合您:

var result = from account in accounts
             group account by account.UserID
             into grouping
             select grouping.Any(account => account.AcctType == AcctType.Joint) && grouping.Any(account => account.AcctType == AcctType.Single)
             ? grouping.Where(account => account.AcctType == AcctType.Joint).Take(1)
             : grouping.ToList();

这是做什么的:

  1. 浏览所有帐户
  2. UserID
  3. 分组帐户
  4. 检查每个分组,看它是否包含至少一个联合帐户和一个帐户:
    • 如果是,请过滤分组以获取所有联合帐户,然后选择第一个帐户
    • 如果没有,请获取所有帐户
  5. 修改

    对不起,我没注意到你在使用EF。 您收到此错误的原因是因为LINQ to SQL不支持树状结果。这意味着在单个查询中执行此操作是不可能的。

    您必须将其拆分:

    var groupings = accounts.GroupBy(account => account.UserID);
    var joint = groupings.Where(grouping => grouping.Any(account => account.AcctType == AcctType.Joint) && grouping.Any(account => account.AcctType == AcctType.Single));
    var result = joint.Select(grouping => grouping.First(account => account.AcctType == AcctType.Joint)).Concat(groupings.Except(joint).SelectMany(grouping => grouping.ToList()));
    

    分为3个操作并不优雅,但必须避免重复某些操作,例如group-by。

答案 1 :(得分:0)

您写道:

  

要求是使用银行帐户类型获取所有用户   已提供

因此,结果主要是用户的属性,他们提供的银行账户的一些属性,以及关于单一和联合银行账户的一些责任。

后来你写道:

  

可能存在多个单一行为且没有关节的用户   行为以及反之亦然。

您的要求不完整。

正确指定:

  • 只有一个帐户且没有联合帐户的用户:返回(选择)具有(仅选择)一个且仅一个帐户的属性的用户属性
  • 拥有零个或多个单个帐户且只有一个联合帐户的用户:返回(选择)具有(仅选择)一个联合帐户的属性的用户属性

未指定:

  • 零个帐户和零联合帐户的用户
  • 拥有多个单一帐户且零联合帐户的用户
  • 拥有多个联名帐户的用户

您可以决定使用用户拥有的AcctType返回所有银行帐户类型的UserId,AcctType和一系列AcctNo。

您还可以决定为每个银行帐户类型重复UserId和AcctType。

当然第一个更有效率,因为UserId和AcctType只被提取一次,即使用户有数百个银行账户。

假设您有一个正确的Entity-Framework类定义,其中User和BankAccountType之间存在标准的一对多关系:

class User
{
    public int Id {get; set;}

    // a user has zero or more bank accountTypes
    public virtual ICollection<BankAccountType> BankAccountTypes {get; set;}

    ...
}

enum AcctType
{
    single,
    joint,
};

class BankAccountType
{
    public int Id {get; set;}

    // every bank account type belongs to one user via foreign key UserId:
    public int UserId {get; set;}
    public virtual User User {get; set;}

    public AcctType AccType {get; set;}
    public int AcctNo {get; set;}
}

您的查询分步骤:

var result = dbContext.BankAcctTypes
    // Group all BankAcctTypes by the same UserId:
    .GroupBy(bankAcctType => bankAcctType.UserId)

    // From every group, get the Key (which is the UserId)
    // get all elements of the group
    // get a sequence of all joint accounts
    .Select(group => new
    {
        UserId = group.Key,      // the UserId of all bankAcctType in this group
        AllBankAccounts = group,
        JointBankAccounts = group
            .Where(groupElement => groupElement.AccType == AcctType.Joint),

    })

    // if there are joint accounts, keep the joint accounts,
    // otherwise keep AllAccounts (which are all Single, as there are no joint)
    .Select(item => new
    {
        UserId = item.UserId,
        BankAccounts = item.JointBankAccounts.Any() ?
            item.JointBankAccount :
            item.AllBankAccounts,
    })

    // finally extract the values you want:
    .Select(item => new
    {
        UserId = item.UserId,
        BankAccounts = item.BankAccounts.Select(bankAccount => new
        {
            Id = bankAccount.Id,
            AcctType = bankAccount.AcctType,
            AcctNo = bankAccount.AcctNo
        }),
    });