代码之家  ›  专栏  ›  技术社区  ›  Erik Funkenbusch

与常量值和投影结合(或concat等)

  •  3
  • Erik Funkenbusch  · 技术社区  · 15 年前

    我发现了一个非常讨厌的关于linq to sql的gotcha,我不确定最好的解决方案是什么。

    如果采用一个简单的L2S UNION语句,并在一端包含L2S代码,在另一端包含常量,则这些常量不会包含在SQL UNION中,并且只会投影到SQL之后的输出中,从而导致有关该UNION的列数的SQL错误。

    举个例子:

    (from d in dc.mytable where foo == "bar" select new {First = d.Foo, Second = d.Roo})
    .Union(from e in dc.mytable where foo == "roo" select new {First= "", Second = e.Roo})
    

    这将生成一个错误“所有使用union、intersect或except运算符组合的查询在其目标列表中的表达式数必须相等。”

    这一点尤其阴险(令人恼火),因为列表中显然有相同数量的表达式,但是当您查看SQL时,您会注意到它不会在联合的后半部分为“first”生成列。这是因为“first”是在查询后插入到投影中的。

    好的,简单的解决方案是将每个部分转换为可枚举的、列表或其他内容,然后在内存中而不是SQL中进行联合,如果处理的是少量数据,那就没问题了。但是,如果您使用的是一组大数据,然后计划在返回之前对其进行进一步筛选(在SQL中),那么这是不理想的。

    我想我要找的是一种强制L2在SQL中包含列的方法。有可能吗?

    更新:

    虽然不是完全重复,但此错误类似于 This Question 也有类似的解决方案。所以我要结束了,但并没有删除这个问题,因为它可能会帮助其他人以不同的方式找到可行的解决方案。

    不幸的是,L2S有时太聪明了。

    2 回复  |  直到 14 年前
        1
  •  1
  •   Erik Funkenbusch    15 年前

    我已经决定唯一真正的解决方案是使用存储过程。希望这有帮助。

        2
  •  1
  •   Tormod    14 年前

    这是linq2sql提供程序中的一个错误。 在LinqPad中,您可以清楚地看到错误。

    (from d in dc.mytable where foo == "bar" select new {First = d.Foo, Second = d.Roo})  
    .Union(from e in dc.mytable where foo == "roo" select new {First= "", Second = e.Roo}) 
    

    服务器端是否会产生这样的结果:

    SELECT [t2].[Foo], [t2].[Roo]
    FROM (
        SELECT [t0].[Foo], @p0 AS [value]
        FROM [dc].[Mytable] AS [t0]
        UNION ALL
        SELECT [t1].[Foo], [t1].[Roo]
        FROM [dc].[Mytable] AS [t1]
        ) AS [t2]
    

    这将是一个问题,因为联合将命名第二列“value”而不是“roo”,这将导致外部查询失败。

    但是,如果您切换两张表的顺序

    (from e in dc.mytable where foo == "roo" select new {First= "", Second = e.Roo})  
    .Union(from d in dc.mytable where foo == "bar" select new {First = d.Foo, Second = d.Roo}) 
    

    因此,生成的T-SQL中的常量赋值出现在非第一个表中,那么事情可能会发生,因为T-SQL忽略了后续表的列名。

    注意:联合中的第一个表同时决定列名和类型。所以,不管怎样,最好还是找LinqPad。