InsertAll只有Slick的新记录

时间:2013-08-05 20:01:37

标签: postgresql scala slick

  

[PSQLException:错误:重复键值违反了唯一约束   “dictionary_word_idx”详细信息:Key(word)=(odirane)已经存在。]

我有唯一的索引可以防止任何重复。我想知道如何InsertAll一个包含数千个元素但只有新元素的数组?我正在使用Slick 1.0.1和Postgresql 9.1

修改 我正在尝试以下方法:

    def run = {
      val source = scala.io.Source.fromFile("/home/user/dev/txt/test1.txt")
      val lines = source.mkString
      source.close()

      val words = lines.split("[^\\p{Ll}]").distinct

      database withTransaction {

        val q = for {
            w <- words.toList
            row <- Dictionary if row.word != w  
        } yield w


        Dictionary.autoInc.insertAll(q: _*)
      }


      words.length
    }

但是需要编译:

 polymorphic expression cannot be instantiated to expected type; 
 [error]  found   : [G, T]scala.slick.lifted.Query[G,T] 
 [error]  required: scala.collection.GenTraversableOnce[?] [error]          
  row <- Dictionary if row.word != w

编辑2:

case class Word(id: Option[Long], word:String)

object Dictionary extends Table[Word]("dictionary") {
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def word = column[String]("word")

  def * = id.? ~ word <> (Word, Word.unapply _)
  def dictionary_word_idx = index("dictionary_word_idx", word, unique = true)
  def autoInc = word returning id
}

2 个答案:

答案 0 :(得分:2)

另一种方法是编写原始SQL。 Postgres没有on duplicate ignore的默认方式,但您可以通过几种不同的方式模拟它,如https://dba.stackexchange.com/questions/30499/optimal-way-to-ignore-duplicate-inserts

所示

将其与http://slick.typesafe.com/doc/1.0.0-RC2/sql.html

相结合

编辑:

这是一个例子

def insert(c: String) =
    (Q.u + """INSERT INTO dictionary
        (word)
    SELECT""" +?  c + 
    """WHERE
        NOT EXISTS (
            SELECT word FROM dictionary WHERE word = """ +? c + ")"
    ).execute

val words = lines.split("[^\\p{Ll}]")

words.foreach(insert)

这就是“立刻”的意思吗?我认为这将是最高效的方式,而不是疯狂。

如果它对你来说太慢了,还有另一个建议是创建一个没有唯一约束的临时表,将当前表复制到临时表中,将新单词插入临时表,然后从该表中选择不同的表。这显示在这里:https://stackoverflow.com/a/4070385/375874

但我觉得这样做太过分了。除非你有一些疯狂的要求或什么。

答案 1 :(得分:1)

概念:

def insertAll[T](items: Seq[T]): Seq[Either[(T, Exception), (T, Int)]] = items.map { i =>
  try {
    // Perform an insert supposing returns and int representing the PK on the table
    val pk = …
    Right(i, pk)
  } catch {
    case e: Exception => Left(i, e)
  }
}

执行每个插入操作,然后根据结果返回一个Left或Right对象,该对象保留最终结果的轨迹,并为您提供详细的上下文来解释操作。

修改

让我们假设您的DAO对象如下:

object Dictionary extends Table[Word]("dictionary") {
   // ...
}

其中Word是你的对象模型,而且你已经提供了螺母和螺栓(我可以从你的粘贴代码中推断出来)它应该是(wordsSeq[Word]) :

words.map { w => 
  try {
    Right(w, Dictionary.autoInc.insert(w))
  } catch {
    case e: Exception => Left(w, e)
  }
}

你得到的是一系列Either,它包含了进一步处理的结果。

<强>考虑 我提供的解决方案乐观地尝试对DB执行操作,而不需要根据DB的状态预先过滤列表。 通常,预过滤在大量多用户应用程序中存在问题,前提是您不能假设在执行过滤器后没有人在预过滤列表中添加单词。 陈述更简单:唯一性约束是DBMS提供的一个强大的功能,它比利用重新发明更好。 您上面编辑的解决方案是无解决方案,因为您仍然需要面对可能的PK违规异常。