diff --git a/src/metabase/db.clj b/src/metabase/db.clj
index f760ae191c44d3703006773bd36c07be97318285..c2134196435088aa8bc51bbbc74f33c3715275b9 100644
--- a/src/metabase/db.clj
+++ b/src/metabase/db.clj
@@ -5,13 +5,14 @@
             (clojure [set :as set]
                      [string :as str])
             [environ.core :refer [env]]
-            (korma [core :refer :all]
-                   [db :refer :all])
+            (korma [core :as k]
+                   [db :as kdb])
             [medley.core :as m]
             [metabase.config :as config]
-            [metabase.db.internal :refer :all :as i]
+            [metabase.db.internal :as i]
             [metabase.models.interface :as models]
-            [metabase.util :as u]))
+            [metabase.util :as u])
+  (:import com.metabase.corvus.migrations.LiquibaseMigrations))
 
 ;; ## DB FILE, JDBC/KORMA DEFINITONS
 
@@ -32,30 +33,30 @@
   "Configure connection details for JDBC."
   []
   (case (config/config-kw :mb-db-type)
-    :h2 {:subprotocol "h2"
-         :classname   "org.h2.Driver"
-         :subname     (db-file)}
+    :h2       {:subprotocol "h2"
+               :classname   "org.h2.Driver"
+               :subname     (db-file)}
     :postgres {:subprotocol "postgresql"
-               :classname "org.postgresql.Driver"
-               :subname (str "//" (config/config-str :mb-db-host)
-                             ":" (config/config-str :mb-db-port)
-                             "/" (config/config-str :mb-db-dbname))
-               :user (config/config-str :mb-db-user)
-               :password (config/config-str :mb-db-pass)}))
+               :classname   "org.postgresql.Driver"
+               :subname     (str "//" (config/config-str :mb-db-host)
+                                 ":" (config/config-str :mb-db-port)
+                                 "/" (config/config-str :mb-db-dbname))
+               :user        (config/config-str :mb-db-user)
+               :password    (config/config-str :mb-db-pass)}))
 
 
 (defn setup-korma-db
   "Configure connection details for Korma."
   []
   (case (config/config-kw :mb-db-type)
-    :h2 (h2 {:db (db-file)
-             :naming {:keys   str/lower-case
-                      :fields str/upper-case}})
-    :postgres (postgres {:db       (config/config-str :mb-db-dbname)
-                         :port     (config/config-int :mb-db-port)
-                         :user     (config/config-str :mb-db-user)
-                         :password (config/config-str :mb-db-pass)
-                         :host     (config/config-str :mb-db-host)})))
+    :h2       (kdb/h2 {:db     (db-file)
+                       :naming {:keys   str/lower-case
+                                :fields str/upper-case}})
+    :postgres (kdb/postgres {:db       (config/config-str :mb-db-dbname)
+                             :port     (config/config-int :mb-db-port)
+                             :user     (config/config-str :mb-db-user)
+                             :password (config/config-str :mb-db-pass)
+                             :host     (config/config-str :mb-db-host)})))
 
 
 ;; ## CONNECTION
@@ -80,9 +81,9 @@
   [jdbc-db direction]
   (let [conn (jdbc/get-connection jdbc-db)]
     (case direction
-      :up    (com.metabase.corvus.migrations.LiquibaseMigrations/setupDatabase conn)
-      :down  (com.metabase.corvus.migrations.LiquibaseMigrations/teardownDatabase conn)
-      :print (com.metabase.corvus.migrations.LiquibaseMigrations/genSqlDatabase conn))))
+      :up    (LiquibaseMigrations/setupDatabase conn)
+      :down  (LiquibaseMigrations/teardownDatabase conn)
+      :print (LiquibaseMigrations/genSqlDatabase conn))))
 
 
 ;; ## SETUP-DB
@@ -125,7 +126,7 @@
     (log/info "Database Migrations Current ... CHECK")
 
     ;; Establish our 'default' Korma DB Connection
