簡體   English   中英

Slick:LIKE以列為參數的查詢

[英]Slick: LIKE query with column as parameter

我有以下SQL查詢,我想將其映射到Slick

SELECT * FROM   rates
WHERE  '3113212512' LIKE (prefix || '%') and  present = true
ORDER  BY prefix DESC
LIMIT  1;

但是,沒有為String定義like符號:

case class Rate(grid: String, prefix: String, present: Boolean)

class Rates(tag: Tag) extends Table[Rate](tag, "rates") {
  def grid = column[String]("grid", O.PrimaryKey, O.NotNull)
  def prefix = column[String]("prefix", O.NotNull)
  def present = column[Boolean]("present", O.NotNull)

  // Foreign keys
  def ratePlan = foreignKey("rate_plan_fk", ratePlanId, RatePlans)(_.grid)
  def ratePlanId = column[String]("rate_plan_id", O.NotNull)

  def * = (grid, prefix, present) <>(Rate.tupled, Rate.unapply)
}

object Rates extends TableQuery(new Rates(_)) {
  def findActiveRateByRatePlanAndPartialPrefix(ratePlan: String, prefix: String) = {
    DB withSession {
      implicit session: Session =>
        Rates.filter(_.ratePlanId === ratePlan)
          .filter(_.present === true)
          .filter(prefix like _.prefix)
          .sortBy(_.prefix.desc).firstOption
    }
  }
}

顯然,這樣的事情不起作用是合乎邏輯的:

.filter(prefix like _.prefix)

和:

.filter(_.prefix like prefix)

將呈現不正確的SQL,我現在甚至沒有考慮'%'(只有關於前綴的WHERE子句:

(x2."prefix" like '3113212512')

代替:

'3113212512' LIKE (prefix || '%')

當然我可以使用靜態查詢解決這個問題,但我想知道這是否可行?

為清楚起見,這里是數據庫中的一些前綴

31
3113
312532623
31113212

結果預計會有31113212

您可以簡單地以支持的樣式重寫查詢。 我認為這應該是等價的:

.filter(s => (s.prefix === "") || s.prefix.isEmpty || LiteralColumn(prefix) like s.prefix)

如果條件更復雜且需要Case表達式,請參閱http://slick.typesafe.com/doc/2.1.0/sql-to-slick.html#case

如果你還想要|| 運算符您可以這樣定義它:

/** SQL || for String (currently collides with Column[Boolean] || so other name) */
val thisOrThat = SimpleBinaryOperator[String]("||") 

...

.filter(s => LiteralColumn(prefix) like thisOrThat(s.prefix,"%"))

我沒有試過這個,但我看到我們沒有測試SimpleBinaryOperator。 如果不起作用,請在github.com/slick/slick上打開一張票。 可能我們也應該改變它以返回一個類型化的函數。 我為https://github.com/slick/slick/issues/1073添加了一張票。

另請參閱http://slick.typesafe.com/doc/2.1.0/userdefined.html#scalar-database-functions

幸運的是,這里甚至不需要SimpleBinaryOperator。

更新2

實際上一個不需要concat,因為它也可以寫成如下:

filter(s => LiteralColumn(prefix) like (s.prefix ++ "%"))

更新:

在cvogt提示后,這是不使用內部Slick API的最終結果:

        val concat = SimpleBinaryOperator[String]("||")

        Rates.filter(_.ratePlanId === ratePlan)
          .filter(_.present === true)
          .filter(s => LiteralColumn(prefix) like concat(s.prefix, "%"))
          .sortBy(_.prefix.desc).firstOption

完美地給出了:

and ('3113212512' like (x2."prefix" || '%'))

替代方案:

這似乎是可能的。 但請注意,以下方法使用Slick的內部API,並且由於API更改,將來可能不兼容(請參閱cvogt的評論)我使用了Slick 2.1.0。

基本上我創建了一個Slick的擴展,我在其中定義了一個自定義方案來實現所需的結果:

package utils

import scala.language.{higherKinds, implicitConversions}
import scala.slick.ast.ScalaBaseType._
import scala.slick.ast._
import scala.slick.lifted.{Column, ExtensionMethods}

object Helpers {
  implicit class PrefixExtensionMethods[P1](val c: scala.slick.lifted.Column[P1]) extends AnyVal with ExtensionMethods[String, P1] {
    def subPrefixFor[P2, R](prefix: Column[P2])(implicit om: o#arg[String, P2]#to[Boolean, R]) = {
      om.column(Library.Like, prefix.toNode, om.column(new Library.SqlOperator("||"), n, LiteralNode("%")).toNode)
    }
  }
}

現在可以按如下方式使用:

import utils.Helpers._

[...]

  def findActiveRateByRatePlanAndPartialPrefix(ratePlan: String, prefix: String) = {
    DB withSession {
      implicit session: Session =>
        Rates.filter(_.ratePlanId === ratePlan)
          .filter(_.present === true)
          .filter(_.prefix subPrefixFor prefix)
          .sortBy(_.prefix.desc).firstOption
    }
  }

並將呈現正確的結果:

and ('3113212512' like (x2."prefix" || '%'))

筆記:

  1. 我可能有一些更好的命名
  2. 不得不鏈接|| 和使用om.column一樣
  3. 我引入了新的SqlOperator,因為Or運算符渲染或者我需要在Postgres中進行連接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM