C# LINQ到SQL连接生成连接为空的SQL

C# LINQ到SQL连接生成连接为空的SQL,c#,.net,linq,join,C#,.net,Linq,Join,我不擅长Linq表达式,今天我遇到了一个奇怪的问题,如下面的内部连接语句 var orders = (from q in dao.CurrentDBContext.New_OrderForm join d in dao.CurrentDBContext.New_OrderGoodsDetail on q.billNum equals d.billNum select new {

我不擅长Linq表达式,今天我遇到了一个奇怪的问题,如下面的内部连接语句

var orders = (from q in dao.CurrentDBContext.New_OrderForm 
              join d in dao.CurrentDBContext.New_OrderGoodsDetail on q.billNum equals d.billNum
              select new
             {    
                q.billNum,
                q.orderSource,
                q.sourceOddNum    
                d.PPT    
             }
当我跟踪linq语句时,我对
实体框架将linq语句转换为下面的sql语句感到困惑

SELECT 
[Extent1].[billNum] AS [billNum], 
[Extent1].[orderSource] AS [orderSource], 
[Extent1].[sourceOddNum] AS [sourceOddNum], 
[Extent2].[PPT] AS [PPT]
FROM [dbo].[New_OrderForm] AS [Extent1]
INNER JOIN [dbo].[New_OrderGoodsDetail] AS [Extent2] 
           ON ([Extent1].[billNum] = [Extent2].[billNum]) OR 
              (([Extent1].[billNum] IS NULL) AND ([Extent2].[billNum] IS NULL))
您知道为什么下面的SQL段会自动追加吗

或([Extent1].[billNum]为空)和([Extent2].[billNum]为空)


我不期望上面的自动追加,因为它确实降低了SQL性能。有什么建议吗?

根据@Giorgi的回答,
UseDatabaseNullSemantics
标志将不适用于
equals
关键字-只有
=
操作数。因此,为了绕过这个问题并确保billN上的连接um不是
子句的一部分此方法应该有效(与
UseDatabaseNullSemantics
标志结合使用):


这将在不使用
的情况下生成
连接
,似乎Linq翻译
q.billNum等于d.billNum
是这样一种方式,即如果q.billNum和d.billNum都为NULL(在SQL中,NULL永远不等于NULL,因此查询中的OR)时,它还包含一个有效匹配项

如果两个字段都不能为NULL,那么使这两个字段都不可为NULL将是最好的解决方案

如果不是这种情况,您还可以尝试在Linq语句中添加where子句,以指定q.billNum和d.billNum都不能为NULL。如果运气好,Linq将认识到不可能为NULL的值

注意:如果您使用Oracle,您应该检查空字符串和NULL(空字符串相当于NULL)。空字符串在SQL Server中作为有效值应该是可以接受的

由于上述情况没有帮助,您可以尝试自己编写查询。如果我没有弄错的话,它将是以下内容(假设
var
是示例代码中的
列表
——您的查询结果应该与您使用的类相匹配):

StringBuilder查询=新建StringBuilder();
AppendLine(“选择[Extent1].[billNum]作为[billNum],”;
query.AppendLine(“[Extent1].[orderSource]作为[orderSource],”;
AppendLine(“[Extent1].[sourceOddNum]作为[sourceOddNum],”;
query.AppendLine(“[Extent2].[PPT]作为[PPT]”);
AppendLine(“FROM[dbo].[New_OrderForm]AS[Extent1]”;
AppendLine(“内部联接[dbo].[New_ordergoodsdeail]作为[Extent1].[billNum]=[Extent2].[billNum]”上的[Extent2];
List orders=DbContext.Database.SqlQuery(query.ToString()).ToList();

在过去,我使用过类似的解决方法来解决性能问题。

如果您使用的是EF6,请尝试设置

context.Configuration.UseDatabaseNullSemantics = true;
它不会为这些列生成空检查

例如,如果UseDatabaseNullSemantics为true,则(操作数1==操作数2)将分别转换为:(操作数1=操作数2),如果UseDatabaseNullSemantics为false,则分别转换为((操作数1=操作数2)和((操作数1为NULL或操作数2为NULL))或((操作数1为NULL)和(操作数2为NULL))


以下是在无法将
billNum
列更改为不可为空时可以执行的操作

首先,设置@Giorgi提到的选项

class CurrentDBContext
{
    public CurrentDBContext()
    {
        Configuration.UseDatabaseNullSemantics = true;
        // ...
    }
}
然后将LINQ查询更改为不使用
连接
,而是像这样使用简单的
where

var orders = (from q in dao.CurrentDBContext.New_OrderForm 
              from d in dao.CurrentDBContext.New_OrderGoodsDetail
              where q.billNum == d.billNum
              select ...

结果将是与您所显示的完全相同的SQL查询(with
JOIN
!)没有
部分。

q.billNum
d.billNum
的类型是什么?这两种数据类型都是相同的,字符串类型是您的列可以为空的吗?也许
q.billNum等于d.billNum
中的等式假设您也希望
NULL==NULL
匹配?是的,该列可以为空。可能您没有按要求配置它们。您真的需要它们为空吗?因为这就是为什么EF会附加您所要求的条件的原因。添加
where
子句不会删除“extra”
条件(这需要EF对查询表达式执行“全局”优化)。但是,如果知道
billNum
永远不会为
NULL
,SQL Server执行计划可能会受益。嗨,山姆,谢谢你的详细回复,现在可能不可行,因为系统处于联机状态,客户不允许我将字段设置为不可为NULL,我已尝试此“var orders=(来自dao.CurrentDBContext.New_OrderForm.Where(p=>p.billNum!=null)中的q,在dao.CurrentDBContext.New_OrderGoodsDetail.Where(p=>p.billNum!=null)中加入d)在q.billNum等于d.billNum'上,也尝试了“或”从dao.CurrentDBContext.New中的q开始,从dao.CurrentDBContext.New\u OrderGoodsDetail.Where(p=>p.billNum==q.billNum&&p.billNum!=null&&q.billNum!=null)“遗憾的是,这两种方法都没有用处。”(谢谢Sam,这也是一个很好的解决方案,但我必须将sql语句从linq代码中切换出来,这样很多linq代码需要更改,因为很多动态条件都附加在业务逻辑代码中,正如您提到的,最好的解决方案是使这两个字段不可为null,我需要说服客户更改它:)实际测试表明,这并不能解决问题(生成了相同的SQL)。文档中的SQL与问题中的SQL也不同。我不确定
UseDatabaseNullSemantics
真正的作用是什么。确切地说,我进行了测试,看起来开关没有用(这对我来说非常有效。将标志设置为true将删除SQL或在可空字符串比较上创建。谢谢@Giorgi。刚才我安装了EF 6.1.3,但它仍然不起作用。:(抱歉,我在where子句中使用了它,操作数为
=
(根据文档中的示例)。具有
class CurrentDBContext
{
    public CurrentDBContext()
    {
        Configuration.UseDatabaseNullSemantics = true;
        // ...
    }
}
var orders = (from q in dao.CurrentDBContext.New_OrderForm 
              from d in dao.CurrentDBContext.New_OrderGoodsDetail
              where q.billNum == d.billNum
              select ...