【问题标题】:SqlException about UNION, INTERSECT and EXCEPT关于 UNION、INTERSECT 和 EXCEPT 的 SqlException
【发布时间】:2010-10-13 15:28:48
【问题描述】:

有人可以帮我解决这个例外吗?我不明白它的含义或如何解决它...这是一个带有以下消息的 SqlException:

使用 UNION、INTERSECT 或 EXCEPT 运算符组合的所有查询必须在其目标列表中具有相同数量的表达式。

我在伪代码中运行查询时得到它,如下所示:

// Some filtering of data
var query = data.Subjects
            .Where(has value)
            .Where(has other value among some set of values);

// More filtering, where I need to have two different options
var a = query
            .Where(some foreign key is null);
var b = query
            .Where(some foreign key is not null)
            .Where(and that foreign key has a property which is what I want);
query = a.Union(b);

// Final filter and then get result as a list
var list = query
            .Where(last requirement)
            .ToList();

如果我删除 a.Union(b) 部分,它会毫无例外地运行。所以我知道错误在那里。但为什么我会得到它?我该如何解决?我在这里做的事情太疯狂了吗?我是否误解了如何使用 Union 的东西?

基本上我所拥有的是一些实体,它们具有其他实体的外键。我需要获取所有将外键设置为null 或该外国实体满足某些要求的实体。

【问题讨论】:

  • 这是一个 SqlException。我更新了问题=)
  • 您确定没有任何查询投射任何内容(选择)?我没有遇到这个,但现在我不记得我所做的联合是 linq2sql 还是针对已经检索到的数据:(
  • 但我不明白这应该是一个问题,因为没有联合查询可以正常工作。意思是,它拥有所需的一切,并且不会因为缺少值等而失败。我之前已经遇到过这个错误,这不是它:p
  • 重新提问;是的,是的。都整理好了吗?我是AFK...
  • (那条评论属于你的答案吗?)

标签: c# sql linq-to-sql


【解决方案1】:

你能不能把它写在一个查询中?

.Where(row => row.ForeignKey == null || row.ForeignKey.SomeCondition);

也有合并表达式的方法 (OrElse),但这并不是微不足道的。

但不确定错误来自哪里!

编辑:还没有测试过,但这在逻辑上应该等同于 UNION:

public static IQueryable<T> WhereAnyOf<T>(
    this IQueryable<T> source,
    params Expression<Func<T, bool>>[] predicates)
{
    if (source == null) throw new ArgumentNullException("source");
    if (predicates == null) throw new ArgumentNullException("predicates");
    if (predicates.Length == 0) return source.Where(row => false);
    if (predicates.Length == 1) return source.Where(predicates[0]);

    var param = Expression.Parameter(typeof(T), "row");
    Expression body = Expression.Invoke(predicates[0], param);
    for (int i = 1; i < predicates.Length; i++)
    {
        body = Expression.OrElse(body,
            Expression.Invoke(predicates[i], param));
    }
    return source.Where(Expression.Lambda<Func<T, bool>>(body, param));
}

【讨论】:

  • 问题是它不仅仅是 SomeCondition,还有 SomeCondition 以及 WhereBetween 方法的使用,你不久前帮助过我,诸如此类。 (stackoverflow.com/questions/553443/…)
  • 也许您可以将 WhereAnyOf(上图)与 WhereBetween 的主要部分(或返回谓词的 WhereBetween 版本,而不是调用 source.Where(predicate))一起使用
  • 这看起来很有希望而且很有趣……明天第一件事就试试看! (工作结束了,要赶公共汽车!)
  • 将 OrElse 与 AndAlso 交换是否会使其成为 WhereAllOf?并且会直接返回 Expression.Lambda-and-so-forth 而不是将它放在哪里,从而可以更轻松地组合它们?就像我可以做两个 WhereAllOf,然后将它们与 WhereAnyOf 结合,然后做 Where?
  • 你为什么用for而不是foreach
【解决方案2】:

查询 = a.Union(b);

改变捕获的变量不是一个好主意...可能是错误的原因。

更新:没关系

这是另一个想法。提示在错误消息中。

var a = query
         .Where(some foreign key is null)
         .Select(x => x);

或者通过添加另一个“假”来玩,直到它们变得相等:)

【讨论】:

  • 你是对的,重复使用会带来混乱,但请注意,这些都没有被捕获......
  • 他的意思是 - 有 var query2 = a.Union(b) 并在下游使用 query2。但是,我非常怀疑在这种情况下是否是问题所在,因为它实际上并没有被捕获。
  • @svish:你能测试一下 marc 说的话吗?这就是我的意思。我怀疑它是 IQueryable 可能会导致问题,只是预感:)
  • 我看不出这里应该是什么问题。我知道 foreach 循环等问题,但在这里我只是更新变量类型?与string s = "test"; s = s.ToUpper(); 没有太大区别。还是我错了?
  • 我不明白。添加一个 Select 没有任何帮助?
【解决方案3】:

