Slick:如何将 SQL LIKE 语句与 SQL IN 语句结合使用
Posted
技术标签:
【中文标题】Slick:如何将 SQL LIKE 语句与 SQL IN 语句结合使用【英文标题】:Slick: How can I combine a SQL LIKE statement with a SQL IN statement 【发布时间】:2018-02-22 07:12:11 【问题描述】:我基本上想用更“光滑”的东西替换下面的代码:
final case class User(firstName: String, lastName: String)
def dbAction(lastNameParts: Seq[String]): SqlStreamingAction[Vector[User], User, Effect]
implicit val getUserResult =
GetResult((r: PositionedResult) =>
val resultSet: ResultSet = r.rs
User(
resultSet.getString(1),
resultSet.getString(2)
)
)
val pattern = orgIds.mkString("|")
sql"""SELECT u.first_name, u.last_name
FROM users u
WHERE last_name ~* $pattern""".as[User]
因此生成的 SQL 将是:
SELECT u.first_name, u.last_name
FROM users u
WHERE last_name ~* '%bar|baz%';
因此,此 dbAction 将返回一个操作,我可以使用该操作来查询列表中包含某些名称部分的所有用户。
所以
dbAction(Seq("bar", "baz"))
将返回一个查询包含字符串“bar”或“baz”(不区分大小写)的所有姓氏的操作。 我找到了一种查询单个模式的方法
val query = for
user <- users if user.lastName like "%bar%"
yield (user.firstName, user.lastName)
我找到了一种查询列表包含的方法
u <- users if u.lastName.inSet(Seq("bar", "baz"))
但找不到一种方法来组合它
编辑:另一种可能解决该问题的方法是通过正则表达式。有没有办法实现类似下面的 SQL 语句:
select * from users where last_name ~ '[\w]*bar[\w]*|[\w]*baz[\w]*';
由于这是一个不同的问题,如何使用正则表达式我为此创建了一个不同的问题: Slick: Is there a way to create a WHERE clause with a regex?
【问题讨论】:
if (user.lastName like "%bar%") && user.lastName.inSet(Seq("bar", "baz"))
不会工作?查看生成的 SQL 语句会很有趣
这不是我想要达到的。我想达到与上面第一个代码部分中的 sql 语句相同的结果。我将在编辑中添加插值的 sql 语句
【参考方案1】:
查看您发布的代码,我认为您不需要将IN
与LIKE
结合起来。我读这个问题是想做一个正则表达式查询。虽然 Slick 不支持开箱即用的 ~*
运算符,但您可以自己添加它。这将为您提供一种使用 提升嵌入 样式的 Slick 查询来执行查询的方法。
为此,您可以使用SimpleExpression
构建器。关于它的文档不多,但起点是参考手册的Scalar Database Functions 页面。
我们要做的是按照以下思路编写一个方法:
def find(names: Seq[String]): DBIO[Seq[String]] =
val pattern = names.mkString("|")
users.filter(_.lastName regexLike pattern).map(_.lastName).result
要获得regexLike
,我们可以丰富(增强,“pimp”)一个字符串列,使其具有regexLike
方法:
implicit class RegexLikeOps(s: Rep[String])
def regexLike(p: Rep[String]): Rep[Boolean] =
val expr = SimpleExpression.binary[String,String,Boolean] (s, p, qb) =>
qb.expr(s)
qb.sqlBuilder += " ~* "
qb.expr(p)
expr.apply(s,p)
implicit class
部分允许编译器构造RegexLikeOps
类,只要它有一个Rep[String]
调用Rep[String]
还没有的方法(即,当要求regexLike
时) .
我们的regexLike
方法将另一个Rep[String]
参数作为模式,然后使用SimpleExpression
构建器安全地构造我们想要使用的SQL。
综合起来我们可以写出:
val program = for
_ <- users.schema.create
_ <- users ++= User("foo") :: User("baz") :: User("bar") :: Nil
result <- find( Seq("baz","bar") )
yield result
println( Await.result(db.run(program), 2.seconds) )
生成的 SQL(在我使用 H2 的测试中)是:
select "last_name" from "app_user" where "last_name" ~* 'baz|bar'
完整代码为:https://github.com/d6y/so46199828
【讨论】:
这解决了我的问题。谢谢你。如果我没看错,您基本上可以创建任何类型的 sql 运算符,对吗?很酷的功能。 @Stoecki 是的,这是一个有用的功能。有点相关,如果你没看过,还有github.com/tminglei/slick-pg,它为 Postgres 添加了其他功能。 @RichardDallaway 是的,我今天找到了这个存储库,但到目前为止还没有深入研究它。但是我会。感谢您指出它。【参考方案2】:只需结合这两个查询条件:
import slick.lifted.Tag
import slick.jdbc.H2Profile.api._
import scala.concurrent.duration._
import scala.concurrent.Await
object Test
final case class User(firstName:String, lastName:String, id:Long = 0l)
class UserTable(tag: Tag) extends Table[User](tag, "user")
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def firstName = column[String]("first_name")
def lastName = column[String]("last_name")
def * = (firstName, lastName, id).mapTo[User]
def main(args:Array[String]):Unit =
val db = Database.forConfig("h2config")
val users = TableQuery[UserTable]
val initialData = Seq(
User("Alex", "Arendar"),
User("David", "Arora"),
User("Dude", "Stoecki"),
User("Alexander", "the Great")
)
Await.result(
db.run(
users.schema.create andThen (users ++= initialData)
), 3 seconds
)
val query1 = for
user <- users if user.firstName like "%Alex%"
yield (user.firstName, user.lastName)
println(query1.result.statements.head)
println(Await.result(db.run(query1.result), 3 seconds))
val query2 = for
user <- users if (user.firstName like "%Alex%") && user.firstName.inSet(Seq("Alex", "David"))
yield (user.firstName, user.lastName)
println(query2.result.statements.head)
println(Await.result(db.run(query2.result), 3 seconds))
db.close()
我的控制台输出是:
select "first_name", "last_name" from "user" where "first_name" like '%Alex%'
Vector((Alex,Arendar), (Alexander,the Great))
select "first_name", "last_name" from "user" where ("first_name" like '%Alex%') and ("first_name" in ('Alex', 'David'))
Vector((Alex,Arendar))
Process finished with exit code 0
如您所见,生成的 SQL 确实包含 like
和 in
部分。
我在这里使用了内存 H2 数据库,但我认为这适用于任何 RDBMS。
【讨论】:
这不是我想要的。我有一个像 Seq("%le%", %av%) 这样的模式列表,并且想要针对这个模式列表进行查询,而不是将类似 SQL 的语句与 SQL-in 语句连接起来。您的数据示例的结果将是 Vector((Alex,Arendar), ("David", "Arora"), (Alexander,the Great))。到目前为止,谢谢。以上是关于Slick:如何将 SQL LIKE 语句与 SQL IN 语句结合使用的主要内容,如果未能解决你的问题,请参考以下文章