diff --git a/frontend/test/metabase/scenarios/binning/qb-explicit-joins.cy.spec.js b/frontend/test/metabase/scenarios/binning/qb-explicit-joins.cy.spec.js
index 26fdb7de671070912745fbcefce5d35d44035696..22807ba47530ed781037b645061d1589d3883b87 100644
--- a/frontend/test/metabase/scenarios/binning/qb-explicit-joins.cy.spec.js
+++ b/frontend/test/metabase/scenarios/binning/qb-explicit-joins.cy.spec.js
@@ -190,32 +190,11 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
       cy.findByText("QB Binning").click();
     });
 
-    /**
-     * Generated title seems to be incorrect.
-     * Please see: https://github.com/metabase/metabase/issues/16693.
-     *
-     *  1. Todo: unskip the titles in this block once #16693 gets fixed.
-     *  2. Unskip the repro for metabase#16693 which was conviniently created in this same file.
-     *
-     * Note: after #16693 gets fixed, it might even make sense to completly remove the related repro,
-     * since all other tests within this `context` will already cover that implicitly and will guard against a regression.
-     */
-
-    it.skip("should render the correct title (metabase#16693)", () => {
-      cy.findByText("People → Birth Date").click();
-      cy.findByText("Distribution").click();
-
-      cy.findByText("Count by People → Birth Date: Month");
-    });
-
     it("should work for time series", () => {
       cy.findByText("People → Birth Date").click();
       cy.findByText("Distribution").click();
 
-      /**
-       * Please see the comment no. 1 above.
-       */
-      // cy.findByText("Count by People → Birth Date: Month");
+      cy.findByText("Count by People → Birth Date: Month");
 
       assertOnXYAxisLabels({ xLabel: "People → Birth Date", yLabel: "Count" });
 
@@ -230,10 +209,7 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
         .click();
       cy.findByText("Quarter").click();
 
-      /**
-       * Please see the comment no. 1 above.
-       */
-      // cy.findByText("Count by People → Birth Date: Quarter");
+      cy.findByText("Count by People → Birth Date: Quarter");
 
       cy.findByText("Q1 - 1960");
       cy.findByText("Q1 - 1965");
@@ -243,10 +219,7 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
       cy.findByText("Products → Price").click();
       cy.findByText("Distribution").click();
 
-      /**
-       * Please see the comment no. 1 above.
-       */
-      // cy.findByText("Count by Products → Price: Auto binned");
+      cy.findByText("Count by Products → Price: Auto binned");
 
       assertOnXYAxisLabels({ xLabel: "Products → Price", yLabel: "Count" });
 
@@ -260,10 +233,7 @@ describe("scenarios > binning > from a saved QB question with explicit joins", (
       cy.findByText("People → Longitude").click();
       cy.findByText("Distribution").click();
 
-      /**
-       * Please see the comment no. 1 above.
-       */
-      // cy.findByText("Count by People → Longitude: Auto binned");
+      cy.findByText("Count by People → Longitude: Auto binned");
 
       assertOnXYAxisLabels({
         xLabel: "People → Longitude",
diff --git a/frontend/test/metabase/scenarios/question/nested.cy.spec.js b/frontend/test/metabase/scenarios/question/nested.cy.spec.js
index a06a8baa198700838b67e199eb8e07d7890a9406..a49bbccf00f7d6600e42f97fb878645922bc9b08 100644
--- a/frontend/test/metabase/scenarios/question/nested.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/nested.cy.spec.js
@@ -383,7 +383,7 @@ describe("scenarios > question > nested", () => {
     });
   });
 
-  it.skip("'distribution' should work on a joined table from a saved question (metabase#14787)", () => {
+  it("'distribution' should work on a joined table from a saved question (metabase#14787)", () => {
     // Set the display really wide and really tall to avoid any scrolling
     cy.viewport(1600, 1200);
 
diff --git a/src/metabase/query_processor/middleware/annotate.clj b/src/metabase/query_processor/middleware/annotate.clj
index 2dc84c860a6695958847fa03c61f16ae802a5b86..fe9149069e78113ef4474095d54c8839e8867867 100644
--- a/src/metabase/query_processor/middleware/annotate.clj
+++ b/src/metabase/query_processor/middleware/annotate.clj
@@ -173,9 +173,10 @@
     :field_ref       clause}))
 
 (s/defn ^:private col-info-for-field-clause*
-  [{:keys [source-metadata expressions], :as inner-query} [_ id-or-name opts :as clause] :- mbql.s/field]
-  (let [join (when (:join-alias opts)
-               (join-with-alias inner-query (:join-alias opts)))]
+  [{:keys [source-metadata source-card-id], :as inner-query} [_ id-or-name opts :as clause] :- mbql.s/field]
+  (let [join                      (when (:join-alias opts)
+                                    (join-with-alias inner-query (:join-alias opts)))
+        join-is-at-current-level? (some #(= (:alias %) (:join-alias opts)) (:joins inner-query))]
     ;; TODO -- I think we actually need two `:field_ref` columns -- one for referring to the Field at the SAME
     ;; level, and one for referring to the Field from the PARENT level.
     (cond-> {:field_ref clause}
@@ -228,7 +229,14 @@
       (or (:source-field opts)
           (:fk-field-id join))
       (assoc :fk_field_id (or (:source-field opts)
-                              (:fk-field-id join))))))
+                              (:fk-field-id join)))
+
+      ;; If the source query is from a saved question, remove the join alias as the caller should not be aware of joins
+      ;; happening inside the saved question. The `not join-is-at-current-level?` check is to ensure that we are not
+      ;; removing `:join-alias` from fields from the right side of the join.
+      (and source-card-id
+           (not join-is-at-current-level?))
+      (update :field_ref mbql.u/update-field-options dissoc :join-alias))))
 
 (s/defn ^:private col-info-for-field-clause :- {:field_ref mbql.s/Field, s/Keyword s/Any}
   "Return results column metadata for a `:field` or `:expression` clause, in the format that gets returned by QP results"
diff --git a/src/metabase/query_processor/middleware/fetch_source_query.clj b/src/metabase/query_processor/middleware/fetch_source_query.clj
index 9c36250d9f831c18538ab6450067dede675464ef..fa495300410171e762583e5cf111e33989749c0e 100644
--- a/src/metabase/query_processor/middleware/fetch_source_query.clj
+++ b/src/metabase/query_processor/middleware/fetch_source_query.clj
@@ -9,6 +9,7 @@
 
     {:source-query    {...} ; Query for Card 1
      :source-metadata [...] ; metadata about columns in Card 1
+     :source-card-id  1     ; Original Card ID
      ...}
 
   This middleware resolves Card ID `:source-table`s at all levels of the query, but the top-level query often uses the
@@ -17,7 +18,7 @@
 
     {:database <virtual-id>, :type :query, :query {:source-table \"card__1\"}}
     ->
-    {:database 1, :type :query, :query {:source-query {...}, :source-metadata {...}}}
+    {:database 1, :type :query, :query {:source-query {...}, :source-metadata {...}, :source-card-id 1}}
 
   TODO - consider renaming this namespace to `metabase.query-processor.middleware.resolve-card-id-source-tables`"
   (:require [clojure.set :as set]
@@ -49,6 +50,7 @@
    {:database        mbql.s/DatabaseID
     :source-metadata [mbql.s/SourceQueryMetadata]
     :source-query    mbql.s/SourceQuery
+    :source-card-id  su/IntGreaterThanZero
     s/Keyword        s/Any}
    (complement :source-table)
    "`:source-table` should be removed"))
@@ -151,8 +153,8 @@
         source-query-and-metadata (-> card-id card-id->source-query-and-metadata)]
     (merge
      (dissoc m :source-table)
-     ;; record the `::card-id` we've resolved here. We'll include it in `:info` for permissions purposes later
-     {::card-id card-id}
+     ;; record the `card-id` we've resolved here. We'll include it in `:info` for permissions purposes later
+     {:source-card-id card-id}
      source-query-and-metadata)))
 
 (defn- resolve-all*
@@ -225,14 +227,11 @@
 
 (s/defn ^:private extract-resolved-card-id :- {:card-id (s/maybe su/IntGreaterThanZero)
                                                :query   su/Map}
-  "If the ID of the Card we've resolved (`::card-id`) was added by a previous step, remove it from `query`, and add it
+  "If the ID of the Card we've resolved (`:source-card-id`) was added by a previous step, add it
   to `:query` `:info` (so it can be included in the QueryExecution log), then return a map with the resolved
   `:card-id` and updated `:query`."
   [query :- su/Map]
-  (let [card-id (get-in query [:query ::card-id])
-        query   (mbql.u/replace-in query [:query]
-                  (&match :guard (every-pred map? ::card-id))
-                  (recur (dissoc &match ::card-id)))]
+  (let [card-id (get-in query [:query :source-card-id])]
     {:query   (cond-> query
                 card-id (update-in [:info :card-id] #(or % card-id)))
      :card-id card-id}))
@@ -242,9 +241,9 @@
   "Recursively replace all Card ID source tables in `query` with resolved `:source-query` and `:source-metadata`. Since
   the `:database` is only useful for top-level source queries, we'll remove it from all other levels."
   [query :- su/Map]
-  ;; if a `::card-id` is already in the query, remove it, so we don't pull user-supplied input up into `:info`
+  ;; if a `:source-card-id` is already in the query, remove it, so we don't pull user-supplied input up into `:info`
   ;; allowing someone to bypass permissions
-  (-> (m/dissoc-in query [:query ::card-id])
+  (-> (m/dissoc-in query [:query :source-card-id])
       check-for-circular-references
       resolve-all*
       copy-source-query-database-ids
diff --git a/test/metabase/query_processor/middleware/annotate_test.clj b/test/metabase/query_processor/middleware/annotate_test.clj
index b7b67d93a6816b926500a982047390e0f322c4e2..16849f81671da96db2133d0f1cf5c52b159b2d99 100644
--- a/test/metabase/query_processor/middleware/annotate_test.clj
+++ b/test/metabase/query_processor/middleware/annotate_test.clj
@@ -1,7 +1,7 @@
 (ns metabase.query-processor.middleware.annotate-test
   (:require [clojure.test :refer :all]
             [metabase.driver :as driver]
-            [metabase.models :refer [Field]]
+            [metabase.models :refer [Card Field]]
             [metabase.query-processor :as qp]
             [metabase.query-processor.middleware.annotate :as annotate]
             [metabase.query-processor.store :as qp.store]
@@ -612,3 +612,31 @@
                               :id           %ean
                               :field_ref    &Products.ean})
                            (ean-metadata (add-column-info nested-query {}))))))))))))))
