【问题标题】:How to write the following in Linq or Sql如何在 Linq 或 Sql 中编写以下内容
【发布时间】:2010-11-24 02:53:12
【问题描述】:

如果我能得到一些帮助来编写一个 LINQ,该 LINQ 将从表 A 中获取所有字段,并且配置文件 1 在表 AB 中具有值的那些字段显示该值,否则如果配置文件 1 在表 AB 中没有条目,然后将值显示为空。

表 A

AID    Field
-----------
1      OneField
2      TwoField
3      ThreeField

表 B

BID    Value
-----------
1      OneValue
2      TwoValue
3      ThreeValue

表 AB

ABID   AID   BID  ProfileId
-------------------------
1      1     1    1
2      2     3    1

我正在尝试编写一个 Linq(或 sql 查询),它将显示配置文件 id 1、表 a 中的所有值以及适用的表 b 中的值。

例如

ProfileID  AID       Field        BID       Value
--------------------------------------------------------
1          1         OneField     1         OneValue
1          2         TwoField     3         ThreeValue
1          3         ThreeField   NULL      NULL

如您所见,目的是从表 A 中获取所有字段,并且配置文件 1 在表 AB 中有条目的字段显示表 B 中的值,否则如果配置文件 1 在表 AB 中没有条目, 然后显示为 null。

如果解决方案需要它,我很乐意用 sql 编写它并通过存储过程调用它。

谢谢

【问题讨论】:

    标签: c# sql linq linq-to-sql tsql


    【解决方案1】:

    我得到了一个有效的 LINQ 解决方案,其中包含一个完整的独立测试。我没有在末尾添加任何断言,但末尾的变量“queryResult”将包含一个包含您想要的结果的列表。我对其进行了测试并验证了它的工作原理。代码如下:

    编辑:回应您的评论这里是示例#2

        public class tablea
        {
            public int AID { get; set; }
            public string Field { get; set; }
        }
        public class tableb
        {
            public int BID { get; set; }
            public string Value { get; set; }
        }
        public class tableab
        {
            public int ProfileID { get; set; }
            public int AID { get; set; }
            public string Field { get; set; }
            public int BID { get; set; }
            public string Value { get; set; }
        }
    
        public class result
        {
            public int? profileid { get; set; }
            public int? aid { get; set; }
            public string field { get; set; }
            public string bid { get; set; }
            public string value { get; set; }
        }
    
        [Test]
        public void TestQuery()
        {
            var tablea = new List<tablea>();
            var tableb = new List<tableb>();
            var tableab = new List<tableab>();
    
            tablea.Add(new tablea{AID = 1,Field = "OneField"});
            tablea.Add(new tablea{AID = 2,Field = "TwoField"});
            tablea.Add(new tablea{AID = 3,Field = "ThreeField"});
    
            tableb.Add(new tableb{BID = 1,Value = "OneValue"});
            tableb.Add(new tableb{BID = 2,Value = "TwoValue"});
            tableb.Add(new tableb{BID = 3,Value = "ThreeValue"});
    
            tableab.Add(new tableab{AID = 1,BID=1,ProfileID = 1});
            tableab.Add(new tableab{AID = 2,BID=3,ProfileID = 1});
    
            var profileId = 1;
    
            var q1 = (from a in tablea
                      let bid = (from ab in tableab where ab.ProfileID == profileId && ab.AID == a.AID select ab.BID).FirstOrDefault()
                      let value = (from ab in tableab where ab.ProfileID == profileId && ab.AID == a.AID && ab.BID == bid select ab.Value).FirstOrDefault()
                    select new result
                                {
                                    profileid = profileId,
                                    aid = a.AID,
                                    field = a.Field,
                                    bid = (bid == 0 ? "null" : bid.ToString()),
                                    value = value ?? "null"
                               }).ToList();
    
        }
    

    【讨论】:

    • 嗨罗伯托。谢谢你,它在单元测试中效果很好。但是,对于 linq2sql 实体,DefaultIfEmpty 失败,因为如果为空,则默认传递的值对它们有 fk 约束,并且我得到异常“System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException:由于对象的当前状态,操作无效”。真可惜。当我找到解决方案时,我会继续尝试并发布。谢谢。
    • 该死的……这么近。不幸的是,我不使用 LINQ to SQL,所以我不知道它的局限性。不幸的是,如果您为 DefaultIfEmpty 返回 null 则查询的其余部分将失败,因为它正在尝试访问该返回对象的属性....
    • 谢谢罗伯托。这为我指明了正确的方向。我会继续努力。将您的答案标记为完整的公认答案。 +1 用于进行完整的独立测试。
    • linq生成的sql做4个子查询返回想要的数据。在某个时候,我会对其进行分析,看看是否需要对其进行优化,但现在我真的很高兴 :)
    【解决方案2】:

    这应该可以工作(未经测试):

    SELECT ProfileID, A.AID, Field, B.BID, Value 
    FROM AB 
    LEFT OUTER JOIN A ON AB.AID = A.AID
    LEFT OUTER JOIN B ON AB.BID = B.BID
    

    【讨论】:

    • 没有带回第 3 行。这就是我卡住的地方。
    【解决方案3】:

    既然你想要表 A 的 ALL ROWS,你需要从表 A 开始:

    SELECT AB.ProfileID, A.AID, A.Field, B.BID, B.Value 
    FROM A 
    LEFT OUTER JOIN AB ON AB.AID = A.AID
    LEFT OUTER JOIN B ON AB.BID = B.BID
    WHERE (AB.ProfileID = 1 OR AB.ProfileID IS NULL)
    

    如果您在查询中从表 AB 开始,您将只能得到表 AB 中的内容 - 而第 3 行在表 AB 中没有条目。

    马克

    【讨论】:

    • 谢谢马克,但它仍然不存在,因为如果我在表 AB 中有超过 1 个配置文件,并且配置文件 2 AID = 3,那么 B 上的左外连接不起作用。 Profileid 1 返回 2 条记录,profileid 2 返回 3。我认为它缺少记录,因为当其他一些配置文件使用 AID=3 时,ProfileID 不会为空。
    • 这里的主要问题之一是您尝试在 profileID 上进行选择,但您想要选择的内容超出了“AB”连接表中可用的内容 - 这真的很难.. ....
    • 您能否将您想要选择的 ALL 行(包括其 ProfileID)添加到“AB”表中,然后将“BID”列留空以供那些没有的行一个“B”实体关联??这样,您可以从 AB 连接表中选择并使用“ProfileID = x”WHERE 子句
    • 是的,我也想过添加到 ab 中,但是 ab 表中已经有记录。因此,以免我编写触发器,在表 a 中插入值不会自动将所有配置文件的这些值插入到表 ab 中。试图想出一种干净的方法来做到这一点......
    • 也许我应该接受打击并运行多个查询,一个获取所有字段,一个获取 profileid 1 的所有值,并加入代码而不是 sql。
    【解决方案4】:

    [代码] var 结果 = 来自 a 中的 a 加入 ab in ab on a.aid 等于 ab.aid 进入 tmp1 join b in b on b.id 等于 ab.bid 到 tmp2 from c in tmp1.DefaultIfEmpty() /* 这是获取空值 */ 来自 tmp2.DefaultIfEmpty() 中的 d 选择新的 { c.ProfileID, a. 援助, a.字段,
    c.投标, d.价值 }; [/代码]

    【讨论】:

      【解决方案5】:

      SQL 版本是这样的

      select TableA.*, TableB.*
      from TableA
          left outer join TableC on TableA.ID = Aid
          left outer join TableB on TableB.id = Bid
      where
          TableC.ProfileID = 1
          or TableC.ProfileID is null
      

      即使在连接的另一端没有匹配的记录,外连接也可确保您从 TableA 中获得结果。因此,您也必须在 ProfileID 中允许空值。

      【讨论】:

        【解决方案6】:

        请看下面的例子:

        var result = from a  in a
                     join ab in ab on a.aid equals ab.aid into tmp1
                 join b  in b  on b.id  equals ab.bid into tmp2
                 from c in tmp1.DefaultIfEmpty()  /* this is to get the null values */
                 from d in tmp2.DefaultIfEmpty()
                 select new 
                {
                 c.ProfileID,
                 a.AID,
                 a.Field,   
                         c.bid,
                 d.Value
                };
        

        【讨论】:

        • 谢谢!您还可以编辑您自己的帖子并更改它们 - 既然您已经添加了第二个帖子,为什么不使用非格式化代码 sn-p 删除第一个...。 .
        • 马克感谢您帮助我,但我担心我没有看到删除按钮
        • 您好 Edwards,感谢您的帖子。出于某种原因,Visual Studio 不喜欢第一个连接之后的第二个连接。我错过了什么吗?
        • 嗨,Ash,你是使用 linq to entity 还是 linq to SQL
        • 我正在使用 linq 2 sql。谢谢
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-14
        • 1970-01-01
        • 2019-05-18
        相关资源
        最近更新 更多