关于UNION,INTERSECT和EXCEPT的SqlException

时间:2009-03-16 15:12:17

标签: c# sql linq-to-sql

有人可以帮我解决这个例外吗?我不明白它意味着什么或如何解决它...这是一个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的所有实体或该外来实体满足某些要求的所有实体。

8 个答案:

答案 0 :(得分:9)

从您列出的SQL错误判断,您可能遇到了同样的问题。基本上,当Linq to SQL查询在两个不同的查询中使用Concat或Union扩展方法时,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();

答案 1 :(得分:4)

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

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

答案 2 :(得分:0)

你可以在一个查询中写一下吗?

.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));
}

答案 3 :(得分:0)

  

query = a.Union(b);

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

更新:确定不是

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

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

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

答案 4 :(得分:0)

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

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

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

必须是您的Where次调用之一,这会为您的选择列表添加额外的列。

答案 5 :(得分:0)

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

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

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

答案 6 :(得分:0)

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”的第一个子查询,导致在联合中使用一个字段而不是两个字段,这显然会失败。
我不得不改变第一个查询,以便特殊字段和AnotherSpecialField具有不同的值,与第二个子查询非常相似。

答案 7 :(得分:0)

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