如何使 HoneySQL 处理顺序作为复合键?

Posted

技术标签:

【中文标题】如何使 HoneySQL 处理顺序作为复合键?【英文标题】:How can I make HoneySQL handle order by as a compound key? 【发布时间】:2016-10-30 22:01:43 【问题描述】:

请注意,输出已被“风格化”,因此在 SO 上阅读效果更好。

我有什么...

(sql/format 
  (-> 
    (sqlh/select :*) 
    (sqlh/from :event) 
    (sqlh/merge-where [:in :field_id ["1673576", "1945627", "1338971"]]) 
    (sqlh/merge-where [:in :layer ["fha.abs" "fha.rank" "fha.true-color"]])
    (sqlh/merge-order-by :field_id)
    (sqlh/merge-order-by :layer)
    (sqlh/merge-order-by :event_date)
    (sqlh/limit 5)))
=>
["SELECT * 
  FROM event 
  WHERE ((field_id in (?, ?, ?)) AND (layer in (?, ?, ?))) 
  ORDER BY field_id, layer, event_date 
  LIMIT ?"
 "1673576"
 "1945627"
 "1338971"
 "fha.abs"
 "fha.rank"
 "fha.true-color"
 5]

我想要什么...

(sql/format 
  (-> 
    (sqlh/select :*) 
    (sqlh/from :event) 
    (sqlh/merge-where [:in :field_id ["1673576", "1945627", "1338971"]]) 
    (sqlh/merge-where [:in :layer ["fha.abs" "fha.rank" "fha.true-color"]])
    ;;; this doesn't work, but is conceptually what I'm looking for
    (sqlh/merge-order-by [:field_id :layer :event_date])
    (sqlh/limit 5)))
=>
["SELECT * 
  FROM event 
  WHERE ((field_id in (?, ?, ?)) AND (layer in (?, ?, ?))) 
  ORDER BY (field_id, layer, event_date) 
  LIMIT ?"
 "1673576"
 "1945627"
 "1338971"
 "fha.abs"
 "fha.rank"
 "fha.true-color"
 5]

如何让 HoneySQL 发出将我的 order by 子句视为表本身用作主键的复合键的 SQL?

似乎 HoneySQL 应该能够做到这一点,因为当在 where 子句中遇到相同的挑战时,它“做正确的事”......

(sql/format
  (->
    (sqlh/select :*)
    (sqlh/from :event)
    (sqlh/merge-where [:= [:field_id :layer :event_date] ["1338971" "fha.abs" (c/from-string "2011-08-02T10:54:55-07")]])))
