4

内存中有一个包含 50,000 个产品 ID 的列表。我想从数据库中获取所有这些产品。使用dbContext.Products.Where(p => list.contains(p.ID))会在 SQL - 中生成一个巨大的 IN WHERE ID IN (2134,1324543,5675,32451,45735...),并且它需要永远。这部分是因为 SQL Server 解析这么大的字符串需要时间,而且执行计划很糟糕。(我通过尝试使用临时表来知道这一点)。

所以我使用 SQLBulkCopy 将 ID 插入到临时表中,然后运行

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

这给了很好的表现。但是,现在我需要产品及其供应商(每种产品有多个)。使用自定义 SQL 命令无法取回我知道的复杂对象。那么如何使用临时表从他们的供应商处获取产品?

(如果我能以某种方式引用 LINQ 中的临时表,那么就可以了 - 我可以这样做dbContext.Products.Where(p => dbContext.TempTable.Any(t => t.ID==p.ID))。如果我可以在 UDF 中引用它也很好 - 但你不能。我不能使用真正的表,因为并发用户会使它处于不一致的状态。)

谢谢

4

2 回答 2

3

我很想探索使用Join语法而不是Contains. 这是我的测试代码:

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();

这是使用 Contains (sql1) 生成的 sql

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

这是使用 Join 生成的 sql:

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]

因此,sql 使用 union all 创建了一个大的选择语句来创建临时表的等效项,然后它连接到该表。sql比较冗长,但可能效率很高——恐怕我没有资格说。

虽然它没有回答标题中提出的问题,但它确实显示了一种避免巨人的方法IN。好的....现在它是一个巨人UNION ALL....无论如何...我希望这个贡献对某些人有用

于 2013-10-21T09:56:51.483 回答
1

我建议您扩展过滤器表(TempTable在上面的代码中)以存储类似 a UserIdorSessionId的内容ProductID's

  • 这将为您提供您所追求的所有性能
  • 它适用于并发用户

如果这个过滤表发生了很大的变化,那么考虑在一个单独的事务中更新它(即一个不同的实例dbContext)以避免在这个表上持有一个写锁超过必要的时间。

于 2013-10-18T09:13:35.730 回答