diff --git a/dev/src/dev.clj b/dev/src/dev.clj
index d9a0209ffdb939d92cc612a639deadfe10168a2c..a472a6792225bca57278e8c61e43bf6e0449f125 100644
--- a/dev/src/dev.clj
+++ b/dev/src/dev.clj
@@ -106,7 +106,8 @@
   explain-query]
  [dev.migrate
   migrate!
-  rollback!]
+  rollback!
+  migration-sql-by-id]
  [model-tracking
   track!
   untrack!
diff --git a/dev/src/dev/migrate.clj b/dev/src/dev/migrate.clj
index 1c3d4e6f3df51a885f6bec30d88f88ec2ec3647b..65f57291bb298ac8d9ad82dc02add7c908f0070e 100644
--- a/dev/src/dev/migrate.clj
+++ b/dev/src/dev/migrate.clj
@@ -1,12 +1,17 @@
 (ns dev.migrate
   (:gen-class)
   (:require
+   [clojure.string :as str]
    [metabase.db :as mdb]
    [metabase.db.liquibase :as liquibase]
    [metabase.util.malli :as mu]
    [toucan2.core :as t2])
   (:import
-   (liquibase Liquibase)))
+   (liquibase Contexts Liquibase RuntimeEnvironment)
+   (liquibase.changelog ChangeLogIterator)
+   (liquibase.changelog.filter ChangeSetFilter)
+   (liquibase.sqlgenerator SqlGeneratorFactory)
+   (liquibase.changelog.visitor ListVisitor)))
 
 (set! *warn-on-reflection* true)
 
@@ -101,3 +106,37 @@
 
       (throw (ex-info "Invalid command" {:command cmd
                                          :args    args})))))
+
+(defn- stmts-to-sql
+  [stmts sql-generator-factory database]
+  (str/join "\n" (for [stmt stmts
+                       sql (.generateSql ^SqlGeneratorFactory sql-generator-factory stmt database)]
+                   (.toString sql))))
+
+(defn- change->sql
+  [change sql-generator-factory database]
+  {:forward  (stmts-to-sql (.generateStatements change database) sql-generator-factory database)
+   :rollback (stmts-to-sql (.generateRollbackStatements change database) sql-generator-factory database)})
+
+(defn migration-sql-by-id
+  "Get the sql statements for a specific migration ID.
+    (migration-sql-by-id \"v51.2024-06-12T18:53:02\")
+    ;; =>
+      {:forward \"DROP INDEX public.idx_user_id_device_id;\",
+       :rollback \"CREATE INDEX idx_user_id_device_id ON public.login_history(session_id, device_id);\"}"
+  [id]
+  (t2/with-connection [conn]
+    (liquibase/with-liquibase [^Liquibase liquibase conn]
+      (let [database            (.getDatabase liquibase)
+            change-log-iterator (ChangeLogIterator. (.getDatabaseChangeLog liquibase) (into-array ChangeSetFilter []))
+            list-visistor       (ListVisitor.)
+            runtime-env         (RuntimeEnvironment. database (Contexts.) nil)
+            _                   (.run change-log-iterator list-visistor runtime-env)
+            change-set          (first (filter #(= id (.getId %))(.getSeenChangeSets list-visistor)))
+            sql-generator-factory (SqlGeneratorFactory/getInstance)]
+        (reduce (fn [acc data]
+                  ;; merge all changes in one change set into one single :forward and :rollback
+                  (merge-with (fn [x y]
+                                (str x "\n" y)) acc data))
+                {}
+                (map #(change->sql % sql-generator-factory database) (.getChanges change-set)))))))
diff --git a/dev/test/dev/migrate_test.clj b/dev/test/dev/migrate_test.clj
new file mode 100644
index 0000000000000000000000000000000000000000..171c97bbc0a840b7af9930eccd75f406eb82dc85
--- /dev/null
+++ b/dev/test/dev/migrate_test.clj
@@ -0,0 +1,11 @@
+(ns dev.migrate-test
+  (:require
+   [clojure.test :refer :all]
+   [metabase.dev.migrate :as dev.migrate]))
+
+(deftest migration-sql-by-id-test
+  (is (= {:forward
+          "ALTER TABLE public.query_field RENAME COLUMN direct_reference TO explicit_reference;",
+          :rollback
+          "ALTER TABLE public.query_field RENAME COLUMN explicit_reference TO direct_reference;"}
+         (dev.migrate/migration-sql-by-id "v51.2024-06-07T12:37:36"))))