Siehe temporäre Tabelle in Entity Framework-Abfrage

Es gibt eine listnliste im memory von 50.000 Produkt-IDs. Ich möchte alle diese Produkte aus der DB bekommen. Mit dbContext.Products.Where(p => list.contains(p.ID)) erzeugt ein riesiges IN in der SQL – WHERE ID IN (2134,1324543,5675,32451,45735...) , und es dauert ewig. Dies ist zum Teil, weil es time für SQL server braucht, um eine solche große characterfolge zu parsing, und auch der Ausführungsplan ist schlecht. (Ich weiß das aus dem Versuch, stattdessen einen temporären Tisch zu benutzen).

Also habe ich SQLBulkCopy verwendet, um die IDs in eine temporäre Tabelle einzufügen und dann lief

 dbContext.Set<Product>().SqlQuery("SELECT * FROM Products WHERE ID IN (SELECT ID FROM #tmp))" 

Das gab gute performance. Allerdings brauche ich jetzt die Produkte, mit ihren Lieferanten (mehrere für jedes Produkt). Mit einem benutzerdefinierten SQL-Befehl gibt es keinen path, um wieder ein komplexes object, das ich kenne. Also, wie kann ich die Produkte mit ihren Lieferanten, mit dem temporären Tisch?

(Wenn ich irgendwie auf die temporäre Tabelle in LINQ verweisen kann, dann wäre es ok – ich könnte einfach dbContext.Products.Where(p => dbContext.TempTable.Any(t => t.ID==p.ID)) Wenn ich es in einer UDF verweisen könnte, wäre das auch gut – aber du kannst es nicht. Ich kann keinen echten Tisch benutzen, da gleichzeitige Benutzer es in einem inkonsistenten Zustand verlassen würden.)

Vielen Dank

Ich schlage vor, Sie verlängern die Filtertabelle ( TempTable in den Code oben), um etwas wie ein UserId oder SessionId sowie ProductID's zu speichern:

  • Dies wird Ihnen die ganze performance, die Sie nach
  • es wird für gleichzeitige Benutzer arbeiten

Wenn sich diese Filtertabelle viel ändert, dann erwäge es, sie in einer separaten Transaktion (dh einer anderen Instanz von dbContext ) zu dbContext , um zu vermeiden, dass eine Schreibsperre für diese Tabelle länger als nötig ist.

Ich war neugierig, die sql generiert mit Join Syntax anstatt Contains zu erkunden. Hier ist der Code für meinen Test:

 IQueryable<Product> queryable = Uow.ProductRepository.All; List<int> inMemKeys = new int[] { 2134, 1324543, 5675, 32451, 45735 }.ToList(); string sql1 = queryable.Where(p => inMemKeys.Contains(p.ID)).ToString(); string sql2 = queryable.Join(inMemKeys, t => t.ID, pk => pk, (t, pk) => t).ToString(); 

Dies ist die sql generiert mit Enthält (sql1)

 SELECT [extent1].[id] AS [id],...etc FROM [dbo].[products] AS [extent1] WHERE ([extent1].[id] IN (2134, 1324543, 5675, 32451, 45735)) 

Dies ist die sql generiert mit Join:

 SELECT [extent1].[id] AS [id],...etc FROM [dbo].[products] AS [extent1] INNER JOIN (SELECT [unionall3].[c1] AS [c1] FROM (SELECT [unionall2].[c1] AS [c1] FROM (SELECT [unionall1].[c1] AS [c1] FROM (SELECT 2134 AS [c1] FROM (SELECT 1 AS x) AS [singlerowtable1] UNION ALL SELECT 1324543 AS [c1] FROM (SELECT 1 AS x) AS [singlerowtable2]) AS [unionall1] UNION ALL SELECT 5675 AS [c1] FROM (SELECT 1 AS x) AS [singlerowtable3]) AS [unionall2] UNION ALL SELECT 32451 AS [c1] FROM (SELECT 1 AS x) AS [singlerowtable4]) AS [unionall3] UNION ALL SELECT 45735 AS [c1] FROM (SELECT 1 AS x) AS [singlerowtable5]) AS [unionall4] ON [extent1].[id] = [unionall4].[c1] 

So schafft der Sql eine große select-statement mit union alle, um das Äquivalent Ihrer temporären Tabelle zu erstellen, dann verbindet er sich mit dieser Tabelle. Die sql ist ausführlicher, aber es kann auch effizient sein – ich fürchte, ich bin nicht qualifiziert zu sagen.

Während es die Frage nicht beantwortet, wie in der Überschrift dargelegt, zeigt es einen path, um den Riesen IN zu vermeiden. OK … jetzt ist es eine riesige UNION ALL …. sowieso … Ich hoffe, dass dieser Beitrag für einige nützlich ist