C# 中的 LINQ to SQL 查询

Posted

技术标签:

【中文标题】C# 中的 LINQ to SQL 查询【英文标题】:LINQ to SQL query in C# 【发布时间】:2020-03-18 10:08:10 【问题描述】:

我是 C# /LINQ 查询的初学者 - 我有以下查询。右外连接在简单 SQL 中工作正常,但是,我无法在 LINQ 中实现它,但没有得到任何结果。

SELECT bt.PosGUID,s.PosGUID
    FROM tblSchedule s right Join tblTrade bt
        ON s.PosGUID = bt.PosGUID  AND  RowType = '4'

SELECT bt.PosGUID,s.PosGUID
    FROM  tblTrade bt left Join tblSchedule s 
        ON s.PosGUID = bt.PosGUID  AND  RowType = '4'

我需要了解上述左外连接的最佳方式是什么,我猜右外连接是不可能的,因此转换为左连接并尝试实现。

类似的东西 - 似乎有点复杂的查询:

        var tQuery = from bt in m_DataContext.tblTrades
                     join bPos in m_DataContext.tblBigPositionStatics on
                         new  bt.PosGUID  equals
                         new  bPos.PosGUID 

                     join bo in m_DataContext.tblBigOrders
                         on new  bt.ClOrdID, bt.PosGUID  equals new  bo.ClOrdID, bo.PosGUID 

                     join tradingAcc in m_DataContext.tblTradingAccounts
                         on new  Entity = bPos.PosEntity, Account = bPos.PosAccount  equals
                         new  tradingAcc.Entity, tradingAcc.Account 

                     join btRef in m_DataContext.tblTrades.DefaultIfEmpty()
                         on new  bt.PosGUID, ExecID = bt.ExecRefID  equals new  btRef.PosGUID, btRef.ExecID 
                         into temp
                     from btref in temp.DefaultIfEmpty()

                     join desk in m_DataContext.tblDesks
                     on bt.PosDeskGUID equals desk.GUID

                      // JOIN not working not briging back all records from  TBLTrades
                     join ss in m_DataContext.tblSchedules on bt.PosGUID equals ss.PosGUID into temp1
                     from ss in temp1.DefaultIfEmpty()


                     where bt.CreateDateTime >= dateToRun.getDate(false)
                     && bt.CreateDateTime < dateToRun.getDate(false).AddDays(1)
                     && bo.AsOfDateTime.Date == bt.AsOfDateTime.Date
                     && bPos.HardDeleteDate == null
                      && ss.RowType == "4"


                     //&& !"1".Equals(bt.ExecTransType)
                     //&& bt.HasBeenCorrected == false
                     && deskGuidList.Contains(desk.GUID)


                     select new  bt, bo, desk, bPos, tradingAcc, btref,ss ;

【问题讨论】:

您在 C# 中使用什么在 SQL 中进行查询?实体框架?英孚核心?其他 ?还请显示所涉及的数据类的最小定义。 旧的 LinqToSql 技术好吗?好用,但过时了;) 你可以将两列命名相同,如果你这样做,你怎么知道哪一列有什么数据?您的 SQL 应该类似于 SELECT bt.PosGUID AS bt_PosGUID,s.PosGUID AS s_PosGUID 这能回答你的问题吗? LINQ to SQL Left Outer Join 谢谢 - 为我的原始查询添加了更多详细信息。 【参考方案1】:

如果要对表 A 和 B 进行右外连接,只需交换这些表即可进行左外连接。

左外连接是一个 GroupJoin,后跟一个 SelectMany。根据我的经验,我使用 GroupJoin 的频率远高于使用 Left Outer Join 的频率。尤其是在一对多关系中。

例如:假设您有一张 Schools 表和一张 Student 表。每个学校都有零个或多个学生,每个学生都在一个学校学习,使用外键 SchoolId:一个简单的一对多关系。

给我所有学校及其所有学生

var result = dbContext.Schools
    .GroupJoin(dbContext.Students,          // GroupJoin Schools and Students
    school => school.Id,                    // from every School take the primary key
    student => student.SchoolId,            // from every Student take the foreign key
    (school, studentsOnThisSchool) => new   // from every School with all its Students
                                           // make one new object

        // Select only the School properties I plan to use
        Id = schoolId,
        Name = school.Name,

        OlderStudents = studentsOnThisSchool

            .Select(student => new
            
                // Select only the Student properties I plan to use:
                Id = student.Id,
                Name = student.Name,
                ...

                // not needed, I already know the value:
                // SchoolId = student.SchoolId,
            );

结果将是这样的序列:

School 1 with Students A, B, C, D.
School 2 with Students E, F,
School 3 without any Students
School 4 with Students G, H, I,
...

在我看来,这比左外连接的结果有用得多:

School 1 with Student A,
School 2 with Student E,
School 3 with Null student,
School 1 with Student B,
School 2 with Student F,
School 1 with Student C,
...

但是,嘿,这是你的选择。

我有一个 TblTrade,其中包含交易。每个 Trade 至少具有 Bt、PosGuid 和 RowType 属性。 我还有一个包含日程表的 TblSechedule。每个 Schedule 至少具有 Bt 和 PosGuid 属性。 向我提供所有具有相同 PosGuid 值的零个或多个 Schedules 的 RowType 4 交易。

var result = tblTrade
    // keep only the trades that have RowType equal to 4:
    .Where(trade => trade.RowType == 4)

    // do the GroupJoin:
    .GroupJoin(tblSchedule,
         trade => trade.PosGuid,
         schedule => schedule.PosGuid,
         (trade, schedulesWithSamePosGuid) => new
         
             // Select the trade properties you plan to use:
             TradeId = trade.Id,
             PosGuid = trade.PosGuid,
             ...

             Schedules = schedulesWithSamePosGuid.Select(schedule => new
             
                  // Select the schedule properties you plan to use:
                  Id = schedule.Id,
                  ...

                  // not needed, you already know the value:
                  // PosGuid = schedule.PosGuid.
              )
              .ToList(),
         );

如果你真的想要一个扁平的左外连接,添加一个 SelectMany:

.SelectMany(groupJoinResult.Schedules,
(trade, schedule) => new

    PosGuid = trade.PosGuid,

    // the Trade properties:
    Trade = new
    
        Id = trade.TradeId,
        ...
    ,

    Schedule = new
    
        Id = schedule.Id,
        ...
    ,
);

如果你愿意,你可以创建一个扩展函数LeftOuterJoin:

public static class MyQueryableExtensions

    // version without EqualityComparer:
    public static IQueryable<TResult> LeftOuterJoin<T1, T2, TKey, TResult>(
       this IQueryable<T1> source1,
       IQueryable<T2> source2,
       Func<T1, TKey> key1Selector,
       Func<T2, TKey> key2Selector,
       Func<T1, T2, TResult) resultSelector)
    
        return LeftOuterJoin(source1, source2,
            key1Selector, key2Selector, resultSelector, null);
    

具有 EqualityComparer 的版本:

public static IQueryable<TResult> LeftOuterJoin<T1, T2, TKey, TResult>(
   this IQueryable<T1> source1,
   IQueryable<T2> source2,
   Func<T1, TKey> key1Selector,
   Func<T2, TKey> key2Selector,
   Func<T1, T2, TResult) resultSelector,
   IEqualityComparer<TKey> comparer)

    if (comparer == null) comparer = EqualityComparer<TKey>.Default;

    // GroupJoin followed by SelectMany:
    return GroupJoin(source1, source2, key1Selector, key2Selector,
        (source1Item1, source2ItemsWithSameKey) => new
        
            Source1Item = source1Item,
            Source2Items = source2ItemsWithSameKey,
        )
        .SelectMany(groupJoinResult => groupJoinResult.Source2Items,
           (groupJoinResult, source2Item) =>
               ResultSelector(groupJoinResult.Source1Item, source2Item));

    

用法:

var result = tblTrade
    .Where(trade => trade.RowType == 4)
    .LeftOuterJoin(tblSchedule,
    trade => trade.PosGuid,
    schedule => schedule.PosGuid,
    (trade, schedule) => new
    
       // Select the trade and Schedule properties that you plan to use
       // for example the complete trade and schedule:
       Trade = trade,
       Schedule = schedule,

       // or only some properties:
       CommonPosGuid = trade.PosGuid,
       Trade = new
       
           Id = trade.Id,
           ...
       

       Schedule = new
       
           Id = trade.Id,
           ...
       
    )

【讨论】:

非常感谢您的解释 - 我会试一试,如果这有帮助,请告诉您。

以上是关于C# 中的 LINQ to SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to SQL 可以查询 XML 字段 DB-serverside 吗?

Linq to SQL 的增删改查操作

LinQ to SQL 查询

初识Linq to Entity

针对 LINQ To SQL 查询中的位字段评估布尔值

Linq 查询的演变过程