由于这看起来像是生成的 SQL 的问题,您应该尝试使用 SQL Profiler,或使用 this code for DebuggerWritter class 将 SQL 写入 Visual Studio 中的输出窗口。

SQL 错误通常是由于为 UNION 检索到的字段与 2 个查询不同。例如,如果第一个查询可能有 3 个字段,但第二个查询有 4 个字段,则会出现此错误。因此,在这种情况下,查看生成的 SQL 肯定会有所帮助。

【讨论】:

    【解决方案4】:

    我会调用data.GetCommand(query) 并分析生成的DbCommand(尤其是生成的SQL 字符串)。这应该可以为您提供有关问题所在的线索。

    任何地方都没有投影,所以我希望两个目标列表是相同的。

    您可以尝试将您的查询减少到一个仍然不起作用的较小的查询。从query.Union(query) 开始(这至少应该有效)。然后一一添加您的 Where 调用,看看它何时停止工作。

    它必须是您的 Where 调用之一,它将额外的列添加到您的选择列表中。

    【讨论】:

    • 已经看过sql了,虽然我在调试的时候得到了。将其复制并粘贴到新查询中的 sql 管理器中。我在那里得到同样的错误。问题是查询很大,而且我读sql不是很稳定...
    【解决方案5】:

    您是否有机会将值传递给变量中的“选择”端,或者您是否多次返回相同的字段? SP1 引入了一个错误,它试图“优化”此类事情,这可能导致联合查询中断(由于查询部分“优化”了不同的传入参数)。

    如果您发布实际查询而不是伪代码,则更容易识别是否是这种情况。

    (如果是这种情况,一种解决方法是先实现各个部分,然后执行客户端 (L2O) 联合)。

    【讨论】:

      【解决方案6】:

      从您列出的 SQL 错误来看,您可能遇到了与我相同的问题。基本上,当在两个不同查询上使用 Concat 或 Union 扩展方法的 Linq to SQL 查询时,Linq to SQL 中似乎存在一个错误,该错误会分别优化每个投影,而不考虑投影必须保持不变才能完成的事实SQL 联合。

      参考资料:

      LINQ to SQL produces incorrect TSQL when using UNION or CONCAT

      Linq to SQL Union Same Fieldname generating Error

      如果这恰好是您的问题,我已经找到了一个适合我的解决方案,如下所示。

      var queryA = 
          from a in context.TableA
          select new 
          {
              id,
              name,
              onlyInTableA,
          }
      
      var queryB = 
          from b in context.TableB
          let onlyInTableA = default(string)
          select new 
          {
              id,
              name,
              onlyInTableA,
          }
      
      var results = queryA.Union(queryB).ToList();
      

      【讨论】:

      • 请注意,即使实际字面值相同,您也必须为每个计算值定义一个新的 let 语句才能使该技术发挥作用。到目前为止,这是我发现的唯一合理的解决方案。
      • 这是一个可怕的消息,似乎 .NET Framework 4.0 不仅没有纠正这个问题,而且还压制了我上面提出的解决方法。
      • 我同意,.NET 4 甚至优化了 let 语句。然而,一个更难看的 hack 似乎阻止了 v4 中的优化器是对 let 语句执行一个不改变其结果的毫无价值的操作,比如连接一个空字符串或添加零。在这种情况下,类似下面的方法可能会起作用: let onlyInTableA = default(string) + default(string)
      • @Mark - 感谢您的提醒。如果我在升级到 .NET 4.0 后发现自己走投无路,我将不得不记住那个 hack。我希望 EF 最终会对此有所改进。
      • 通过在投影两次的 ID 中添加零来解决我的问题。
      【解决方案7】:

      jpierson 正确总结了问题。
      我也遇到了问题,这次是由select语句中的一些文字引起的:
      Dim results = (From t in TestDataContext.Table1 _
      Where t.ID = WantedID _
      Select t.name, SpecialField = 0, AnotherSpecialField = 0, t.Address).Union _
      From t in TestDataContext.Table1 _
      Where t.SecondID = WantedSecondID _
      Select t.name, SpecialField = 1, AnotherSpecialField = 0, t.Address)

      优化了“SpecialField = 0”和“AnotherSpecialField = 0”的第一个子查询,导致union中使用一个字段而不是两个,显然会失败。
      我必须更改第一个查询,以便 SpecialField 和 AnotherSpecialField 具有不同的值,就像在第二个子查询中一样。

      【讨论】:

        【解决方案8】:

        好吧,我对此有疑问。使用 Sql 08 我有两个表函数,在这两种情况下都返回一个 int 和一个字符串。我创建了一个复杂的对象并使用 linq 来尝试 UNION。有一个 IEqualityComparer 进行比较。一切编译正常,但因不支持的重载而崩溃。好的,我意识到所讨论的问题似乎有点延迟执行。所以我得到了集合,并放置了 ToList(),然后执行 UNION,一切都很好。不确定这是否有帮助,但它对我有用

        【讨论】:

        • 嗯,我知道。但是你正在做的是在本地做 UNION。根据您的情况,这可能是一个好主意,也可能不是一个好主意。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-28
        相关资源
        最近更新 更多