如何使用Slick编写带INTERSECT的SQL查询?
Slick官方目前没有内置intersect方法,但可以通过以下两种方式实现需求:
方法一:子查询+IN子句(通用变通方案)
核心思路是将其中一个查询作为子查询,通过IN筛选同时存在于两个结果集的记录。
匹配单一字段(如主键)
如果只需通过某个唯一字段(比如咖啡名称)匹配交集记录:
val q1 = coffees.filter(_.price < 8.0) val q2 = coffees.filter(_.price > 9.0) // 筛选q1中存在于q2的咖啡记录 val intersectQuery = q1.filter(coffee => q2.map(_.cofName).contains(coffee.cofName))
对应的简化SQL:
SELECT x8."COF_NAME", x8."SUP_ID", x8."PRICE", x8."SALES", x8."TOTAL"
FROM "COFFEES" x8
WHERE x8."PRICE" < 8.0
AND x8."COF_NAME" IN (
SELECT x9."COF_NAME" FROM "COFFEES" x9 WHERE x9."PRICE" > 9.0
)
匹配所有字段(完全相同的记录)
如果需要确保两条记录的所有字段完全一致才视为交集,可以用元组打包所有字段:
val q1Columns = q1.map(c => (c.cofName, c.supId, c.price, c.sales, c.total)) val q2Columns = q2.map(c => (c.cofName, c.supId, c.price, c.sales, c.total)) val intersectQuery = q1.filter(c => q2Columns.contains((c.cofName, c.supId, c.price, c.sales, c.total)))
方法二:自定义扩展intersect方法(原生SQL支持)
通过扩展Slick的Query类,直接操作AST节点生成原生INTERSECT SQL,语法更贴近原生SQL写法。
首先定义扩展类:
import slick.jdbc.JdbcProfile import slick.lifted.Query implicit class QueryIntersectExtension[E, U, C[_]](q1: Query[E, U, C]) { def intersect(profile: JdbcProfile)(q2: Query[E, U, C]): Query[E, U, C] = { import profile.api._ Query( new slick.ast.Union(q1.toNode, q2.toNode, isUnionAll = false, isIntersect = true) ) } }
使用方式:
val q1 = coffees.filter(_.price < 8.0) val q2 = coffees.filter(_.price > 9.0) // 替换为你的实际JdbcProfile实例 val intersectQuery = q1.intersect(profile)(q2)
该方法会生成你期望的原生INTERSECT SQL:
SELECT x8."COF_NAME", x8."SUP_ID", x8."PRICE", x8."SALES", x8."TOTAL"
FROM "COFFEES" x8 WHERE x8."PRICE" < 8.0
INTERSECT
SELECT x9."COF_NAME", x9."SUP_ID", x9."PRICE", x9."SALES", x9."TOTAL"
FROM "COFFEES" x9 WHERE x9."PRICE" > 9.0
注意事项
- 方法二依赖数据库对
INTERSECT的支持(如PostgreSQL、MySQL 8.0+等),性能更优 - 方法一兼容性更强,适用于所有支持子查询的数据库
- 示例中的q1和q2条件无实际交集,仅作语法演示,使用时需替换为真实业务条件
内容的提问来源于stack exchange,提问作者Guillem I

