簡體   English   中英

SQL 視圖上的 LINQ 選擇得到錯誤的答案

[英]LINQ select on a SQL View gets wrong answer

我有一個生成 8 列響應的 SQL 視圖。 它相當復雜,所以我不會在這里列出它,它不會對我試圖理解的問題增加太多。

當我直接使用此查詢在 SQL Manager 中查詢視圖時

SELECT * FROM [GPPS].[dbo].[PartIndex]
WHERE CategoryNameId = 182 AND CycleId = 13 AND BasketId = 304 AND MarketId = 8
ORDER BY ProductNameId

我得到了預期的結果(前兩行很重要)並且 ProductNameId 列在結果中排​​名第 7

                            vvvvv
                            =====   
218   13    8   304 182 124 32575   162.84
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 125 32577   156.13
218   13    8   304 182 127 32578   605.84
218   13    8   304 182 130 32579   141.51

當我對視圖執行以下 LINQ 時

PartIndexes.Where(x => x.CategoryNameId == 182 
                       && x.CycleId == 13 
                       && x.BasketId == 304 
                       && x.MarketId == 8)
           .ToList()
           .OrderBy(x => x.ProductNameId);

我實際上得到的是:

                            vvvvv
                            ===== 
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 125 32577   156.13
218   13    8   304 182 127 32578   605.84
218   13    8   304 182 130 32579   141.51

如您所見,前兩個條目是相同的,並且 ID(32575 和 32576)的區別已經丟失。

在視圖上運行 LINQ 查詢時查看 SQL 探查器會生成以下 SQL

SELECT 
[Extent1].[SetNameId] AS [SetNameId], 
[Extent1].[CycleId] AS [CycleId], 
[Extent1].[MarketId] AS [MarketId], 
[Extent1].[BasketId] AS [BasketId], 
[Extent1].[CategoryNameId] AS [CategoryNameId], 
[Extent1].[ProductNameId] AS [ProductNameId], 
[Extent1].[PartId] AS [PartId], 
[Extent1].[Total] AS [Total]
FROM (SELECT 
  [PartIndex].[SetNameId] AS [SetNameId], 
  [PartIndex].[CycleId] AS [CycleId], 
  [PartIndex].[MarketId] AS [MarketId], 
  [PartIndex].[BasketId] AS [BasketId], 
  [PartIndex].[CategoryNameId] AS [CategoryNameId], 
  [PartIndex].[ProductNameId] AS [ProductNameId], 
  [PartIndex].[PartId] AS [PartId], 
  [PartIndex].[Total] AS [Total]
  FROM [dbo].[PartIndex] AS [PartIndex]) AS [Extent1]
WHERE (182 = [Extent1].[CategoryNameId]) AND (13 = [Extent1].[CycleId]) AND (304 =  [Extent1].[BasketId]) AND (8 = [Extent1].[MarketId])

然后當我直接在 SQL 管理器中執行它時,我得到了想要的結果:

218   13    8   304 182 124 32575   162.84
218   13    8   304 182 124 32576   184.08
218   13    8   304 182 125 32577   156.13
218   13    8   304 182 127 32578   605.84
218   13    8   304 182 130 32579   141.51

任何人都知道這里可能發生什么,以及為什么執行 LINQ 請求返回的結果與 SQL 中的結果不同,但在執行由 LINQ 查詢生成的 SQL 時,它返回所需的結果?

直接使用 SQL 時 LINQ 在正確呈現時不做的事情是什么?

您的問題與此類似: Using a view with no primary key with Entity

指定密鑰s,使您的行都是唯一的。 您可以通過屬性在實體映射上指定這些鍵:

public class YearlySalesOnEachCountry
{        
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }

    public long SalesCount { get; set; }      
    public decimal TotalSales { get; set; }
}

或者您可以通過代碼方法來完成:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);    
    modelBuilder.Entity<YearlySalesOnEachCountry>()
           .HasKey(x => new { x.CountryId, x.OrYear });     
}

如果實體框架為視圖選擇的鍵不是唯一的,則可能無法正確返回結果。 對於某些視圖,無法定義正確的鍵(包含所有非空列)並且對使用視圖沒有任何好處。

對於這些情況,請考慮使用 EF Edmx 界面手動定義密鑰:

 1) Any existing non-null field or 

 2) A separately added column "key" such as:

     select 1 as EfKey -- Must use with AsNoTracking()

這兩種方法都需要對每個查詢(鏈接)使用“AsNoTracking()”。

使用 AsNoTracking() 信號 EF 繞過其基於密鑰的記錄緩存機制。 如果沒有 AsNoTracking(),結果可能會被損壞,其中包含重復的行。

使用 (2) 的一個優點是,如果忘記了 AsNoTracking(),那么結果應該很糟糕,很容易被注意到。

避免使用 row_number() 的任何變體,因為它通常會妨礙 SQL 引擎中謂詞的有效使用。 這可以通過查看帶有謂詞的 SQL 實際計划來驗證。 (抱歉,這是我最初發布的建議。)

   -- Avoid!
   select row_number() over (order by (select null)) as RowId,
          ...

希望 EF 團隊會考慮為視圖提供一個選項,允許禁用關鍵要求並自動使用 AsNoTracking() 與每個查詢。

實際上,@stanke 的問題給了我一個想法。

我實際上稍微改變了視圖以包含另一列,以便可以唯一地標識每條記錄。

我實際上不需要結果表中的列值,但它確實幫助 LINQ 在查詢時保持記錄的唯一性。 看起來 SQL 本身就可以很好地做到這一點,但 LINQ 需要一些幫助來保持記錄的不同。

它現在可以在 SQL 和 LINQ 中按預期工作

我加了

ISNULL(CONVERT(VARCHAR(50), NEWID()), '') AS Pkid 

作為我認為解決這個問題的第一列。

在前兩行的第 6 列,您具有相同的值 - 124,如果這是此視圖中的某個外鍵,則可能會導致一行被過濾。 我在使用數據表加載函數時遇到了類似的情況,因為它在將檢索到的數據加載到數據表時應用了約束。 嘗試從 linq 到 sql 模式中刪除密鑰。

我沒有看到您的 LINQ 有任何問題。

您如何填充 PartIndexes(ORM、SqlCommand 等)?

也許您在 DAL 或 ORM 映射中的邏輯被搞亂了,這搞亂了 PartIndexes 中存儲的內容。

假設您正在使用EF (entity framework) 如果是這樣,請在 IQueryable 上使用.AsNoTracking() ,否則您可能會得到緩存的結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM