【问题标题】:Get distinct results from database that has a "dictionary"从具有“字典”的数据库中获取不同的结果
【发布时间】:2020-08-03 07:58:17
【问题描述】:

我有一个带有这样“字典”的数据库:

Id (INT) | Key (VARCHAR) | Value (VALUE) | ForeignKey (INT?)
------------------------------------------------------------
1        | foo           | bar           | 23
2        | bar           | foo           | NULL
3        | foobar        | value         | NULL
4        | foobar        | othervalue    | 47

现在我想获取特定外键的所有键和值,以及所有为 NULL 的外键,因此结果如下所示:

ForeignKey = 23:           ForeignKey = 47:

foo    | bar               bar    | foo
bar    | foo               foobar | othervalue
foobar | value

最初我尝试过这种方法:

dbContext.Table
    .Where(t => t.ForeignKey == 47 || t.ForeignKey == null)

但这给了我两次foobar

然后我考虑了多个请求并将结果联合起来:

var t1 = _dbContext.Table
    .Where(t => t.ForeignKey == 47);

var t2 = _dbContext.Table
    .Where(t => t.ForeignKey == null && !t1.Any(tt => tt.Key == t.Key));

var final = t1.Union(t2);

这看似可行,但它创建的 SQL(粗略)似乎过多,其中包含三个 SELECT、三个 WHERE 和一个 UNION

SELECT [t1].[Id], [t1].[Key], [t1].[Value], [t1].ForeignKey
FROM [Table] AS [t1]
WHERE [t1].[ForeignKey] = 47
UNION
SELECT [t2].[Id], [t2].[Key], [t2].[Value], [t2].ForeignKey
FROM [Table] AS [t2]
WHERE [t2].[ForeignKey] IS NULL AND NOT (EXISTS (
    SELECT 1
    FROM [Table] AS [t3]
    WHERE ([t3].[ForeignKey] = 47) AND ([t3].[Key] = [t2].[Key])))

我只是有一种直觉,“必须有更好的方法”......所以,有吗?如何获取特定外键的键和值,以及尚未获取的 NULL 键?

【问题讨论】:

  • 对于“ForeignKey = 47”,您还应该有“foobar | value”...
  • @TheImpaler 不,这就是重点......对于“ForeignKey = 47”我想要 value = “othervalue”的行
  • "...还有所有为 NULL 的外键..." - 你说过,所以应该包括“foobar | value”。我读错了什么?
  • 按照我的理解,具有空外键的项目代表默认值。如果给定外部 ID 存在具有键的行,则该行的值优先。但是在没有覆盖该键的值的情况下,OP 希望结果中包含该键及其默认值。
  • @StriplingWarrior 这很有道理。不幸的是,我无法真正理解它。 30 分钟后投票结束,没有任何说明。

标签: sql linq optimization entity-framework-core


【解决方案1】:

您看到的 SQL 对我来说似乎并不过分。除非您对此进行了测试并发现它的性能差到无法接受,否则我不一定会花太多时间来优化它。

不过,这里有另一种方法,可能更可取。您必须进行一些测试,看看它是否会产生更好的 SQL、执行计划和性能。大多数情况下,我会选择其他开发人员更容易理解的方法。

var final = dbContext.Table.Where(t => t.ForeignKey == 47 || t.ForeignKey == null)
    .GroupBy(t => t.Key)
    .Select(g => g.OrderBy(t => t.ForeignKey == null ? 1 : 0).FirstOrDefault());

【讨论】:

  • 我喜欢你的查询的想法。我之前尝试过类似的东西,但没有得到它的工作。不幸的是,您的查询也不起作用,但会产生以下异常:The LINQ expression '(GroupByShaperExpression:\r\nKeySelector: (c.Key), \r\nElementSelector:(EntityShaperExpression: \r\n EntityType: Table\r\n ValueBufferExpression: \r\n (ProjectionBindingExpression: EmptyProjectionMember)\r\n IsNullable: False\r\n)\r\n)\r\n .OrderBy(t => t.ForeignKey == null ? 1 : 0)' could not be translated
  • 是的,基于 cmets here 听起来不同版本的 EF 处理这个问题的方式不同,而且没有一个可能以理想的方式处理它。除非性能导致问题,否则我可能会坚持使用您的查询。
  • 我设法优化了我的原始查询并最终避免了生成的一些LEFT JOIN。由于缺乏对这个问题的反馈,而且它似乎变成了基于意见的,我会接受你的回答并留在那里。感谢您的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 2016-10-18
  • 2019-05-29
  • 2015-11-09
  • 2019-07-31
相关资源
最近更新 更多