【问题标题】:Scala slick: filter using "in" on multiple columnsScala slick:在多列上使用“in”过滤
【发布时间】:2019-03-02 17:30:06
【问题描述】:

假设我有以下表结构:

create table PEOPLE (
    ID integer not null primary key,
    NAME varchar(100) not null
);

create table CHILDREN (
    ID integer not null primary key,
    PARENT_ID_1 integer not null references PERSON (id),
    PARENT_ID_2 integer not null references PERSON (id)
);

并且我想生成每个父母的姓名列表。在 slick 中,我可以写如下内容:

for {
  parent <- people
  child  <- children if {
    parent.id === child.parent_id_1 ||
    parent.id === child.parent_id_2
  }
} yield {
  parent.name
}

这会生成预期的 SQL:

select p.name
from people p, children c
where p.id = c.parent_id_1 or p.id = c.parent_id_2

但是,这并不是最优的:表达式的 OR 部分可能会导致某些 DBMS 的性能极低,即使那里有索引,最终也会执行全表扫描以加入 p.id(请参阅例如this bug report for H2)。一般的问题是查询规划器无法知道分别执行OR 的每一侧并将结果重新连接在一起是否更快,或者只是进行全表扫描[2]。

我想生成如下所示的 SQL,然后 可以按预期使用(主键)索引:

select p.name
from people p, children c
where p.id in (c.parent_id_1, c.parent_id_2)

我的问题是:我怎样才能在 slick 中做到这一点?现有的方法似乎没有提供方法:

我希望能够写的是这样的:

for {
  parent <- people
  child  <- children
  if parent.id in (child.parent_id_1, child.parent_id_2)
} yield {
  p.name
}

但现在不可能。

[1] 我的实际设计比这个复杂一点,但归结为同样的问题。

[2] 一些 DBMS 确实 对简单的情况进行了优化,例如OR-expansion in Oracle.

【问题讨论】:

  • 如果您在两个查询上执行explainORIN),比如说,在 PostgreSQL 中,您会发现它们本质上是相同的(都在条件 id = c.parent_id_1 OR id = c.parent_id_2 下执行)。
  • @LeoC 当然,它在很大程度上取决于 DBMS,但也取决于表的相对大小、索引策略等 - 所以如果没有更多关于您使用的测试数据的信息,很难判断你的陈述是否准确。我的观点是,总的来说,我希望p.id in (..., ...)至少or 一样快,但在许多情况下它可以快得多。因此,我希望尽可能使用in
  • 这个相关的SO Q&A 可能很有趣。答案之一表明,如果列被索引(如您的情况),INOR 在 MySQL 中的执行相同。鉴于 RDBMS 可能以不同的方式实现其查询优化器,我认为最好在实际数据集、模式和 RDBMS 平台上进行一些分析。

标签: scala slick slick-3.0


【解决方案1】:

事实证明,这目前(如 slick 3.2.3)是不可能的,所以我 raised an issue on github 并提交了一个拉取请求以添加此功能。

【讨论】:

    猜你喜欢
    • 2021-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-21
    • 2018-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多