-    (default-connection (create-db korma-db))))
+    (kdb/default-connection (kdb/create-db korma-db))))
 
 (defn setup-db-if-needed [& args]
   (when-not @setup-db-has-been-called?
@@ -149,7 +150,7 @@
                  (models/pre-update entity)
                  (models/internal-pre-update entity)
                  (#(dissoc % :id)))
-        result (-> (update entity (set-fields obj) (where {:id entity-id}))
+        result (-> (k/update entity (k/set-fields obj) (k/where {:id entity-id}))
                    (> 0))]
     (when result
       (models/post-update entity (assoc obj :id entity-id)))
@@ -168,13 +169,36 @@
   "Wrapper around `korma.core/delete` that makes it easier to delete a row given a single PK value.
    Returns a `204 (No Content)` response dictionary."
   [entity & {:as kwargs}]
-  (delete entity (where kwargs))
+  (k/delete entity (k/where kwargs))
   {:status 204
    :body nil})
 
 
 ;; ## SEL
 
+(comment
+  :id->field `(let [[entity# field#] ~entity]
+                (->> (sel :many :fields [entity# field# :id] ~@forms)
+                     (map (fn [{id# :id field-val# field#}]
+                            {id# field-val#}))
+                     (into {})))
+  :field->id `(let [[entity# field#] ~entity]
+                (->> (sel :many :fields [entity# field# :id] ~@forms)
+                     (map (fn [{id# :id field-val# field#}]
+                            {field-val# id#}))
+                     (into {})))
+  :field->field `(let [[entity# field1# field2#] ~entity]
+                   (->> (sel :many entity# ~@forms)
+                        (map (fn [obj#]
+                               {(field1# obj#) (field2# obj#)}))
+                        (into {})))
+  :field->obj `(let [[entity# field#] ~entity]
+                 (->> (sel :many entity# ~@forms)
+                      (map (fn [obj#]
+                             {(field# obj#) obj#}))
+                      (into {})))
+  )
+
 (defmacro sel
   "Wrapper for korma `select` that calls `post-select` on results and provides a few other conveniences.
 
@@ -235,69 +259,15 @@
 
     (sel :many Table :db_id 1)                    -> (select User (where {:id 1}))
     (sel :many Table :db_id 1 (order :name :ASC)) -> (select User (where {:id 1}) (order :name ASC))"
-  {:arglists '([one-or-many option? entity & forms])}
-  [one-or-many & args]
-  {:pre [(contains? #{:one :many} one-or-many)]}
-  (if (= one-or-many :one)
-    `(first (sel :many ~@args (limit 1)))
-    (let [[option [entity & forms]] (u/optional keyword? args)]
-      (case option
-        :field  `(let [[entity# field#] ~entity]
-                   (map field#
-                        (sel :many [entity# field#] ~@forms)))
-        :id     `(sel :many :field [~entity :id] ~@forms)
-        :id->fields `(->> (sel :many :fields [~@entity :id] ~@forms)
-                          (map (fn [{id# :id :as obj#}]
-                                 {id# obj#}))
-                          (into {}))
-        :id->field `(let [[entity# field#] ~entity]
-                      (->> (sel :many :fields [entity# field# :id] ~@forms)
-                           (map (fn [{id# :id field-val# field#}]
-                                  {id# field-val#}))
-                           (into {})))
-        :field->id `(let [[entity# field#] ~entity]
-                      (->> (sel :many :fields [entity# field# :id] ~@forms)
-                           (map (fn [{id# :id field-val# field#}]
-                                  {field-val# id#}))
-                           (into {})))
-        :field->field `(let [[entity# field1# field2#] ~entity]
-                         (->> (sel :many entity# ~@forms)
-                              (map (fn [obj#]
-                                     {(field1# obj#) (field2# obj#)}))
-                              (into {})))
-        :field->obj `(let [[entity# field#] ~entity]
-                       (->> (sel :many entity# ~@forms)
-                            (map (fn [obj#]
-                                   {(field# obj#) obj#}))
-                            (into {})))
-        :fields `(let [[~'_ & fields# :as entity#] ~entity]
-                   (map #(select-keys % fields#)
-                        (sel :many entity# ~@forms)))
-        nil     `(-sel-select ~entity ~@forms)))))
-
-(defn -sel-transform [entity result]
-  (->> result
-       (models/internal-post-select entity)
-       (models/post-select entity)))
-
-(defmacro -sel-select
-  "Internal macro used by `sel` (don't call this directly).
-   Generates the korma `select` form."
-  [entity & forms]
-  (let [forms (sel-apply-kwargs forms)]                                          ; convert kwargs like `:id 1` to korma `where` clause
-    `(let [[entity# field-keys#] (destructure-entity ~entity)                    ; pull out field-keys if passed entity vector like `[entity & field-keys]`
-           entity# (entity->korma entity#)                                       ; entity## is the actual entity like `metabase.models.user/User` that we can dispatch on
-           entity-select-form# (-> entity#                                       ; entity-select-form# is the tweaked version we'll pass to korma `select`
-                                   (assoc :fields (or field-keys#                ; tell korma which fields to grab. If `field-keys` weren't passed in vector do lookup at runtime
-                                                      (:metabase.models.interface/default-fields entity#))))]
-       (when (config/config-bool :mb-db-logging)
-         (log/debug "DB CALL: " (:name entity#)
-                  (or (:fields entity-select-form#) "*")
-                  ~@(mapv (fn [[form & args]]
-                            `[~(name form) ~(apply str (interpose " " args))])
-                          forms)))
-       (->> (select entity-select-form# ~@forms)
-            (map (partial -sel-transform entity#))))))
+  {:arglists '([options? entity & forms])}
+  [& args]
+  (let [[option args] (u/optional keyword? args)]
+    `(~(if option
+         ;; if an option was specified, hand off to macro named metabase.db.internal/sel:OPTION
+         (symbol (format "metabase.db.internal/sel:%s" (name option)))
+         ;; otherwise just hand off to low-level sel* macro
+         'metabase.db.internal/sel*)
+      ~@args)))
 
 
 ;; ## INS
@@ -311,30 +281,31 @@
   (let [vals (->> kwargs
                   (models/pre-insert entity)
                   (models/internal-pre-insert entity))
-        {:keys [id]} (-> (insert entity (values vals))
+        {:keys [id]} (-> (k/insert entity (k/values vals))
                          (set/rename-keys {(keyword "scope_identity()") :id}))]
     (models/post-insert entity (entity id))))
 
 
 ;; ## EXISTS?
 
-(defmacro exists?
+(defn exists?
   "Easy way to see if something exists in the db.
 
     (exists? User :id 100)"
   [entity & {:as kwargs}]
-  `(not (empty? (select (entity->korma ~entity)
-                        (fields [:id])
-                        ~@(when (seq kwargs)
-                            `[(where ~kwargs)])
-                        (limit 1)))))
+  (boolean (seq (k/select (i/entity->korma entity)
+                          (k/fields [:id])
+                          (k/where (if (seq kwargs) kwargs {}))
+                          (k/limit 1)))))
 
 ;; ## CASADE-DELETE
 
-(defn -cascade-delete [entity objects]
-  (dorun (for [obj objects]
-           (do (models/pre-cascade-delete entity obj)
-               (del entity :id (:id obj)))))
+(defn -cascade-delete [entity f]
+  (let [entity  (i/entity->korma entity)
+        results (i/sel-exec entity f)]
+    (dorun (for [obj results]
+             (do (models/pre-cascade-delete entity obj)
+                 (del entity :id (:id obj))))))
   {:status 204, :body nil})
 
 (defmacro cascade-delete
@@ -343,5 +314,4 @@
 
    Like `del`, this returns a 204/nil reponse so it can be used directly in an API endpoint."
   [entity & kwargs]
-  `(let [entity#    (entity->korma ~entity)]
-     (-cascade-delete entity# (sel :many entity# ~@kwargs))))
+  `(-cascade-delete ~entity (i/sel-fn ~@kwargs)))
diff --git a/src/metabase/db/internal.clj b/src/metabase/db/internal.clj
index 154e1413635dfcd87e404ecb4e301f4e686f48b2..2758e3d906c9847dbe7b436b7b7a5afac68bdd8b 100644
--- a/src/metabase/db/internal.clj
+++ b/src/metabase/db/internal.clj
@@ -1,8 +1,11 @@
 (ns metabase.db.internal
   "Internal functions and macros used by the public-facing functions in `metabase.db`."
-  (:require [clojure.walk :as walk]
-            [cheshire.core :as cheshire]
-            [korma.core :refer [where]]
+  (:require [clojure.string :as s]
+            [clojure.tools.logging :as log]
+            [clojure.walk :as walk]
+            [korma.core :refer [where], :as k]
+            [metabase.config :as config]
+            [metabase.models.interface :as models]
             [metabase.util :as u]))
 
 (declare entity->korma)
@@ -57,22 +60,105 @@
            :else entity))))
 
 
-;; ## READ-JSON
+;;; ## ---------------------------------------- SEL 2.0 FUNCTIONS ----------------------------------------
 
-(defn- read-json-str-or-clob
-  "If JSON-STRING is a JDBC Clob, convert to a String. Then call `json/read-str`."
-  [json-str]
-  (some-> (u/jdbc-clob->str json-str)
-          cheshire/parse-string))
+;;; Low-level sel implementation
 
-(defn read-json
-  "Read JSON-STRING (or JDBC Clob) as JSON and keywordize keys."
-  [json-string]
-  (->> (read-json-str-or-clob json-string)
-       walk/keywordize-keys))
+(defmacro sel-fn [& forms]
+  (let [forms               (sel-apply-kwargs forms)
+        entity-placeholder  (gensym "ENTITY--")]
+    (loop [query `(k/select* ~entity-placeholder), [[f & args] & more] forms]
+      (cond
+        f          (recur `(~f ~query ~@args) more)
+        (seq more) (recur query more)
+        :else      `[(fn [~entity-placeholder]
+                       ~query) ~(str query)]))))
 
-(defn write-json
-  "If OBJ is not already a string, encode it as JSON."
-  [obj]
-  (if (string? obj) obj
-      (cheshire/generate-string obj)))
+(defn sel-exec [entity [select-fn log-str]]
+  (let [[entity field-keys] (destructure-entity entity)
+        entity              (entity->korma entity)
+        entity+fields       (assoc entity :fields (or field-keys
+                                                            (:metabase.models.interface/default-fields entity)))]
+    ;; Log if applicable
+    (future
+      (when (config/config-bool :mb-db-logging)
+        (log/debug "DB CALL: " (:name entity)
+                   (or (:fields entity+fields) "*")
+                   (s/replace log-str #"korma.core/" ""))))
+
+    (->> (k/exec (select-fn entity+fields))
+         (map (partial models/internal-post-select entity))
+         (map (partial models/post-select entity)))))
+
+(defmacro sel* [entity & forms]
+  `(sel-exec ~entity (sel-fn ~@forms)))
+
+;;; :field
+
+(defmacro sel:field [[entity field] & forms]
+  `(let [field# ~field]
+     (map field# (sel* [~entity field#] ~@forms))))
+
+;;; :id
+
+(defmacro sel:id [entity & forms]
+  `(sel:field [~entity :id] ~@forms))
+
+;;; :fields
+
+(defn sel:fields* [fields results]
+  (for [result results]
+    (select-keys result fields)))
+
+(defmacro sel:fields [[entity & fields] & forms]
+  `(let [fields# ~(vec fields)]
+     (sel:fields* (set fields#) (sel* `[~~entity ~@fields#] ~@forms))))
+
+;;; :id->fields
+
+(defn sel:id->fields* [fields results]
+  (->> results
+       (map (u/rpartial select-keys fields))
+       (zipmap (map :id results))))
+
+(defmacro sel:id->fields [[entity & fields] & forms]
+  `(let [fields# ~(conj (set fields) :id)]
+     (sel:id->fields* fields# (sel* `[~~entity ~@fields#] ~@forms))))
+
+;;; :field->field
+
+(defn sel:field->field* [f1 f2 results]
+  (into {} (for [result results]
+             {(f1 result) (f2 result)})))
+
+(defmacro sel:field->field [[entity f1 f2] & forms]
+  `(let [f1# ~f1
+         f2# ~f2]
+     (sel:field->field* f1# f2# (sel* [~entity f1# f2#] ~@forms))))
+
+;;; : id->field
+
+(defmacro sel:id->field [[entity field] & forms]
+  `(sel:field->field [~entity :id ~field] ~@forms))
+
+;;; :field->id
+
+(defmacro sel:field->id [[entity field] & forms]
+  `(sel:field->field [~entity ~field :id] ~@forms))
+
+;;; :field->obj
+
+(defn sel:field->obj* [field results]
+  (into {} (for [result results]
+             {(field result) result})))
+
+(defmacro sel:field->obj [[entity field] & forms]
+  `(sel:field->obj* ~field (sel* ~entity ~@forms)))
+
+;;; :one & :many
+
+(defmacro sel:one [& args]
+  `(first (metabase.db/sel ~@args (k/limit 1))))
+
+(defmacro sel:many [& args]
+  `(metabase.db/sel ~@args))
diff --git a/src/metabase/models/interface.clj b/src/metabase/models/interface.clj
index 140b5590278e08d9c5ff8741cae732d2becc2335..37fe451cf5e594efed25231d999c65c6e0d07b69 100644
--- a/src/metabase/models/interface.clj
+++ b/src/metabase/models/interface.clj
@@ -1,11 +1,11 @@
 (ns metabase.models.interface
   (:require (clojure.tools [logging :as log]
                            [macro :refer [macrolet]])
-            [clojure.walk :refer [macroexpand-all]]
+            [clojure.walk :refer [macroexpand-all], :as walk]
+            [cheshire.core :as cheshire]
             [korma.core :as k]
             [medley.core :as m]
             [metabase.config :as config]
-            metabase.db.internal
             [metabase.util :as u]))
 
 ;;; ## ---------------------------------------- PERMISSIONS CHECKING ----------------------------------------
@@ -112,9 +112,29 @@
    :post-select        #'identity-second
    :pre-cascade-delete #'constantly-nil})
 
+;; ## ---------------------------------------- READ-JSON ----------------------------------------
+
+(defn- read-json-str-or-clob
+  "If JSON-STRING is a JDBC Clob, convert to a String. Then call `json/read-str`."
+  [json-str]
+  (some-> (u/jdbc-clob->str json-str)
+          cheshire/parse-string))
+
+(defn- read-json
+  "Read JSON-STRING (or JDBC Clob) as JSON and keywordize keys."
+  [json-string]
+  (->> (read-json-str-or-clob json-string)
+       walk/keywordize-keys))
+
+(defn- write-json
+  "If OBJ is not already a string, encode it as JSON."
+  [obj]
+  (if (string? obj) obj
+      (cheshire/generate-string obj)))
+
 (def ^:const ^:private type-fns
-  {:json    {:in  'metabase.db.internal/write-json
-             :out 'metabase.db.internal/read-json}
+  {:json    {:in  #'write-json
+             :out #'read-json}
    :keyword {:in  `name
              :out `keyword}})
 
diff --git a/test/metabase/test/data/users.clj b/test/metabase/test/data/users.clj
index fe648603da699e04c2cda905825265aa31231415..bd8f92f6e4dadbcb3ecc3424ede61e155f7bffd1 100644
--- a/test/metabase/test/data/users.clj
+++ b/test/metabase/test/data/users.clj
@@ -52,9 +52,9 @@
   [& {:as kwargs}]
   (let [first-name (random-name)
         defaults {:first_name first-name
-                  :last_name (random-name)
-                  :email (.toLowerCase ^String (str first-name "@metabase.com"))
-                  :password first-name}]
+                  :last_name  (random-name)
+                  :email      (.toLowerCase ^String (str first-name "@metabase.com"))
+                  :password   first-name}]
     (->> (merge defaults kwargs)
          (m/mapply ins User))))