Wicked Smart Data
LearnInsightsAboutContact
Sign InLet's Build
LearnInsightsAboutContact
Sign InLet's Build
Wicked Smart Data

Intelligence, automation, and expert execution — plus an elite library of free knowledge. We turn complexity into competitive advantage.

Start a conversation

Platform

  • Learning Paths
  • Insights
  • RSS Feed

Company

  • About
  • Contact
  • Work With Us

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Wicked Smart Data. All rights reserved.

Intelligence · Automation · Advantage

All Insights
Microsoft Fabric

Configuring OneLake Shortcuts to a Fabric Lakehouse as the Data Source for a Fabric Warehouse: Avoiding Data Duplication While Enabling T-SQL Queries Across Both Engines

Learn how to create OneLake shortcuts in a Fabric Warehouse that point to lakehouse Delta tables — enabling T-SQL queries across both engines with zero data duplication. This lesson covers the full pattern from shortcut creation to cross-engine joins, security configuration, metadata sync, and performance tuning.

⚡ Practitioner24 min readSep 22, 2026Updated Sep 22, 2026
Configuring OneLake Shortcuts to a Fabric Lakehouse as the Data Source for a Fabric Warehouse: Avoiding Data Duplication While Enabling T-SQL Queries Across Both Engines
On this page
  • Introduction
  • Prerequisites
  • Why This Works: OneLake as the Common Storage Substrate
  • Setting Up the Scenario: A Realistic Architecture
  • Step 1: Verify Your Lakehouse Tables Are Ready
  • Step 2: Open the Warehouse and Navigate to the Shortcut Dialog
  • Step 3: Browse to the Lakehouse Table Path and Create the Shortcut
  • Step 4: Query the Shortcut Tables with T-SQL
  • Step 5: Create Views to Abstract the Shortcut Layer
  • Step 6: Understanding and Managing Metadata Sync
