2016-03-07 203 views
21

我在LinqPad中試驗過不同的查詢。表Lot與列Side char(1)。當我寫的LINQ to SQL查詢Lots.Where(l => l.Side == 'A'),它產生下面的SQLWhere Where(l => l.Side =='A')與Where Where(l => l.Side.Equals('A')

-- 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

使用intsmallintvarchar列有與==.Equals產生的SQL沒有什麼區別,爲什麼char(1)和相應的C#類型char不同?

有什麼辦法來預測給定的列類型是否會產生與兩種形式的相等性檢查不同的SQL?

編輯:

我已經檢查通過MS SQL支持所有類型的,只有char(1)nchar(1)顯示此行爲。在LinqToSql中,兩者都由System.Char類型表示。如果這是一個深思熟慮的決定,那麼我本來期望在binary(1)相同的行爲,這可能是System.Byte表示(反而是System.Linq.Binary1長度

編輯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通過。

The dbml and table creation script are in this gist.

+0

你可以發佈DBML映射嗎? – usr

+0

@usr這是通過LINQPad,爲此我不知道如何檢索映射。 – RoadieRich

回答

12

有一些documentation一下:

不匹配在SQL Server: 固定長度的類型。 Transact-SQL區分Unicode和非Unicode類別,並且在每個類別中都有三種不同類型:固定長度的nchar/char,可變長度的nvarchar/varchar和較大的ntext/text。固定長度字符類型可以映射到CLR System.Char類型以檢索字符,但它們在轉換和行爲上並不真正對應於相同的類型。

而且L2S源代碼只有一個使用字符串文字"UNICODE"地方:

enter image description here

看來,對於展現出來的功能的必要前提是與SqlUnary語法樹節點類型Convert

enter image description here

我不知道你如何設法滿足IsNumeric條件。我認爲你有一個類型不匹配。該列是否真的映射爲System.Char

調用Equals可能不會觸發此代碼路徑。這可能是一個L2S錯誤。

Equals被翻譯成多個地方的源代碼。這裏是其中的一個:

enter image description here

它看起來像這樣繞過任何參數轉換。它並不關心論點是什麼。這可能會因各種查詢而失敗(所以這可能是一個錯誤)。我想知道如果你寫l.Side.Equals(1.2m)會發生什麼。我想這從字面上翻譯成SQL。


我現在已經轉載了它。將該列映射到string。這修復了生成的SQL。執行計劃顯示可以使用正在生成的SQL進行索引查找。

+0

'l.Side.Equals(1.2m)'在生成任何SQL之前拋出一個'InvalidCastException'(無法從類型'System.Decimal'轉換爲'System.Char'。) - 至少,沒有任何可見的sql挖入內部。 – RoadieRich

+0

我應該補充一點,我使用LINQPad來查看創建的SQL,如果它有任何區別的話。我假設LINQPad會使用系統L2S庫,但我意識到這個假設可能有缺陷。 – RoadieRich

+0

LinqPad可能使用未修改的二進制文件。您可以通過L2S的運行時元數據系統查詢映射。有關於此的MSDN文檔。但是如果你創建了一個VS項目來運行你的測試會更好。現在我不知道如何繼續LinqPad可以使用各種奇怪的映射和入侵技術。 – usr