+
+;; metabase#14787
+(deftest col-info-for-fields-from-card-test
+  (mt/dataset sample-dataset
+    (let [card-1-query (mt/mbql-query orders
+                         {:joins [{:fields       :all
+                                   :source-table $$products
+                                   :condition    [:= $product_id &Products.products.id]
+                                   :alias        "Products"}]})]
+      (mt/with-temp* [Card [{card-1-id :id} {:dataset_query card-1-query}]
+                      Card [{card-2-id :id} {:dataset_query (mt/mbql-query people)}]]
+        (testing "when a nested query is from a saved question, there should be no `:join-alias` on the left side"
+          (mt/$ids nil
+            (let [base-query (qp/query->preprocessed
+                              (mt/mbql-query nil
+                                {:source-table (str "card__" card-1-id)
+                                 :joins        [{:fields       :all
+                                                 :source-table (str "card__" card-2-id)
+                                                 :condition    [:= $orders.user_id &Products.products.id]
+                                                 :alias        "Q"}]
+                                 :limit        1}))
+                  fields     #{%orders.discount %products.title %people.source}]
+              (is (= [{:display_name "Discount" :field_ref [:field %orders.discount nil]}
+                      {:display_name "Products → Title" :field_ref [:field %products.title nil]}
+                      {:display_name "Q → Source" :field_ref [:field %people.source {:join-alias "Q"}]}]
+                     (->> (:cols (add-column-info base-query {}))
+                          (filter #(fields (:id %)))
+                          (map #(select-keys % [:display_name :field_ref]))))))))))))
diff --git a/test/metabase/query_processor/middleware/fetch_source_query_test.clj b/test/metabase/query_processor/middleware/fetch_source_query_test.clj
index c238eb5160a120fb8108dd5cefa59426095143e5..dadb7cf5685947c896e492e9524a02ebf37cd854 100644
--- a/test/metabase/query_processor/middleware/fetch_source_query_test.clj
+++ b/test/metabase/query_processor/middleware/fetch_source_query_test.clj
@@ -35,7 +35,8 @@
   (testing "make sure that the `resolve-card-id-source-tables` middleware correctly resolves MBQL queries"
     (mt/with-temp Card [card {:dataset_query (mt/mbql-query venues)}]
       (is (= (assoc (default-result-with-inner-query
-                     {:source-query {:source-table (mt/id :venues)}}
+                     {:source-query   {:source-table (mt/id :venues)}
+                      :source-card-id (u/the-id card)}
                      (qp/query->expected-cols (mt/mbql-query venues)))
                     :info {:card-id (u/the-id card)})
              (resolve-card-id-source-tables
@@ -44,9 +45,10 @@
 
       (testing "with aggregations/breakouts"
         (is (= (assoc (default-result-with-inner-query
-                       {:aggregation  [[:count]]
-                        :breakout     [[:field "price" {:base-type :type/Integer}]]
-                        :source-query {:source-table (mt/id :venues)}}
+                       {:aggregation    [[:count]]
+                        :breakout       [[:field "price" {:base-type :type/Integer}]]
+                        :source-query   {:source-table (mt/id :venues)}
+                        :source-card-id (u/the-id card)}
                        (qp/query->expected-cols (mt/mbql-query :venues)))
                       :info {:card-id (u/the-id card)})
                (resolve-card-id-source-tables
@@ -58,8 +60,9 @@
     (mt/with-temp Card [card {:dataset_query (mt/mbql-query checkins)}]
       (testing "with filters"
         (is (= (assoc (default-result-with-inner-query
-                       {:source-query {:source-table (mt/id :checkins)}
-                        :filter       [:between [:field "date" {:base-type :type/Date}] "2015-01-01" "2015-02-01"]}
+                       {:source-query   {:source-table (mt/id :checkins)}
+                        :source-card-id (u/the-id card)
+                        :filter         [:between [:field "date" {:base-type :type/Date}] "2015-01-01" "2015-02-01"]}
                        (qp/query->expected-cols (mt/mbql-query :checkins)))
                       :info {:card-id (u/the-id card)})
                (resolve-card-id-source-tables
@@ -75,9 +78,10 @@
     (mt/with-temp Card [card {:dataset_query (mt/native-query
                                                {:query (format "SELECT * FROM %s" (mt/format-name "venues"))})}]
       (is (= (assoc (default-result-with-inner-query
-                     {:aggregation  [[:count]]
-                      :breakout     [[:field "price" {:base-type :type/Integer}]]
-                      :source-query {:native (format "SELECT * FROM %s" (mt/format-name "venues"))}}
+                     {:aggregation    [[:count]]
+                      :breakout       [[:field "price" {:base-type :type/Integer}]]
+                      :source-query   {:native (format "SELECT * FROM %s" (mt/format-name "venues"))}
+                      :source-card-id (u/the-id card)}
                      nil)
                     :info {:card-id (u/the-id card)})
              (resolve-card-id-source-tables
@@ -94,11 +98,13 @@
                                                   :type     :query
                                                   :query    {:source-table (str "card__" (u/the-id card-1)), :limit 50}}}]]
       (is (= (-> (default-result-with-inner-query
-                  {:limit        25
-                   :source-query {:limit           50
-                                  :source-query    {:source-table (mt/id :venues)
-                                                    :limit        100}
-                                  :source-metadata nil}}
+                  {:limit          25
+                   :source-query   {:limit           50
+                                    :source-query    {:source-table (mt/id :venues)
+                                                      :limit        100}
+                                    :source-card-id  (u/the-id card-1)
+                                    :source-metadata nil}
+                   :source-card-id (u/the-id card-2)}
                   (qp/query->expected-cols (mt/mbql-query :venues)))
                  (assoc-in [:query :source-query :source-metadata]
                            (mt/derecordize (qp/query->expected-cols (mt/mbql-query :venues))))
@@ -126,6 +132,7 @@
       (testing "Are `card__id` source tables resolved in `:joins`?"
         (is (= (mt/mbql-query venues
                  {:joins [{:source-query    {:source-table $$categories, :limit 100}
+                           :source-card-id  card-id
                            :alias           "c",
                            :condition       [:= $category_id [:field %categories.id {:join-alias "c"}]]
                            :source-metadata metadata}]})
@@ -138,6 +145,7 @@
       (testing "Are `card__id` source tables resolved in JOINs against a source query?"
         (is (= (mt/mbql-query venues
                  {:joins [{:source-query {:source-query    {:source-table $$categories, :limit 100}
+                                          :source-card-id  card-id
                                           :source-metadata metadata}
                            :alias        "c",
                            :condition    [:= $category_id [:field %categories.id {:join-alias "c"}]]}]})
@@ -152,6 +160,7 @@
                  {:source-query {:source-table $$venues
                                  :joins        [{:source-query    {:source-table $$categories
                                                                    :limit        100}
+                                                 :source-card-id  card-id
                                                  :alias           "c"
                                                  :condition       [:= $category_id [:field %categories.id {:join-alias "c"}]]
                                                  :source-metadata metadata}]}})
@@ -172,8 +181,10 @@
                              :condition       [:= $category_id &c.$categories.id]
                              :source-query    {:source-query    {:source-table $$categories
                                                                  :limit        100}
+                                               :source-card-id  card-id
                                                :source-metadata metadata
                                                :limit           200}
+                             :source-card-id  card-2-id
                              ;; TODO - FIXME
                              ;;
                              ;; The source metadata that comes back here looks like the result of running the Card
@@ -252,11 +263,11 @@
                                      :condition    [:= *ID/Number &c.venues.id]}]})))))))
 
 (deftest ignore-user-supplied-internal-card-id-keys
-  (testing (str "The middleware uses `::fetch-source-query/card-id` internally and adds it to `:info` at the end. "
+  (testing (str "The middleware uses `:source-card-id` internally and adds it to `:info` at the end. "
                 "Make sure we ignore ones supplied by a user.")
     (is (= (mt/mbql-query venues)
            (resolve-card-id-source-tables
-            (assoc-in (mt/mbql-query venues) [:query ::fetch-source-query/card-id] 1))))))
+            (assoc-in (mt/mbql-query venues) [:query :source-card-id] 1))))))
 
 (deftest dont-overwrite-existing-card-id-test
   (testing "Don't overwrite existing values of `[:info :card-id]`"
@@ -264,6 +275,7 @@
       (let [query (assoc (mt/mbql-query nil {:source-table (format "card__%d" card-id)})
                          :info {:card-id Integer/MAX_VALUE})]
         (is (= (assoc (mt/mbql-query nil {:source-query    {:source-table (mt/id :venues)}
+                                          :source-card-id  card-id
                                           :source-metadata (mt/derecordize (qp/query->expected-cols (mt/mbql-query :venues)))})
                       :info {:card-id Integer/MAX_VALUE})
                (resolve-card-id-source-tables query)))))))