Security: Getting Permissions Right Across Workspace Boundaries
  • Performance Characteristics and When to Choose This Pattern
  • Hands-On Exercise
  • Common Mistakes & Troubleshooting
  • Summary & Next Steps
  • Configuring OneLake Shortcuts to a Fabric Lakehouse as the Data Source for a Fabric Warehouse: Avoiding Data Duplication While Enabling T-SQL Queries Across Both Engines

    Introduction

    Here's a scenario you'll encounter sooner or later: your team has invested real effort in building a medallion lakehouse architecture. The gold layer Delta tables are clean, well-structured, and already powering Direct Lake reports in Power BI. Then a business analyst asks, "Can I just query this in SQL Server Management Studio with proper T-SQL joins?" Or a data engineer needs to build a stored procedure that cross-references gold layer transaction data with warehouse-specific dimension tables. Suddenly you need a Fabric Warehouse — but the last thing you want to do is copy all that lakehouse data into a second storage location, pay twice for the compute to keep it fresh, and introduce sync lag between two versions of the same truth.

    This is exactly the problem that OneLake shortcuts solve elegantly. By creating a shortcut inside a Fabric Warehouse that points to Delta tables in your lakehouse, the warehouse can execute T-SQL against the underlying Parquet files in OneLake without moving a single byte. The lakehouse remains the system of record; the warehouse gains full T-SQL query capability over that data. You get both engines working over one physical copy of the data.

    By the end of this lesson, you'll know how to configure this pattern end-to-end, understand why it works at the storage layer, write cross-engine T-SQL queries that join warehouse-native tables to lakehouse-sourced shortcuts, and avoid the common traps that cause permissions failures, stale metadata, and query performance surprises.

    What you'll learn:

    • Why OneLake's unified storage layer makes zero-copy shortcuts possible between lakehouses and warehouses
    • How to create a shortcut in a Fabric Warehouse pointing to a lakehouse Delta table, step by step
    • How the warehouse exposes shortcut data through its T-SQL endpoint and how to query it
    • How to write T-SQL queries that join warehouse-native tables to shortcut-backed external tables
    • Performance characteristics, security implications, and when this pattern beats the alternatives

    Prerequisites

    You should be comfortable with the following before diving in:

    • You've worked with at least one Fabric Lakehouse and understand Delta tables and the SQL Analytics Endpoint
    • You understand the fundamental difference between lakehouses and warehouses — if not, review Fabric Lakehouse vs Warehouse: Choosing the Right Store for Your Workload first
    • You know what OneLake shortcuts are at a conceptual level — the OneLake Explained article covers the foundations
    • You have a Fabric capacity (trial F2 or higher) and an existing workspace with at least one lakehouse containing managed Delta tables

    Why This Works: OneLake as the Common Storage Substrate

    Before you touch any configuration, you need to understand why this is possible at all — because it's not obvious if you're coming from a traditional data platform background where separate systems mean separate storage.

    In Microsoft Fabric, both lakehouses and warehouses store their underlying data in OneLake, Fabric's unified storage layer. A lakehouse stores its Delta tables as Parquet files plus _delta_log transaction logs under a path like {workspace}/lakehouses/{lakehouse-name}/Tables/{table-name}/. A Fabric Warehouse stores its tables similarly in OneLake under {workspace}/warehouses/{warehouse-name}/. Both are just files in the same OneLake file system.

    A shortcut is a metadata pointer — a symbolic link inside OneLake — that says "when something accesses this path, redirect to that path." When a warehouse shortcut points to a lakehouse table's OneLake path, the warehouse's T-SQL engine reads the Parquet files directly from the lakehouse location. No ETL. No COPY INTO. No data movement. The warehouse compute layer reads the data through the shortcut pointer as though it lives natively in the warehouse.

    Key insight

    Shortcuts don't replicate data — they redirect read I/O. The physical Parquet files exist exactly once in OneLake. The lakehouse writes to them, and the warehouse reads from them through the shortcut pointer. This means updates written by Spark or Dataflow Gen2 to the lakehouse table are immediately visible in the warehouse without any refresh step.

    This is meaningfully different from creating a linked server or an external table that queries a remote database. There's no network hop to another service. The warehouse engine and the lakehouse engine are both reading the same OneLake storage — it's more like two query engines sharing the same disk than two systems talking over a network.


    Setting Up the Scenario: A Realistic Architecture

    Let's ground this in a real-world scenario. You're the data engineer for a mid-size retail company. Your setup looks like this:

    Lakehouse: retail_gold — contains gold layer Delta tables produced by PySpark transformations, including:

    • fact_sales — 200M+ row transaction fact table, partitioned by sale_date
    • dim_customer — 1.2M customer records with demographic attributes
    • dim_product — 85,000 product records with hierarchy and cost attributes

    Warehouse: retail_warehouse — contains tables managed directly by the warehouse engine, including:

    • finance.budget_targets — quarterly budget targets by product category and region, loaded by a finance team pipeline
    • ops.territory_mapping — sales territory assignments maintained by operations, updated via T-SQL procedures

    The reporting team needs queries that join fact_sales from the lakehouse against budget_targets from the warehouse — comparing actuals to targets. The finance analysts also want to run ad-hoc T-SQL queries, not Spark notebooks. Rather than copying the entire fact table and dimension tables into the warehouse, you'll create shortcuts so the warehouse can read them directly.

    Note

    This scenario assumes your lakehouse and warehouse are in the same Fabric workspace. Cross-workspace shortcuts are also supported, but require additional attention to workspace-level permissions which we'll cover in the security section.


    Step 1: Verify Your Lakehouse Tables Are Ready

    Shortcuts in a warehouse can only point to Delta table paths — not to raw files in the Files section of a lakehouse. If your lakehouse tables appear in the Tables section and were created as managed Delta tables (via Spark, Dataflow Gen2, or pipeline Copy Activity with Delta as the target format), you're good.

    Open your retail_gold lakehouse in the Fabric portal and switch to the SQL analytics endpoint view. Run a quick validation query:

    SELECT
        COUNT(*) AS row_count,
        MAX(sale_date) AS latest_sale,
        MIN(sale_date) AS earliest_sale
    FROM dbo.fact_sales;
    

    If this returns results, the Delta table is valid and readable through the SQL engine. That's a prerequisite for the warehouse shortcut to work — if the lakehouse's own SQL endpoint can't read the table, neither can the warehouse via shortcut.

    Also note the exact table names. They'll become the shortcut names inside the warehouse, and the warehouse T-SQL engine is case-sensitive in how it resolves them.

    Tip

    Run DESCRIBE DETAIL on your Delta tables in a Spark notebook to verify the table format version and file statistics. Tables written by older Spark versions using Delta protocol 1.x work fine, but if you have tables written with Delta 3.x liquid clustering features, verify that the Fabric warehouse runtime supports that Delta reader protocol version before assuming the shortcut will work transparently.


    Step 2: Open the Warehouse and Navigate to the Shortcut Dialog

    In the Fabric portal, navigate to your retail_warehouse item and open it. You'll land in the warehouse editor, which shows the object explorer on the left with your warehouse's native schemas and tables.

    To create a shortcut, you need to reach the warehouse's OneLake view, not the SQL editor. Look for the New shortcut option — in the current Fabric UI, you access this through the toolbar at the top of the warehouse editor. Click the New shortcut button (it's typically in the same toolbar area where you'd see options for new tables or queries).

    This opens the New shortcut wizard, which will ask you to choose the shortcut source. You'll see options including:

    • Microsoft OneLake
    • Azure Data Lake Storage Gen2
    • Amazon S3
    • Google Cloud Storage (in preview at time of writing)

    Choose Microsoft OneLake. This is the key distinction — you're not pointing to an external storage account, you're creating an internal OneLake-to-OneLake shortcut. Authentication is handled automatically via the Fabric identity model, which means no credentials to manage and no expiry surprises.

    Warning

    Do not choose ADLS Gen2 even if your lakehouse data is technically stored in an Azure storage account behind OneLake. Always use the OneLake option for Fabric-to-Fabric shortcuts. Using the ADLS Gen2 path directly bypasses Fabric's access control model and creates a credential management burden you don't need.


    Step 3: Browse to the Lakehouse Table Path and Create the Shortcut

    After selecting Microsoft OneLake as the source, the wizard presents a tree view of your OneLake namespace. You'll navigate:

    1. Select your workspace — in this case, the workspace containing retail_gold
    2. Expand the workspace to see its Fabric items
    3. Select the retail_gold lakehouse
    4. Expand it to see its contents, then expand Tables
    5. Select the individual table — for example, fact_sales

    The wizard will show you the OneLake path it will use, which looks something like:

    onelake://retail_gold.lakehouse/Tables/fact_sales
    

    Give the shortcut a name. By default it'll match the table name (fact_sales), which is what you want — keeping names consistent makes T-SQL queries predictable and avoids confusion when colleagues look at the warehouse object catalog.

    Click Create. Repeat this process for dim_customer and dim_product.

    After creating all three shortcuts, refresh the object explorer in the warehouse editor. You'll see the shortcut tables appear in the warehouse's object tree, typically under the dbo schema, distinguished from native warehouse tables by a small shortcut icon.

    Note

    Shortcuts in a warehouse are exposed as external tables from the T-SQL perspective. They appear in the sys.external_tables catalog view rather than sys.tables. This matters when writing metadata queries or when tools try to enumerate warehouse objects — you may need to query both catalog views to get a complete picture.


    Step 4: Query the Shortcut Tables with T-SQL

    Now for the payoff. Open a new SQL query in your warehouse editor. The shortcut tables are immediately queryable with standard T-SQL syntax:

    -- Basic validation: count rows and check the data range
    SELECT
        COUNT(*)        AS total_transactions,
        SUM(sale_amount) AS total_revenue,
        MIN(sale_date)  AS first_sale,
        MAX(sale_date)  AS last_sale
    FROM dbo.fact_sales;
    

    This reads directly from the Delta Parquet files in the retail_gold lakehouse. No data was moved. The warehouse T-SQL engine translates the query into a scan of the Parquet files through the shortcut path.

    Now let's write the cross-engine query that justifies this whole architecture — joining the shortcut-backed lakehouse tables against the warehouse-native finance table:

    SELECT
        dp.category_name,
        dp.subcategory_name,
        dc.region,
        DATEPART(YEAR, fs.sale_date)    AS sale_year,
        DATEPART(QUARTER, fs.sale_date) AS sale_quarter,
        SUM(fs.sale_amount)             AS actual_revenue,
        MAX(bt.revenue_target)          AS budget_target,
        SUM(fs.sale_amount) - MAX(bt.revenue_target) AS variance,
        ROUND(
            (SUM(fs.sale_amount) / NULLIF(MAX(bt.revenue_target), 0)) * 100,
            2
        )                               AS pct_of_target
    FROM
        dbo.fact_sales AS fs
        INNER JOIN dbo.dim_product  AS dp ON fs.product_key = dp.product_key
        INNER JOIN dbo.dim_customer AS dc ON fs.customer_key = dc.customer_key
        INNER JOIN finance.budget_targets AS bt
            ON dp.category_name = bt.category_name
            AND dc.region = bt.region
            AND DATEPART(YEAR, fs.sale_date) = bt.fiscal_year
            AND DATEPART(QUARTER, fs.sale_date) = bt.fiscal_quarter
    GROUP BY
        dp.category_name,
        dp.subcategory_name,
        dc.region,
        DATEPART(YEAR, fs.sale_date),
        DATEPART(QUARTER, fs.sale_date)
    ORDER BY
        sale_year,
        sale_quarter,
        actual_revenue DESC;
    

    In this query, dbo.fact_sales, dbo.dim_product, and dbo.dim_customer are all shortcut-backed Delta tables from the lakehouse. finance.budget_targets is a native warehouse table. The T-SQL engine joins them as though they're all local — because from the storage perspective, they effectively are.

    Key insight

    The warehouse query optimizer treats shortcut tables similarly to external tables when building execution plans. This means predicate pushdown works for simple column filters — if you filter WHERE fs.sale_date >= '2024-01-01', the engine will skip Parquet row groups that don't match based on Delta statistics. However, complex join-pushdown optimizations that work across warehouse-native tables may not apply to shortcut tables. Design your queries to filter shortcut tables early and join results to native tables last.


    Step 5: Create Views to Abstract the Shortcut Layer

    Direct references to shortcut table names in application queries are fine for ad-hoc work, but in production you should abstract the access through views. This gives you flexibility to change the underlying shortcut path or replace a shortcut with a native copy (if, say, you decide the performance trade-off isn't worth it) without changing every downstream query.

    -- Create a reporting schema to hold the business-facing views
    CREATE SCHEMA reporting;
    GO
    
    -- Wrap the shortcut tables in views with business-friendly column names
    CREATE VIEW reporting.vw_sales_actuals AS
    SELECT
        fs.sale_transaction_id,
        fs.sale_date,
        fs.sale_amount,
        fs.quantity_sold,
        fs.discount_pct,
        dp.product_name,
        dp.category_name,
        dp.subcategory_name,
        dp.unit_cost,
        dc.customer_name,
        dc.region,
        dc.segment
    FROM
        dbo.fact_sales      AS fs
        INNER JOIN dbo.dim_product  AS dp ON fs.product_key = dp.product_key
        INNER JOIN dbo.dim_customer AS dc ON fs.customer_key = dc.customer_key;
    GO
    
    -- The budget vs actuals report can now reference clean view names
    CREATE VIEW reporting.vw_budget_vs_actuals AS
    SELECT
        sa.category_name,
        sa.region,
        DATEPART(YEAR, sa.sale_date)    AS sale_year,
        DATEPART(QUARTER, sa.sale_date) AS sale_quarter,
        SUM(sa.sale_amount)             AS actual_revenue,
        MAX(bt.revenue_target)          AS budget_target,
        SUM(sa.sale_amount) - MAX(bt.revenue_target) AS variance
    FROM
        reporting.vw_sales_actuals AS sa
        INNER JOIN finance.budget_targets AS bt
            ON sa.category_name = bt.category_name
            AND sa.region = bt.region
            AND DATEPART(YEAR, sa.sale_date) = bt.fiscal_year
            AND DATEPART(QUARTER, sa.sale_date) = bt.fiscal_quarter
    GROUP BY
        sa.category_name,
        sa.region,
        DATEPART(YEAR, sa.sale_date),
        DATEPART(QUARTER, sa.sale_date);
    GO
    

    Now analysts can run SELECT * FROM reporting.vw_budget_vs_actuals WHERE sale_year = 2024 without knowing anything about shortcuts, Delta tables, or the underlying architecture. If you later decide to materialize the sales data into the warehouse for performance reasons, you swap out the view definition — the analyst's query doesn't change.

    This approach also integrates cleanly with building a Fabric data warehouse with T-SQL, where views and cross-database query patterns are discussed in depth.


    Step 6: Understanding and Managing Metadata Sync

    Here's something that surprises many practitioners: when the lakehouse schema changes — new columns added, columns renamed — the warehouse shortcut does not automatically reflect those changes. The shortcut still points to the correct Parquet files, but the warehouse's internal metadata cache for the external table may be stale.

    If someone adds a promotion_code column to fact_sales in the lakehouse via a PySpark notebook, and you immediately run:

    SELECT promotion_code FROM dbo.fact_sales;
    

    ...you may get an error like "Invalid column name 'promotion_code'" even though the data physically contains that column.

    To refresh the warehouse's metadata for a shortcut table, you need to trigger a sync. In the current Fabric warehouse experience, you can do this by:

    1. Navigating to the warehouse in the Fabric portal
    2. Opening the Refresh option in the toolbar — this triggers a metadata synchronization job that re-reads the Delta table schema and updates the warehouse's internal catalog

    Alternatively, you can drop and recreate the shortcut to force a full schema refresh:

    -- Check current column definitions for the shortcut table
    SELECT
        c.name           AS column_name,
        t.name           AS data_type,
        c.max_length,
        c.is_nullable
    FROM
        sys.external_tables AS et
        INNER JOIN sys.columns AS c ON et.object_id = c.object_id
        INNER JOIN sys.types   AS t ON c.user_type_id = t.user_type_id
    WHERE
        et.name = 'fact_sales'
    ORDER BY c.column_id;
    

    If the column count doesn't match what you know is in the Delta table, you need a metadata refresh.

    Warning

    If your lakehouse table undergoes schema changes frequently (for example, you're in active development and columns are being added or restructured regularly), build a metadata refresh step into your pipeline orchestration. You can trigger a warehouse metadata refresh programmatically via the Fabric REST API as a pipeline activity. Don't rely on analysts manually noticing stale column definitions — they'll just assume the column doesn't exist.

    Schema evolution patterns in your Delta tables — and how to manage them across medallion layers — are covered in detail in the handling schema evolution in Fabric lakehouse Delta tables article.


    Security: Getting Permissions Right Across Workspace Boundaries

    Security for shortcuts is one of the most common sources of confusion, so let's be explicit about how it works.

    Same-workspace shortcuts are the simplest case. If your retail_gold lakehouse and retail_warehouse warehouse are in the same Fabric workspace, any user with at least Viewer role on the workspace can read the shortcut data in the warehouse (assuming they have read access to the warehouse itself). The workspace identity automatically has access to read from both items.

    Cross-workspace shortcuts require more deliberate setup. If the lakehouse lives in a Data Engineering workspace and the warehouse lives in a Finance Analytics workspace, the warehouse's managed identity must be granted at least Read access to the lakehouse item in the Data Engineering workspace. Without this, the shortcut creation succeeds (it's just a metadata pointer), but queries against the shortcut will fail at runtime with an access denied error — which is particularly confusing because the error appears during query execution, not during shortcut creation.

    To grant this access:

    1. Go to the Data Engineering workspace
    2. Open the retail_gold lakehouse's settings
    3. Under Manage permissions, add the identity of the Finance Analytics workspace (or the specific users who need access) with Read permission

    Warning

    Don't confuse workspace role permissions with item-level permissions. A user can have the Viewer role on the Finance Analytics workspace — giving them the ability to query the warehouse — without having any access to the Data Engineering workspace. For cross-workspace shortcuts, you need to explicitly grant item-level read access to the lakehouse. If you skip this step, queries will silently fail for affected users even though the shortcut appears healthy in the warehouse.

    Row-level security defined on the lakehouse SQL analytics endpoint does not automatically propagate through the warehouse shortcut. If you have RLS policies on fact_sales in the lakehouse, those policies apply when users query through the lakehouse's SQL endpoint — but when users query dbo.fact_sales in the warehouse through the shortcut, they bypass those lakehouse-level policies. You need to implement equivalent RLS directly in the warehouse if the security requirement applies to warehouse consumers. This is covered thoroughly in the implementing row-level security in a Fabric warehouse guide.


    Performance Characteristics and When to Choose This Pattern

    You've seen that shortcuts enable zero-copy cross-engine queries, but that doesn't mean performance is free. Here's what to understand about the trade-offs.

    What works well:

    • Large scans with selective filters on partitioned columns. If fact_sales is partitioned by sale_date, a query like WHERE sale_date >= '2024-Q1-01' will use partition pruning and skip large swaths of data. This is because the warehouse reads the Delta transaction log to understand partition layout.
    • Aggregation-heavy queries where the bottleneck is data transfer, not compute. Since there's no data movement, shortcut queries avoid the ETL delay that would exist if you had to copy data first.
    • Queries that join small warehouse-native tables against large shortcut tables — the optimizer can handle this reasonably well.

    What to watch:

    • Highly selective point lookups on non-partitioned, non-indexed columns. Delta tables in a shortcut don't have warehouse-style columnstore indexes. A query like WHERE transaction_id = 'TXN-8472921' will scan all Parquet files unless the Delta table has Z-Order applied on that column. See the Delta table performance optimization guide for how to address this at the lakehouse layer.
    • Complex joins between multiple large shortcut tables. The warehouse optimizer doesn't have statistics about shortcut table data distribution the same way it does for native tables. Large multi-table joins that require shuffle operations can be slow.
    • Queries with many small Parquet files. If the lakehouse table hasn't been OPTIMIZE'd recently, a table with thousands of small files will generate significant file-open overhead. Run OPTIMIZE on your gold layer Delta tables regularly as part of your pipeline orchestration.

    When to use shortcuts vs materializing into the warehouse:

    Scenario Recommendation
    Gold layer data is authoritative and already well-optimized Shortcut — no duplication needed
    Finance team needs pure T-SQL with stored procedures Shortcut works; add views to abstract
    Highly selective point-lookup queries (e.g., order lookup by ID) Consider materializing; shortcuts favor scans
    Data must be visible in warehouse within seconds of lakehouse write Shortcut — no sync lag
    Regulatory requirement that warehouse contains a self-contained copy Materialize — shortcuts are read-through
    Complex aggregations with many joins between large tables Test both; shortcuts may be adequate with OPTIMIZE

    Hands-On Exercise

    In this exercise, you'll build the complete shortcut-based cross-engine query pattern using a realistic dataset.

    Setup: Create a lakehouse called sales_gold and a warehouse called analytics_warehouse in the same workspace.

    Step 1: Create the lakehouse tables using a Spark notebook.

    Open a notebook attached to sales_gold and run:

    from pyspark.sql import SparkSession
    from pyspark.sql.types import *
    from pyspark.sql.functions import *
    import random
    from datetime import date, timedelta
    
    spark = SparkSession.builder.getOrCreate()
    
    # Generate a realistic dim_store table
    store_data = [
        (1, "Chicago Downtown",     "Chicago",     "IL", "Midwest",     "Tier 1"),
        (2, "Chicago Oak Park",     "Chicago",     "IL", "Midwest",     "Tier 2"),
        (3, "Houston Galleria",     "Houston",     "TX", "South",       "Tier 1"),
        (4, "Houston Midtown",      "Houston",     "TX", "South",       "Tier 2"),
        (5, "Seattle Capitol Hill", "Seattle",     "WA", "West",        "Tier 1"),
        (6, "Seattle Bellevue",     "Seattle",     "WA", "West",        "Tier 2"),
        (7, "New York Midtown",     "New York",    "NY", "Northeast",   "Tier 1"),
        (8, "New York Brooklyn",    "New York",    "NY", "Northeast",   "Tier 2"),
    ]
    
    store_schema = StructType([
        StructField("store_key",   IntegerType(), False),
        StructField("store_name",  StringType(),  False),
        StructField("city",        StringType(),  False),
        StructField("state",       StringType(),  False),
        StructField("region",      StringType(),  False),
        StructField("store_tier",  StringType(),  False),
    ])
    
    dim_store_df = spark.createDataFrame(store_data, store_schema)
    dim_store_df.write.format("delta").mode("overwrite").saveAsTable("dim_store")
    
    # Generate fact_daily_sales with 2 years of data
    categories = ["Electronics", "Apparel", "Home & Garden", "Sports", "Beauty"]
    base_date = date(2023, 1, 1)
    sales_rows = []
    
    for day_offset in range(730):  # 2 years
        sale_date = base_date + timedelta(days=day_offset)
        for store_key in range(1, 9):
            for cat_idx, category in enumerate(categories):
                revenue = round(random.uniform(5000, 95000), 2)
                txn_count = random.randint(50, 800)
                sales_rows.append((
                    f"S{day_offset:04d}T{store_key}C{cat_idx}",
                    sale_date.isoformat(),
                    store_key,
                    category,
                    revenue,
                    txn_count,
                    round(revenue / txn_count, 2)
                ))
    
    sales_schema = StructType([
        StructField("sale_id",          StringType(),  False),
        StructField("sale_date",        StringType(),  False),
        StructField("store_key",        IntegerType(), False),
        StructField("category",         StringType(),  False),
        StructField("daily_revenue",    DoubleType(),  False),
        StructField("transaction_count",IntegerType(), False),
        StructField("avg_basket_size",  DoubleType(),  False),
    ])
    
    fact_df = spark.createDataFrame(sales_rows, sales_schema)
    fact_df = fact_df.withColumn("sale_date", col("sale_date").cast("date"))
    
    # Write partitioned by year and month for query performance
    fact_df.write \
        .format("delta") \
        .mode("overwrite") \
        .partitionBy("sale_date") \
        .saveAsTable("fact_daily_sales")
    
    print(f"Created fact_daily_sales with {fact_df.count():,} rows")
    print("Created dim_store with 8 stores")
    

    Step 2: Create a budget targets table in the warehouse.

    Open analytics_warehouse and run this in the SQL editor:

    CREATE SCHEMA planning;
    GO
    
    CREATE TABLE planning.store_budget (
        budget_id      INT           IDENTITY(1,1) PRIMARY KEY,
        region         VARCHAR(50)   NOT NULL,
        category       VARCHAR(100)  NOT NULL,
        fiscal_year    INT           NOT NULL,
        fiscal_quarter INT           NOT NULL,
        revenue_target DECIMAL(18,2) NOT NULL,
        created_at     DATETIME2     DEFAULT GETUTCDATE()
    );
    
    INSERT INTO planning.store_budget (region, category, fiscal_year, fiscal_quarter, revenue_target)
    VALUES
        ('Midwest',   'Electronics',   2024, 1, 1250000.00),
        ('Midwest',   'Electronics',   2024, 2, 1380000.00),
        ('Midwest',   'Electronics',   2024, 3, 1450000.00),
        ('Midwest',   'Electronics',   2024, 4, 1900000.00),
        ('Midwest',   'Apparel',       2024, 1,  890000.00),
        ('Midwest',   'Apparel',       2024, 2,  920000.00),
        ('Midwest',   'Apparel',       2024, 3,  950000.00),
        ('Midwest',   'Apparel',       2024, 4, 1200000.00),
        ('South',     'Electronics',   2024, 1, 1100000.00),
        ('South',     'Electronics',   2024, 2, 1200000.00),
        ('South',     'Electronics',   2024, 3, 1300000.00),
        ('South',     'Electronics',   2024, 4, 1750000.00),
        ('West',      'Electronics',   2024, 1, 1050000.00),
        ('West',      'Apparel',       2024, 1,  780000.00),
        ('Northeast', 'Electronics',   2024, 1, 1400000.00),
        ('Northeast', 'Apparel',       2024, 1,  980000.00);
    

    Step 3: Create the shortcuts. Follow the steps from earlier in this lesson to create shortcuts in analytics_warehouse pointing to fact_daily_sales and dim_store in sales_gold.

    Step 4: Write the cross-engine analysis query.

    -- Actual vs budget by region and category for 2024
    WITH actuals AS (
        SELECT
            ds.region,
            fds.category,
            YEAR(fds.sale_date)    AS fiscal_year,
            DATEPART(QUARTER, fds.sale_date) AS fiscal_quarter,
            SUM(fds.daily_revenue) AS actual_revenue,
            SUM(fds.transaction_count) AS total_transactions
        FROM
            dbo.fact_daily_sales AS fds
            INNER JOIN dbo.dim_store AS ds ON fds.store_key = ds.store_key
        WHERE
            YEAR(fds.sale_date) = 2024
        GROUP BY
            ds.region,
            fds.category,
            YEAR(fds.sale_date),
            DATEPART(QUARTER, fds.sale_date)
    )
    SELECT
        a.region,
        a.category,
        a.fiscal_year,
        a.fiscal_quarter,
        ROUND(a.actual_revenue, 2)              AS actual_revenue,
        b.revenue_target,
        ROUND(a.actual_revenue - b.revenue_target, 2) AS variance,
        ROUND((a.actual_revenue / NULLIF(b.revenue_target,0)) * 100, 1) AS pct_attainment,
        a.total_transactions,
        CASE
            WHEN a.actual_revenue >= b.revenue_target THEN 'On Target'
            WHEN a.actual_revenue >= b.revenue_target * 0.9 THEN 'Near Target'
            ELSE 'Below Target'
        END AS performance_band
    FROM
        actuals AS a
        LEFT JOIN planning.store_budget AS b
            ON  a.region        = b.region
            AND a.category      = b.category
            AND a.fiscal_year   = b.fiscal_year
            AND a.fiscal_quarter = b.fiscal_quarter
    ORDER BY
        a.fiscal_quarter,
        a.region,
        a.category;
    

    This query spans both engines: fact_daily_sales and dim_store are Delta tables from the lakehouse accessed via shortcuts; planning.store_budget is a native warehouse table. Both are queryable in one T-SQL statement with no data duplication.


    Common Mistakes & Troubleshooting

    "Invalid object name 'dbo.fact_daily_sales'" after creating the shortcut

    The shortcut was created but the warehouse metadata sync hasn't completed. Wait 30-60 seconds after creating the shortcut and refresh the object explorer. If the table still doesn't appear, navigate away from the warehouse and return — this forces a metadata reload. If the problem persists, verify the Delta table is readable from the lakehouse SQL analytics endpoint first.

    Query returns no rows but the lakehouse table has data

    This almost always means the shortcut is pointing to the correct path but the Delta log is empty or the table files are in the Files section rather than the Tables section. In the lakehouse, only items under Tables are managed Delta tables. Files dropped into the Files section are raw and cannot be shortcutted as tables into the warehouse.

    "Access denied" errors at query time despite the shortcut appearing healthy

    For cross-workspace shortcuts, this is the missing item-level permission on the source lakehouse. For same-workspace shortcuts, check that the user querying the warehouse has at least Viewer access on the workspace — direct warehouse item permissions alone aren't sufficient to read through a shortcut to a lakehouse in the same workspace.

    New columns from the lakehouse are missing in warehouse queries

    Metadata cache is stale. Trigger a metadata refresh from the warehouse toolbar, or drop and recreate the shortcut. Build metadata refresh into your pipeline's post-processing steps when schema changes are expected.

    Queries against shortcut tables are slow despite the lakehouse queries being fast

    Check whether your Delta table has been OPTIMIZE'd recently. Also verify partition pruning is happening by examining the query plan — filter conditions on the partition column must be explicit and constant (not derived from a subquery) for partition elimination to kick in. Consider materializing heavily-queried aggregations into native warehouse tables using CREATE TABLE AS SELECT patterns and refreshing them on a schedule.

    Stored procedures referencing shortcut tables fail to compile

    Fabric Warehouse stored procedures can reference shortcut tables, but the table must exist at the time the procedure is compiled. If you drop and recreate a shortcut (for a metadata refresh), stored procedures that reference it need to be recompiled. Use EXEC sp_recompile 'your_procedure_name' after recreating shortcuts that stored procedures depend on.


    Summary & Next Steps

    You've built the complete pattern: Delta tables authored in a Fabric Lakehouse, shortcuts created in a Fabric Warehouse that redirect reads to those Delta files through OneLake, and T-SQL queries that transparently join shortcut-backed external tables with warehouse-native tables — all without duplicating a single byte of data.

    The key principles to carry forward:

    • Shortcuts are OneLake metadata pointers, not data copies. Reads go through the pointer to the physical files at their original location.
    • Shortcut tables appear as external tables in the warehouse's T-SQL catalog. Query both sys.external_tables and sys.tables for complete object enumeration.
    • Schema changes in the lakehouse require a warehouse metadata refresh — build this into your orchestration when schemas evolve.
    • RLS policies on the lakehouse do not propagate through shortcuts. Implement security independently in each engine.
    • Partition pruning and Delta file statistics work through shortcuts — optimize your Delta tables at the source for best query performance.

    Where to go from here:

    • Learn how this pattern integrates with Direct Lake reporting by reading about Direct Lake mode in Power BI — you can have a Power BI semantic model in Direct Lake mode over the lakehouse while the warehouse serves the SQL analysts, all over the same physical data.
    • Explore OneLake shortcuts to external storage if some of your source data lives in ADLS Gen2 or S3 rather than another Fabric lakehouse.
    • If your architecture requires bringing external databases into OneLake without building ETL pipelines, database mirroring in Microsoft Fabric is the pattern to investigate — and mirrored data can itself be shortcutted into a warehouse using exactly the technique you just learned.
    Work With Us

    From insight to implementation

    Reading is the start. When you're ready to build the data, automation, or AI systems behind it, our team turns strategy into shipped results.

    Let's Build

    Microsoft Fabric Fundamentals

    Previous

    Automating Fabric Lakehouse Metadata Refresh with Semantic Link: Syncing Delta Table Schema Changes to a Direct Lake Semantic Model Using Python and the Fabric REST API

    Related Insights

    Microsoft FabricExpert

    Automating Fabric Lakehouse Metadata Refresh with Semantic Link: Syncing Delta Table Schema Changes to a Direct Lake Semantic Model Using Python and the Fabric REST API

    27 min
    Microsoft FabricPractitioner

    Implementing a Fabric Notebook-Based Data Quality Framework: Validating Row Counts, Null Thresholds, and Referential Integrity Across Medallion Layers Before Pipeline Promotion

    22 min
    Microsoft FabricExpert

    Unpivoting, Aggregating, and Reshaping Lakehouse Data in Dataflow Gen2: Advanced Power Query Transformations Before Writing to Delta Tables

    27 min

    On this page

    • Introduction
    • Prerequisites
    • Why This Works: OneLake as the Common Storage Substrate
    • Setting Up the Scenario: A Realistic Architecture
    • Step 1: Verify Your Lakehouse Tables Are Ready
    • Step 2: Open the Warehouse and Navigate to the Shortcut Dialog
    • Step 3: Browse to the Lakehouse Table Path and Create the Shortcut
    • Step 4: Query the Shortcut Tables with T-SQL
    • Step 5: Create Views to Abstract the Shortcut Layer
    • Step 6: Understanding and Managing Metadata Sync
    • Security: Getting Permissions Right Across Workspace Boundaries
    • Performance Characteristics and When to Choose This Pattern
    • Hands-On Exercise
    • Common Mistakes & Troubleshooting
    • Summary & Next Steps