diff --git a/frontend/src/metabase-lib/lib/Question.ts b/frontend/src/metabase-lib/lib/Question.ts
index 329da4e5aa91c451ca0660e215cf3f012861588d..a4c8a414a2c7c04ca176c17d294b47d9cec6037f 100644
--- a/frontend/src/metabase-lib/lib/Question.ts
+++ b/frontend/src/metabase-lib/lib/Question.ts
@@ -285,6 +285,10 @@ class QuestionInner {
     return this.setCard(assoc(this.card(), "display", display));
   }
 
+  /**
+   * returns whether this question is a model
+   * @returns boolean
+   */
   isDataset() {
     return this._card && this._card.dataset;
   }
diff --git a/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx b/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx
index 9f1a194e18db6a6ad820780b4809069dcb49a7bb..9308f2fc7edc55b3362aaa88f09376b72ed8316f 100644
--- a/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx
+++ b/frontend/src/metabase/modes/components/drill/ObjectDetailDrill.jsx
@@ -3,8 +3,10 @@ import { isFK, isPK } from "metabase/lib/schema_metadata";
 import { zoomInRow } from "metabase/query_builder/actions";
 
 function hasManyPKColumns(question) {
-  const table = question.query().table();
-  const fields = table?.fields ?? question.getResultMetadata();
+  const fields = question.isDataset()
+    ? question.getResultMetadata() ?? question.query().table?.()?.fields
+    : question.query().table?.()?.fields ?? question.getResultMetadata();
+
   return fields.filter(field => isPK(field)).length > 1;
 }
 
diff --git a/frontend/src/metabase/query_builder/actions/object-detail.js b/frontend/src/metabase/query_builder/actions/object-detail.js
index e4a1f44e0f93ac4bd8128217c813ea18b592cce7..d0ede23ffddb1033041624551b5d64e258cce532 100644
--- a/frontend/src/metabase/query_builder/actions/object-detail.js
+++ b/frontend/src/metabase/query_builder/actions/object-detail.js
@@ -11,6 +11,7 @@ import { FieldDimension } from "metabase-lib/lib/Dimension";
 import {
   getCard,
   getFirstQueryResult,
+  getPKColumnIndex,
   getNextRowPKValue,
   getPreviousRowPKValue,
   getTableForeignKeys,
@@ -19,9 +20,12 @@ import { setCardAndRun } from "./core";
 import { updateUrl } from "./navigation";
 
 export const ZOOM_IN_ROW = "metabase/qb/ZOOM_IN_ROW";
-export const zoomInRow = ({ objectId }) => dispatch => {
+export const zoomInRow = ({ objectId }) => (dispatch, getState) => {
   dispatch({ type: ZOOM_IN_ROW, payload: { objectId } });
-  dispatch(updateUrl(null, { objectId, replaceState: false }));
+
+  // don't show object id in url if it is a row index
+  const hasPK = getPKColumnIndex(getState()) !== -1;
+  hasPK && dispatch(updateUrl(null, { objectId, replaceState: false }));
 };
 
 export const RESET_ROW_ZOOM = "metabase/qb/RESET_ROW_ZOOM";
diff --git a/frontend/src/metabase/query_builder/selectors.js b/frontend/src/metabase/query_builder/selectors.js
index f2610c131563aa1a8e86c23ab846f5f53c14e225..bbf0e8372d1a0452ba83cb3290d3f8b4f85890c2 100644
--- a/frontend/src/metabase/query_builder/selectors.js
+++ b/frontend/src/metabase/query_builder/selectors.js
@@ -140,6 +140,10 @@ export const getPKColumnIndex = createSelector(
       return;
     }
     const { cols } = result.data;
+    const hasMultiplePks = cols.filter(isPK).length > 1;
+    if (hasMultiplePks) {
+      return -1;
+    }
     return cols.findIndex(isPK);
   },
 );
@@ -151,6 +155,9 @@ export const getPKRowIndexMap = createSelector(
       return {};
     }
     const { rows } = result.data;
+    if (PKColumnIndex < 0) {
+      return rows.map((_, index) => index);
+    }
     const map = {};
     rows.forEach((row, index) => {
       const PKValue = row[PKColumnIndex];
@@ -426,6 +433,9 @@ export const getPreviousRowPKValue = createSelector(
     if (!result) {
       return;
     }
+    if (PKColumnIndex === -1) {
+      return rowIndex - 1;
+    }
     const { rows } = result.data;
     return rows[rowIndex - 1][PKColumnIndex];
   },
@@ -437,6 +447,9 @@ export const getNextRowPKValue = createSelector(
     if (!result) {
       return;
     }
+    if (PKColumnIndex === -1) {
+      return rowIndex + 1;
+    }
     const { rows } = result.data;
     return rows[rowIndex + 1][PKColumnIndex];
   },
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.styled.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.styled.tsx
index 1dbbb003add4a3c0e777d3fd91faa4cd5c409402..bf2f7ec51be8a65a2cc8d5c7d03e077a483d52f9 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.styled.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.styled.tsx
@@ -1,13 +1,13 @@
 import styled from "@emotion/styled";
 import { breakpointMinMedium } from "metabase/styled-components/theme/media-queries";
-import colors from "metabase/lib/colors";
+import { color } from "metabase/lib/colors";
 
 interface ObjectDetailModalProps {
   wide: boolean;
 }
 
 export const ObjectDetailModal = styled.div<ObjectDetailModalProps>`
-  border: 1px solid ${colors.border};
+  border: 1px solid ${color("border")};
   border-radius: 0.5rem;
   overflow: hidden;
   ${breakpointMinMedium} {
@@ -28,6 +28,11 @@ export const ObjectDetailBodyWrapper = styled.div`
   height: calc(100vh - 8rem);
 `;
 
+export const ObjectIdLabel = styled.span`
+  color: ${color("text-medium")};
+  margin-left: 0.5rem;
+`;
+
 export const ObjectDetailsTable = styled.div`
   overflow-y: auto;
   flex: 1;
@@ -41,7 +46,7 @@ export const ObjectRelationships = styled.div`
   overflow-y: auto;
   flex: 0 0 100%;
   padding: 2rem;
-  background-color: ${colors["bg-light"]};
+  background-color: ${color("bg-light")};
   ${breakpointMinMedium} {
     flex: 0 0 33.3333%;
     max-height: calc(80vh - 4rem);
@@ -52,7 +57,7 @@ export const CloseButton = styled.div`
   display: flex;
   margin-left: 1rem;
   padding-left: 1rem;
-  border-left: 1px solid ${colors.border};
+  border-left: 1px solid ${color("border")};
   ${breakpointMinMedium} {
     display: none;
   }
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
index d946119d381602666f8e62d380a6f7bb27aa71e2..42b3ec17ef1cb2d7037827348419ae4f17d28b0a 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
@@ -3,6 +3,7 @@ import { connect } from "react-redux";
 import { t } from "ttag";
 
 import Question from "metabase-lib/lib/Question";
+import { isPK } from "metabase/lib/schema_metadata";
 import { Table } from "metabase-types/types/Table";
 import { ForeignKey } from "metabase-types/api/foreignKey";
 import { DatasetData } from "metabase-types/types/Dataset";
@@ -34,12 +35,18 @@ import {
 } from "metabase/query_builder/selectors";
 import { columnSettings } from "metabase/visualizations/lib/settings/column";
 
-import { getObjectName, getIdValue, getSingleResultsRow } from "./utils";
+import {
+  getObjectName,
+  getDisplayId,
+  getIdValue,
+  getSingleResultsRow,
+} from "./utils";
 import { DetailsTable } from "./ObjectDetailsTable";
 import { Relationships } from "./ObjectRelationships";
 import {
   ObjectDetailModal,
   ObjectDetailBodyWrapper,
+  ObjectIdLabel,
   CloseButton,
   ErrorWrapper,
 } from "./ObjectDetail.styled";
@@ -214,9 +221,20 @@ export function ObjectDetailFn({
     return null;
   }
 
-  const objectName = getObjectName({ table, question });
+  const objectName = getObjectName({
+    table,
+    question,
+    cols: data.cols,
+    zoomedRow,
+  });
 
-  const hasRelationships = tableForeignKeys && !!tableForeignKeys.length;
+  const displayId = getDisplayId({ cols: data.cols, zoomedRow });
+  const hasPk = !!data.cols.find(isPK);
+  const hasRelationships = !!(
+    tableForeignKeys &&
+    !!tableForeignKeys.length &&
+    hasPk
+  );
 
   return (
     <Modal
@@ -235,7 +253,7 @@ export function ObjectDetailFn({
             <ObjectDetailHeader
               canZoom={canZoom && (canZoomNextRow || canZoomPreviousRow)}
               objectName={objectName}
-              objectId={zoomedRowID}
+              objectId={displayId}
               canZoomPreviousRow={canZoomPreviousRow}
               canZoomNextRow={canZoomNextRow}
               viewPreviousObjectDetail={viewPreviousObjectDetail}
@@ -245,8 +263,9 @@ export function ObjectDetailFn({
             <ObjectDetailBody
               data={data}
               objectName={objectName}
-              zoomedRow={zoomedRow}
+              zoomedRow={zoomedRow ?? []}
               settings={settings}
+              hasRelationships={hasRelationships}
               onVisualizationClick={onVisualizationClick}
               visualizationIsClickable={visualizationIsClickable}
               tableForeignKeys={tableForeignKeys}
@@ -262,7 +281,7 @@ export function ObjectDetailFn({
 export interface ObjectDetailHeaderProps {
   canZoom: boolean;
   objectName: string;
-  objectId: ObjectId;
+  objectId: ObjectId | null | unknown;
   canZoomPreviousRow: boolean;
   canZoomNextRow: boolean;
   viewPreviousObjectDetail: () => void;
@@ -284,7 +303,8 @@ export function ObjectDetailHeader({
     <div className="Grid border-bottom relative">
       <div className="Grid-cell">
         <h2 className="p3">
-          {objectName} {objectId}
+          {objectName}
+          {objectId !== null && <ObjectIdLabel> {objectId}</ObjectIdLabel>}
         </h2>
       </div>
       <div className="flex align-center">
@@ -331,8 +351,9 @@ export function ObjectDetailHeader({
 export interface ObjectDetailBodyProps {
   data: DatasetData;
   objectName: string;
-  zoomedRow: unknown[] | undefined;
+  zoomedRow: unknown[];
   settings: unknown;
+  hasRelationships: boolean;
   onVisualizationClick: OnVisualizationClickType;
   visualizationIsClickable: (clicked: unknown) => boolean;
   tableForeignKeys: ForeignKey[];
@@ -347,6 +368,7 @@ export function ObjectDetailBody({
   objectName,
   zoomedRow,
   settings,
+  hasRelationships = false,
   onVisualizationClick,
   visualizationIsClickable,
   tableForeignKeys,
@@ -362,12 +384,14 @@ export function ObjectDetailBody({
         onVisualizationClick={onVisualizationClick}
         visualizationIsClickable={visualizationIsClickable}
       />
-      <Relationships
-        objectName={objectName}
-        tableForeignKeys={tableForeignKeys}
-        tableForeignKeyReferences={tableForeignKeyReferences}
-        foreignKeyClicked={followForeignKey}
-      />
+      {hasRelationships && (
+        <Relationships
+          objectName={objectName}
+          tableForeignKeys={tableForeignKeys}
+          tableForeignKeyReferences={tableForeignKeyReferences}
+          foreignKeyClicked={followForeignKey}
+        />
+      )}
     </ObjectDetailBodyWrapper>
   );
 }
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.unit.spec.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.unit.spec.tsx
index 6c34637f408b4b0105dd631bf4303c4657ce8116..c4c603b8d187a5035122db22789fe11f8b0bd640 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.unit.spec.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.unit.spec.tsx
@@ -60,6 +60,7 @@ describe("Object Detail", () => {
         settings={{
           column: () => null,
         }}
+        hasRelationships={false}
         onVisualizationClick={() => null}
         visualizationIsClickable={() => false}
         tableForeignKeys={[]}
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx
index 6ea9b545465efaf1fca9ebe8c24ae30407ae2337..904ecfdc91a7fc9b63e54d4567303fc1cb072de4 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.tsx
@@ -5,6 +5,8 @@ import { t } from "ttag";
 import { DatasetData } from "metabase-types/types/Dataset";
 
 import ExpandableString from "metabase/query_builder/components/ExpandableString";
+import EmptyState from "metabase/components/EmptyState";
+
 import { isID } from "metabase/lib/schema_metadata";
 import { TYPE, isa } from "metabase/lib/types";
 import { formatValue, formatColumn } from "metabase/lib/formatting";
@@ -100,7 +102,7 @@ export function DetailsTableCell({
 
 export interface DetailsTableProps {
   data: DatasetData;
-  zoomedRow: unknown[] | undefined;
+  zoomedRow: unknown[];
   settings: unknown;
   onVisualizationClick: OnVisualizationClickType;
   visualizationIsClickable: (clicked: any) => boolean;
@@ -113,8 +115,12 @@ export function DetailsTable({
   onVisualizationClick,
   visualizationIsClickable,
 }: DetailsTableProps): JSX.Element {
-  const { rows, cols } = data;
-  const row = zoomedRow || rows[0];
+  const { cols } = data;
+  const row = zoomedRow;
+
+  if (!row?.length) {
+    return <EmptyState message={t`No details found`} />;
+  }
 
   return (
     <ObjectDetailsTable>
@@ -124,7 +130,7 @@ export function DetailsTable({
             <GridCell>
               <DetailsTableCell
                 column={column}
-                value={row[columnIndex]}
+                value={row[columnIndex] ?? t`Empty`}
                 isColumnName
                 settings={settings}
                 className="text-bold text-medium"
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.unit.spec.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.unit.spec.tsx
index 425336aa03ec890e7aba777dd24300036b0dd8ba..5108b2452c05703b80bb1884d0dfdcc79591130e 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.unit.spec.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetailsTable.unit.spec.tsx
@@ -63,7 +63,7 @@ describe("ObjectDetailsTable", () => {
       render(
         <DetailsTable
           data={objectDetailCard.data}
-          zoomedRow={undefined}
+          zoomedRow={objectDetailCard.data.rows[0]}
           onVisualizationClick={() => null}
           visualizationIsClickable={() => false}
           settings={{}}
@@ -78,7 +78,7 @@ describe("ObjectDetailsTable", () => {
       render(
         <DetailsTable
           data={invalidObjectDetailCard.data}
-          zoomedRow={undefined}
+          zoomedRow={invalidObjectDetailCard.data.rows[0]}
           onVisualizationClick={() => null}
           visualizationIsClickable={() => false}
           settings={{}}
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx
index 8bb579963645b28bb6cd5237c3e896ed62fba78c..1a6c20c4db71e9f554884a2842d8fd0a3216498f 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx
@@ -41,7 +41,7 @@ export function Relationships({
   return (
     <ObjectRelationships>
       <div className="text-bold text-medium">
-        {jt`This ${(
+        {jt`${(
           <span className="text-dark" key={objectName}>
             {objectName}
           </span>
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts b/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts
index 4a4d883f8718686f22b2ffb7bed2383f5f53795a..2e49368725cfd80685a889c9049addf3baf72420 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/utils.ts
@@ -2,22 +2,32 @@ import { t } from "ttag";
 import _ from "underscore";
 
 import { singularize } from "metabase/lib/formatting";
-import { isPK } from "metabase/lib/schema_metadata";
+import { isPK, isEntityName } from "metabase/lib/schema_metadata";
 import { Table } from "metabase-types/types/Table";
 import Question from "metabase-lib/lib/Question";
-import { DatasetData } from "metabase-types/types/Dataset";
+import { DatasetData, Column } from "metabase-types/types/Dataset";
 
 import { ObjectId } from "./types";
 
 export interface GetObjectNameArgs {
   table: Table | null;
   question: Question;
+  cols: Column[];
+  zoomedRow: unknown[] | undefined;
 }
 
 export const getObjectName = ({
   table,
   question,
+  cols,
+  zoomedRow,
 }: GetObjectNameArgs): string => {
+  const entityNameColumn = cols && cols?.findIndex(isEntityName);
+
+  if (zoomedRow?.length && zoomedRow[entityNameColumn]) {
+    return zoomedRow[entityNameColumn] as string;
+  }
+
   const tableObjectName = table && table.objectName();
   if (tableObjectName) {
     return tableObjectName;
@@ -26,7 +36,41 @@ export const getObjectName = ({
   if (questionName) {
     return singularize(questionName);
   }
-  return t`Unknown`;
+  return t`Item Detail`;
+};
+
+export interface GetDisplayIdArgs {
+  cols: Column[];
+  zoomedRow: unknown[] | undefined;
+}
+
+export const getDisplayId = ({
+  cols,
+  zoomedRow,
+}: GetDisplayIdArgs): ObjectId | null => {
+  const hasSinglePk =
+    cols.reduce(
+      (pks: number, col: Column) => (isPK(col) ? pks + 1 : pks),
+      0,
+    ) === 1;
+
+  if (!zoomedRow) {
+    return null;
+  }
+
+  if (hasSinglePk) {
+    const pkColumn = cols.findIndex(isPK);
+    return zoomedRow[pkColumn] as ObjectId;
+  }
+
+  const hasEntityName = cols && !!cols?.find(isEntityName);
+
+  if (hasEntityName) {
+    return null;
+  }
+
+  // TODO: respect user column reordering
+  return zoomedRow[0] as ObjectId;
 };
 
 export interface GetIdValueArgs {
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/utils.unit.spec.ts b/frontend/src/metabase/visualizations/components/ObjectDetail/utils.unit.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe4626f6db7dc9ea5ff12a6af998a7298f47cfad
--- /dev/null
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/utils.unit.spec.ts
@@ -0,0 +1,165 @@
+import {
+  metadata,
+  SAMPLE_DATABASE,
+  ORDERS,
+} from "__support__/sample_database_fixture";
+
+import Question from "metabase-lib/lib/Question";
+import { Card } from "metabase-types/types/Card";
+
+import { getObjectName, getDisplayId, getIdValue } from "./utils";
+
+const card = {
+  id: 1,
+  name: "Special Orders",
+  display: "table",
+  visualization_settings: {},
+  can_write: true,
+  dataset_query: {
+    type: "query",
+    database: SAMPLE_DATABASE?.id,
+    query: {
+      "source-table": ORDERS.id,
+    },
+  },
+} as Card;
+
+describe("ObjectDetail utils", () => {
+  const idCol = {
+    name: "id",
+    display_name: "ID",
+    base_type: "int",
+    effective_type: "int",
+    semantic_type: "type/PK",
+  };
+  const qtyCol = {
+    name: "qty",
+    display_name: "qty",
+    base_type: "int",
+    effective_type: "int",
+    semantic_type: "type/int",
+  };
+  const nameCol = {
+    name: "id",
+    display_name: "ID",
+    base_type: "string",
+    effective_type: "string",
+    semantic_type: "type/Name",
+  };
+
+  describe("getObjectName", () => {
+    const question = new Question(card, metadata);
+    const table = question.table();
+
+    it("should get an entity name when there is an entity name column", () => {
+      const name = getObjectName({
+        table: null,
+        question: question,
+        cols: [idCol, qtyCol, nameCol],
+        zoomedRow: [22, 33, "Giant Sprocket"],
+      });
+
+      expect(name).toBe("Giant Sprocket");
+    });
+
+    it("should get a singularized table name if no entity name is present", () => {
+      const name = getObjectName({
+        table: table as any,
+        question: question,
+        cols: [idCol, qtyCol],
+        zoomedRow: [22, 33],
+      });
+
+      expect(name).toBe("Order");
+    });
+
+    it("should get a singularized question name if neither table nor entity names are present", () => {
+      const name = getObjectName({
+        table: null,
+        question: question,
+        cols: [idCol, qtyCol],
+        zoomedRow: [22, 33],
+      });
+
+      expect(name).toBe("Special Order");
+    });
+
+    it("should fall back to default text", () => {
+      const name = getObjectName({
+        table: null,
+        question: new Question(
+          {
+            ...card,
+            name: "",
+          },
+          metadata,
+        ),
+        cols: [idCol, qtyCol],
+        zoomedRow: [22, 33],
+      });
+
+      expect(name).toBe("Item Detail");
+    });
+  });
+
+  describe("getDisplayId", () => {
+    it("should get a display id when there is a single primary key column", () => {
+      const id = getDisplayId({
+        cols: [idCol, qtyCol, nameCol],
+        zoomedRow: [22, 33, "Giant Sprocket"],
+      });
+
+      expect(id).toBe(22);
+    });
+
+    it("should return null if there is no primary key and an entity name", () => {
+      const id = getDisplayId({
+        cols: [qtyCol, nameCol],
+        zoomedRow: [33, "Giant Sprocket"],
+      });
+
+      expect(id).toBe(null);
+    });
+
+    it("should return the first row's value if there is neither an entity name nor a primary key", () => {
+      const id = getDisplayId({
+        cols: [qtyCol, qtyCol],
+        zoomedRow: [33, 44],
+      });
+
+      expect(id).toBe(33);
+    });
+  });
+
+  describe("getIdValue", () => {
+    it("should return the zoomed Row id if present", () => {
+      const id = getIdValue({
+        data: {
+          cols: [idCol, qtyCol, nameCol],
+          rows: [
+            [22, 33, "Giant Sprocket"],
+            [44, 55, "Tiny Sprocket"],
+          ],
+        },
+        zoomedRowID: 1,
+      });
+
+      expect(id).toBe(1);
+    });
+
+    // this code should no longer be reachable now that we always reach object detail by zooming
+    it("should return the primary key of the first row if now zoomed row id exists", () => {
+      const id = getIdValue({
+        data: {
+          cols: [idCol, qtyCol, nameCol],
+          rows: [
+            [22, 33, "Giant Sprocket"],
+            [44, 55, "Tiny Sprocket"],
+          ],
+        },
+      });
+
+      expect(id).toBe(22);
+    });
+  });
+});
diff --git a/frontend/src/metabase/visualizations/components/TableInteractive.jsx b/frontend/src/metabase/visualizations/components/TableInteractive.jsx
index 91790ec1dfaceaec557dc95e46a170f188e43624..2260a9d1bb04402138b1024e81eba2fdf47be0bf 100644
--- a/frontend/src/metabase/visualizations/components/TableInteractive.jsx
+++ b/frontend/src/metabase/visualizations/components/TableInteractive.jsx
@@ -3,10 +3,15 @@ import React, { Component } from "react";
 import PropTypes from "prop-types";
 import ReactDOM from "react-dom";
 import { t } from "ttag";
+import { connect } from "react-redux";
+import _ from "underscore";
+import cx from "classnames";
+import Draggable from "react-draggable";
+import { Grid, ScrollSync } from "react-virtualized";
+
 import "./TableInteractive.css";
 
 import Icon from "metabase/components/Icon";
-
 import ExternalLink from "metabase/core/components/ExternalLink";
 import Button from "metabase/core/components/Button";
 import Tooltip from "metabase/components/Tooltip";
@@ -25,15 +30,11 @@ import { fieldRefForColumn } from "metabase/lib/dataset";
 import { isAdHocModelQuestionCard } from "metabase/lib/data-modeling/utils";
 import Dimension from "metabase-lib/lib/Dimension";
 import { getScrollBarSize } from "metabase/lib/dom";
-
-import _ from "underscore";
-import cx from "classnames";
+import { zoomInRow } from "metabase/query_builder/actions";
 
 import ExplicitSize from "metabase/components/ExplicitSize";
 import MiniBar from "./MiniBar";
 
-import { Grid, ScrollSync } from "react-virtualized";
-import Draggable from "react-draggable";
 import Ellipsified from "metabase/components/Ellipsified";
 import DimensionInfoPopover from "metabase/components/MetadataInfo/DimensionInfoPopover";
 
@@ -64,6 +65,10 @@ function pickRowsToMeasure(rows, columnIndex, count = 10) {
   return rowIndexes;
 }
 
+const mapDispatchToProps = dispatch => ({
+  onZoomRow: objectId => dispatch(zoomInRow({ objectId })),
+});
+
 class TableInteractive extends Component {
   constructor(props) {
     super(props);
@@ -71,6 +76,7 @@ class TableInteractive extends Component {
     this.state = {
       columnWidths: [],
       contentWidths: null,
+      showDetailShortcut: true,
     };
     this.columnHasResized = {};
     this.headerRefs = [];
@@ -110,6 +116,7 @@ class TableInteractive extends Component {
 
     this._measure();
     this._findIDColumn(this.props.data, this.props.isPivoted);
+    this._showDetailShortcut(this.props.query, this.props.isPivoted);
   }
 
   componentWillUnmount() {
@@ -142,6 +149,7 @@ class TableInteractive extends Component {
 
     if (isDataChange) {
       this._findIDColumn(nextData, newProps.isPivoted);
+      this._showDetailShortcut(this.props.query, this.props.isPivoted);
     }
   }
 
@@ -155,9 +163,14 @@ class TableInteractive extends Component {
       IDColumnIndex: pkIndex === -1 ? null : pkIndex,
       IDColumn: pkIndex === -1 ? null : data.cols[pkIndex],
     });
-    if (pkIndex !== -1) {
-      document.addEventListener("keydown", this.onKeyDown);
-    }
+    document.addEventListener("keydown", this.onKeyDown);
+  };
+
+  _showDetailShortcut = (query, isPivoted) => {
+    const hasAggregation = !!query?.aggregations?.()?.length;
+    this.setState({
+      showDetailShortcut: !(isPivoted || hasAggregation),
+    });
   };
 
   _getColumnSettings(props) {
@@ -432,18 +445,18 @@ class TableInteractive extends Component {
     }
   }
 
-  pkClick(rowIndex) {
-    const columnIndex = this.state.IDColumnIndex;
-    const clicked = this.getCellClickedObject(rowIndex, columnIndex);
-
-    return e => this.onVisualizationClick(clicked, e.currentTarget);
-  }
+  pkClick = rowIndex => {
+    const objectId = this.state.IDColumn
+      ? this.props.data.rows[rowIndex][this.state.IDColumnIndex]
+      : rowIndex;
+    return e => this.props.onZoomRow(objectId);
+  };
 
   onKeyDown = event => {
     const detailEl = this.detailShortcutRef.current;
     const visibleDetailButton =
       !!detailEl && Array.from(detailEl.classList).includes("show") && detailEl;
-    const canViewRowDetail = !!this.state.IDColumn && !!visibleDetailButton;
+    const canViewRowDetail = !this.props.isPivoted && !!visibleDetailButton;
 
     if (event.key === "Enter" && canViewRowDetail) {
       const hoveredRowIndex = Number(detailEl.dataset.showDetailRowindex);
@@ -453,7 +466,7 @@ class TableInteractive extends Component {
 
   cellRenderer = ({ key, style, rowIndex, columnIndex }) => {
     const { data, settings } = this.props;
-    const { dragColIndex } = this.state;
+    const { dragColIndex, showDetailShortcut } = this.state;
     const { rows, cols } = data;
 
     const column = cols[columnIndex];
@@ -497,7 +510,7 @@ class TableInteractive extends Component {
         }}
         className={cx("TableInteractive-cellWrapper text-dark", {
           "TableInteractive-cellWrapper--firstColumn": columnIndex === 0,
-          padLeft: columnIndex === 0 && !this.state.IDColumn,
+          padLeft: columnIndex === 0 && !showDetailShortcut,
           "TableInteractive-cellWrapper--lastColumn":
             columnIndex === cols.length - 1,
           "TableInteractive-emptyCell": value == null,
@@ -523,12 +536,10 @@ class TableInteractive extends Component {
             : undefined
         }
         onMouseEnter={
-          this.state.IDColumn
-            ? e => this.handleHoverRow(e, rowIndex)
-            : undefined
+          showDetailShortcut ? e => this.handleHoverRow(e, rowIndex) : undefined
         }
         onMouseLeave={
-          this.state.IDColumn ? e => this.handleLeaveRow() : undefined
+          showDetailShortcut ? e => this.handleLeaveRow() : undefined
         }
         tabIndex="0"
       >
@@ -558,7 +569,7 @@ class TableInteractive extends Component {
   }
 
   getColumnPositions = () => {
-    let left = this.state.IDColumn ? SIDEBAR_WIDTH : 0;
+    let left = this.state.showDetailShortcut ? SIDEBAR_WIDTH : 0;
     return this.props.data.cols.map((col, index) => {
       const width = this.getColumnWidth({ index });
       const pos = {
@@ -577,7 +588,7 @@ class TableInteractive extends Component {
     const { cols } = this.props.data;
     const indexes = cols.map((col, index) => index);
     indexes.splice(dragColNewIndex, 0, indexes.splice(dragColIndex, 1)[0]);
-    let left = this.state.IDColumn ? SIDEBAR_WIDTH : 0;
+    let left = this.state.showDetailShortcut ? SIDEBAR_WIDTH : 0;
     const lefts = indexes.map(index => {
       const thisLeft = left;
       left += columnPositions[index].width;
@@ -626,7 +637,7 @@ class TableInteractive extends Component {
       getColumnTitle,
       renderTableHeaderWrapper,
     } = this.props;
-    const { dragColIndex } = this.state;
+    const { dragColIndex, showDetailShortcut } = this.state;
     const { cols } = data;
     const column = cols[columnIndex];
 
@@ -711,7 +722,7 @@ class TableInteractive extends Component {
             "TableInteractive-cellWrapper TableInteractive-headerCellData text-medium text-brand-hover",
             {
               "TableInteractive-cellWrapper--firstColumn": columnIndex === 0,
-              padLeft: columnIndex === 0 && !this.state.IDColumn,
+              padLeft: columnIndex === 0 && !showDetailShortcut,
               "TableInteractive-cellWrapper--lastColumn":
                 columnIndex === cols.length - 1,
               "TableInteractive-cellWrapper--active": isDragging,
@@ -798,13 +809,15 @@ class TableInteractive extends Component {
   };
 
   getDisplayColumnWidth = ({ index: displayIndex }) => {
-    if (this.state.IDColumn && displayIndex === 0) {
+    if (this.state.showDetailShortcut && displayIndex === 0) {
       return SIDEBAR_WIDTH;
     }
 
-    // if we have an ID column, we've added a column of empty cells and need to shift
+    // if the detail shortcut is visible, we've added a column of empty cells and need to shift
     // the display index to get the data index
-    const dataIndex = this.state.IDColumn ? displayIndex - 1 : displayIndex;
+    const dataIndex = this.state.showDetailShortcut
+      ? displayIndex - 1
+      : displayIndex;
 
     return this.getColumnWidth({ index: dataIndex });
   };
@@ -880,7 +893,7 @@ class TableInteractive extends Component {
     }
 
     const headerHeight = this.props.tableHeaderHeight || HEADER_HEIGHT;
-    const gutterColumn = this.state.IDColumn ? 1 : 0;
+    const gutterColumn = this.state.showDetailShortcut ? 1 : 0;
 
     return (
       <ScrollSync>
@@ -1043,6 +1056,7 @@ export default _.compose(
   ExplicitSize({
     refreshMode: props => (props.isDashboard ? "debounce" : "throttle"),
   }),
+  connect(null, mapDispatchToProps),
   memoizeClass(
     "_getCellClickedObjectCached",
     "_getHeaderClickedObjectCached",
diff --git a/frontend/test/metabase/modes/components/drill/ObjectDetailDrill.unit.spec.js b/frontend/test/metabase/modes/components/drill/ObjectDetailDrill.unit.spec.js
index adcd2867c8aed69e05157b31814fc7d5193a66c5..034ebeab0267d51b8a4851cf82246ec88a4d4bb5 100644
--- a/frontend/test/metabase/modes/components/drill/ObjectDetailDrill.unit.spec.js
+++ b/frontend/test/metabase/modes/components/drill/ObjectDetailDrill.unit.spec.js
@@ -90,6 +90,7 @@ describe("ObjectDetailDrill", () => {
   describe("PK cells", () => {
     describe("general", () => {
       const mockDispatch = jest.fn();
+      const mockGetState = () => ({ qb: { queryResults: {}, card: {} } });
       const { actions, cellValue } = setup({
         column: ORDERS.ID.column(),
       });
@@ -102,7 +103,7 @@ describe("ObjectDetailDrill", () => {
 
       it("should return correct redux action", () => {
         const [action] = actions;
-        action.action()(mockDispatch);
+        action.action()(mockDispatch, mockGetState);
         expect(mockDispatch).toHaveBeenCalledWith({
           type: ZOOM_IN_ROW,
           payload: {
diff --git a/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js b/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js
index 0bb494001d2b11073ecf1f4e4518ac83be777f96..389d512e8a48d3564a4347fce8521c19e2ec4779 100644
--- a/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js
+++ b/frontend/test/metabase/scenarios/dashboard/dashboard-drill.cy.spec.js
@@ -403,7 +403,7 @@ describe("scenarios > dashboard > dashboard drill", () => {
     cy.wait("@dataset");
 
     cy.findByTestId("object-detail").within(() => {
-      cy.findByText(PK_VALUE);
+      cy.findAllByText(PK_VALUE);
     });
 
     const pattern = new RegExp(`/question\\?objectId=${PK_VALUE}#*`);
diff --git a/frontend/test/metabase/scenarios/models/models-metadata.cy.spec.js b/frontend/test/metabase/scenarios/models/models-metadata.cy.spec.js
index c4113d40b4ff12468b5670bbc7fd8cd67130c937..4f1579b98118ec74a44e7b8c813fd370f121cfe9 100644
--- a/frontend/test/metabase/scenarios/models/models-metadata.cy.spec.js
+++ b/frontend/test/metabase/scenarios/models/models-metadata.cy.spec.js
@@ -218,8 +218,8 @@ describe("scenarios > models metadata", () => {
         drillFK({ id: 1 });
         cy.wait("@dataset");
         cy.findByTestId("object-detail").within(() => {
-          cy.findByText("1");
-          cy.findByText("Hudson Borer");
+          cy.findByText("68883"); // zip
+          cy.findAllByText("Hudson Borer");
         });
 
         cy.go("back"); // close modal
@@ -232,8 +232,8 @@ describe("scenarios > models metadata", () => {
         drillFK({ id: 7 });
         cy.wait("@dataset");
         cy.findByTestId("object-detail").within(() => {
-          cy.findByText("7");
-          cy.findByText("perry.ruecker");
+          cy.findAllByText("7");
+          cy.findAllByText("perry.ruecker");
         });
       });
     });
@@ -255,8 +255,8 @@ describe("scenarios > models metadata", () => {
           drillDashboardFK({ id: 1 });
           cy.wait("@dataset");
           cy.findByTestId("object-detail").within(() => {
-            cy.findByText("1");
-            cy.findByText("Hudson Borer");
+            cy.findAllByText("1");
+            cy.findAllByText("Hudson Borer");
           });
 
           cy.go("back");
@@ -267,8 +267,8 @@ describe("scenarios > models metadata", () => {
           drillDashboardFK({ id: 7 });
           cy.wait("@dataset");
           cy.findByTestId("object-detail").within(() => {
-            cy.findByText("7");
-            cy.findByText("perry.ruecker");
+            cy.findAllByText("7");
+            cy.findAllByText("perry.ruecker");
           });
         });
       });
diff --git a/frontend/test/metabase/scenarios/question/reproductions/16938-nested-native-query-pk-drill.cy.spec.js b/frontend/test/metabase/scenarios/question/reproductions/16938-nested-native-query-pk-drill.cy.spec.js
index 0d7e0debd8eff0f0a0bf0e9609452ad8fa5d5edf..db97d880d454742d844fd29ee916c2b95a915594 100644
--- a/frontend/test/metabase/scenarios/question/reproductions/16938-nested-native-query-pk-drill.cy.spec.js
+++ b/frontend/test/metabase/scenarios/question/reproductions/16938-nested-native-query-pk-drill.cy.spec.js
@@ -29,7 +29,7 @@ describe("issue 16938", () => {
       .click();
 
     cy.findByTestId("object-detail").within(() => {
-      cy.findByText(`Order ${ORDER_ID}`);
+      cy.findByText("37.65");
     });
   });
 });
diff --git a/frontend/test/metabase/scenarios/visualizations/object_detail.cy.spec.js b/frontend/test/metabase/scenarios/visualizations/object_detail.cy.spec.js
index fea720d7c51bcf9803e1eef3233b981bbd6e5f19..51af1fff44d0143cef4db8401dcf3901a0d2360f 100644
--- a/frontend/test/metabase/scenarios/visualizations/object_detail.cy.spec.js
+++ b/frontend/test/metabase/scenarios/visualizations/object_detail.cy.spec.js
@@ -58,7 +58,7 @@ describe("scenarios > question > object details", () => {
 
     drillFK({ id: 1 });
 
-    assertUserDetailView({ id: 1 });
+    assertUserDetailView({ id: 1, name: "Hudson Borer" });
     getPreviousObjectDetailButton().should("not.exist");
     getNextObjectDetailButton().should("not.exist");
 
@@ -69,7 +69,7 @@ describe("scenarios > question > object details", () => {
     changeSorting("User ID", "desc");
     drillFK({ id: 2500 });
 
-    assertDetailView({ id: 2500, entityName: "Person", byFK: true });
+    assertUserDetailView({ id: 2500, name: "Kenny Schmidt" });
     getPreviousObjectDetailButton().should("not.exist");
     getNextObjectDetailButton().should("not.exist");
   });
@@ -125,7 +125,8 @@ describe("scenarios > question > object details", () => {
     cy.url().should("contain", "objectId=2");
 
     cy.findByTestId("object-detail")
-      .findByText("Domenica Williamson")
+      .findAllByText("Domenica Williamson")
+      .last()
       .click();
     // Popover is blocking the city. If it renders, Cypress will not be able to click on "Searsboro" and the test will fail.
     // Unfortunately, asserting that the popover does not exist will give us a false positive result.
@@ -168,8 +169,8 @@ function assertOrderDetailView({ id }) {
   assertDetailView({ id, entityName: "Order" });
 }
 
-function assertUserDetailView({ id }) {
-  assertDetailView({ id, entityName: "Person", byFK: true });
+function assertUserDetailView({ id, name }) {
+  assertDetailView({ id, entityName: name, byFK: true });
 }
 
 function getPreviousObjectDetailButton() {