从Where(l => l.Side ==’A’)vs Where(l => l.Side.Equals(’A’)生成不同的SQL
我一直在试验LinqPad中的查询。 我们有一个表格Lot
和一个Side char(1)
。 当我写一个linq到sql查询Lots.Where(l => l.Side == 'A')
,它产生以下SQL
-- Region Parameters DECLARE @p0 Int = 65 -- EndRegion SELECT ..., [t0].[Side], ... FROM [Lot] AS [t0] WHERE UNICODE([t0].[Side]) = @p0
但是,使用Lots.Where(l => l.Side.Equals('A'))
,它会产生
-- Region Parameters DECLARE @p0 Char(1) = 'A' -- EndRegion SELECT ..., [t0].[Side], ... FROM [Lot] AS [t0] WHERE [t0].[Side] = @p0
它会出现(尽管是天真的)检查,后者会略微加快,因为它不需要调用UNICODE
。
使用int
, smallint
或varchar
列,生成的SQL与==
或.Equals
之间没有区别,为什么char(1)
和相应的C#类型char
不同?
有没有办法预测给定的列类型是否会产生两种forms的相等性检查?
编辑:
我检查了MS SQL支持的每种类型,只有char(1)
和nchar(1)
显示此行为。 两者都由System.Char
类型在LinqToSql中表示。 如果这是一个刻意的决定,那么我会期望binary(1)
上的相同行为,它可以由System.Byte
表示(但是System.Linq.Binary
的长度为1
。
编辑2:如果它是相关的,我使用LINQPad来查看创建的SQL。 我假设Linqpad会使用系统的LinqToSQL,但我今天意识到这个假设可能存在缺陷。
编辑3:我运行了一个快速VS项目来测试系统LinqToSQL,我们得到了相同的结果:
码:
static void Main(string[] args) { var db = new DataClasses1DataContext {Log = Console.Out}; Console.Out.WriteLine("l.Side == 'A'"); Console.Out.WriteLine("============="); Console.Out.WriteLine(); foreach (Lot ll in db.Lots.Where(l => l.Side == 'A')) { break; } Console.Out.WriteLine(); Console.Out.WriteLine("---------------------------------------"); Console.Out.WriteLine(); Console.Out.WriteLine("l.Side.Equals('A')"); Console.Out.WriteLine("=================="); Console.Out.WriteLine(); foreach (Lot ll in db.Lots.Where(l => l.Side.Equals('A'))) { break; } Console.In.Read(); }
输出:
l.Side == 'A' ============= SELECT ..., [t0].[Side], ... FROM [dbo].[Lot] AS [t0] WHERE UNICODE([t0].[Side]) = @p0 -- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [65] -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.6.1532.0 --------------------------------------- l.Side.Equals('A') ================== SELECT ..., [t0].[Side], ... FROM [dbo].[Lot] AS [t0] WHERE [t0].[Side] = @p0 -- @p0: Input Char (Size = 1; Prec = 0; Scale = 0) [A] -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.6.1532.0
有趣的是,在== 'A'
版本中,参数作为int
传递,而在.Equals
版本中,它作为char
传递。
dbml和表创建脚本在这个要点中。
有一些关于此的文档 :
SQL Server中的不匹配:固定长度的字符类型。 Transact-SQL区分Unicode和非Unicode类别,并且在每个类别中有三种不同的类型:固定长度nchar / char,可变长度nvarchar / varchar和更大的ntext / text。 固定长度字符类型可以映射到CLR System.Char类型以检索字符,但它们实际上并不对应于转换和行为中的相同类型。
并且L2S源代码只有一个使用字符串文字"UNICODE"
:
看来函数显示的必要前提条件是类型为Convert
的SqlUnary
语法树节点:
我不知道你是如何设法满足IsNumeric
条件的。 我认为你的类型不匹配。 该列是否真的映射为System.Char
?
Equals
调用可能不会触发此代码路径。 这可能是一个L2S错误。
Equals
在源代码中的多个位置进行转换。 这是其中之一:
看起来这绕过了任何参数转换。 它并不关心论证是什么。 这可能会因各种查询而失败(因此可能是一个错误)。 我想知道如果你写l.Side.Equals(1.2m)
会发生什么。 我想这可以转化为SQL。
我现在转载它。 将列映射到string
。 这可以修复生成的SQL。 执行计划显示可以使用正在生成的SQL进行索引搜索。