=>
["SELECT * FROM event WHERE (field_id, layer, event_date) = (?, ?, ?)"
 "1338971"
 "fha.abs"
 #object[org.joda.time.DateTime 0xe59f807 "2011-08-02T17:54:55.000Z"]]

【问题讨论】:

第二个生成的查询与第一个有什么不同? @AshishNegi 在第二个查询中,order by 子句位于一个元组而不是 3 个单独的字段上。最终,这将允许我在单个语句中反转排序顺序。 是的,但不是都给出相同的结果?...无论您有元组还是单个字段。 @AshishNegi 它给出了相同的初始结果,这是正确的,但是当我期待我想要做的事情时;实现一个只需要 LIMIT 而不是 OFFSET 的分页解决方案,我需要能够将这 3 个不同的字段视为一个单元。 HoneySQL 在 where 子句中对此做了正确的事情(我在问题中添加了这个),我正在寻找 order-by 中的等效行为。 也许你应该尝试提出关于限制/偏移的真正问题,以及他们如何在有/没有元组的情况下给出不同的结果.. 【参考方案1】:

首先您需要查看order-by 上的格式行为

(sql/format :order-by [:c1 :c2]) 
=> ["ORDER BY c1, c2"]
(sql/format :order-by [[:c1 :desc] :c2])
=> ["ORDER BY c1 DESC, c2"]

这就是将要生成的关于order-by的结构。

如果您查看宏defhelper,它将做两件事。

    规范类型的defrecord 定义一个函数来调用多重方法

(do (defmethod build-clause :order-by [_ m fields] (assoc m :order-by (collify fields))) (defn order-by [& args__14903__auto__] (let [[m__14904__auto__ args__14903__auto__] (if (plain-map? (first args__14903__auto__)) [(first args__14903__auto__) (rest args__14903__auto__)] [ args__14903__auto__])] (build-clause :order-by m__14904__auto__ args__14903__auto__))) (alter-meta! #'order-by assoc :arglists '([fields] [m fields])))

collify 非常简单。

 (defn collify [x]
     (if (coll? x) x [x]))

所以,我们需要查看defn order-by 函数。 当您拨打(sqlh/merge-order-by [:a :b])时,

args__14903__auto__ = '( [:a :b])

第一个if 将创建两个变量m__14904__auto__ = args__14903__auto__ = (rest args__14903__auto__) = ([:a :b])

所以,我猜merge-order-by函数是错误的。

我这样解决你的问题。

(sql/format
  (->
    (sqlh/select :*)
    (sqlh/from :event)
    (sqlh/merge-where [:in :field_id ["1673576", "1945627", "1338971"]])
    (sqlh/merge-where [:in :layer ["fha.abs" "fha.rank" "fha.true-color"]])
    (sqlh/merge-order-by [:field_id :desc] :layer :event_date)
    (sqlh/limit 5)))

【讨论】:

不完全,但很努力。我越是盯着这个,我就越觉得 HoneySQL 助手无法做到这一点,但也许没关系。当我查看 PostgreSQL 对实际查询的解释时, ORDER BY (field_id, layer, event_date) DESC, vs (ORDER BY field_id DESC, layer DESC, event_date DESC);它似乎产生了相同的查询计划......它只是让我感到困扰,我必须这样做如此细粒度,因为帮助类并没有真正直接映射到底层 SQL【参考方案2】:

已经 2 年多了,但我终于对 Clojure 有了足够的了解,并且与 HoneySQL 合作了足够长的时间来了解年轻时的自己所缺少的东西。它不希望复合键作为向量:sqlh/order-by 是可变的:

(doc sqlh/order-by)
-------------------------
honeysql.helpers/order-by
([& fields] [m & fields])
  nil

它真正想要的是(sqlh/order-by :field_id :layer [:event_date :desc])。该向量仅用于对特定字段排序 DESC。

所以这就是我想要做的:

(-> (sqlh/select :*)
    (sqlh/from :event)
    (sqlh/merge-where [:in :field_id field-ids])
    (sqlh/merge-where (cond (not-empty layers) [:in :layer layers]))
    (sqlh/merge-where (make-where-for-timestamp
                        :event_date event-date-from event-date-to))
    (sqlh/merge-where (make-where-for-timestamp
                        :updated_at updated-at-from updated-at-to))
    (sqlh/order-by :field_id :layer [:event_date :desc])
    sql/format)
=>
["SELECT * FROM event WHERE ((field_id in (?)) AND (layer in (?, ?, ?))) ORDER BY field_id, layer, event_date DESC"
 "1325629"
 "fha.true-color"
 "fha.abs"
 "fha.rank"]

@savior 在https://***.com/a/40356529/688355 一直都是正确的。我的 Clojure 不够好,无法理解它。

【讨论】:

以上是关于如何使 HoneySQL 处理顺序作为复合键?的主要内容,如果未能解决你的问题,请参考以下文章

如何使复合键唯一?

从不使用以 DATETIME 作为复合键第一部分的主键索引

Hibernate 对具有复合键的子实体执行错误的插入顺序

Java:哈希图中的复合键

在休眠中使用复合键

RedShift - 如何通过复合主键过滤表中的记录?