Excelgoodies logo +44 (0)20 3769 3689

LEARN THIS HANDS ON

Full Stack BI (On-Cloud)

. Live Online FILLING FAST
View all upcoming batches
Designing Cost-Efficient Azure SQL Architectures for Self-Service BI

Designing Cost-Efficient Azure SQL Architectures for Self-Service BI

Self-service BI lives or dies on refresh speed, query responsiveness, and the Azure bill at the end of the month. In this article we’ll compare Azure SQL Serverless, Hyperscale, and Managed Instance for self-service BI, and walk through concrete patterns to keep costs under control without killing performance.

If you want to go deeper into building robust BI backends for Power BI, it’s worth investing in strong SQL + BI foundations alongside the architecture choices below.


1. Start from the BI Use Case, Not the Service

Before picking a service, pin down how your self-service BI will actually be used. The same database can be cheap or painfully expensive depending on usage patterns.

Key questions:

  • Data volume
    • Total size now and in 12–24 months
    • Daily/weekly growth
  • Refresh patterns
    • How often do Power BI datasets refresh?
    • Full loads or incremental?
    • Nightly batch, near-real-time, or both?
  • Query patterns
    • How many concurrent Power BI users at peak?
    • Are analysts running ad-hoc SQL from SSMS/Notebooks?
    • Are there external apps hitting the same database?
  • Feature requirements
    • Cross-database queries, SQL Agent, CLR, linked servers?
    • Need for long-running transactions or large tempdb?
    • Strict compatibility with on-prem SQL Server?
  • Cost control levers
    • Is it OK if the database is offline/out of memory for some hours?
    • Can you tolerate warm-up delays?
    • Do you need predictable monthly cost, or is variable cost fine?

Once you’ve answered these, you can map to the right Azure SQL flavor.


2. Azure SQL Options in One Line Each

For self-service BI, you’ll mostly consider these three:

  • Azure SQL Database – Serverless
    • Auto-scales vCores and pauses when idle; great for bursty, low-to-medium workloads.
  • Azure SQL Database – Hyperscale
    • Log-structured storage with fast scaling; built for very large databases and high concurrency.
  • Azure SQL Managed Instance (MI)
    • Near full SQL Server compatibility; best when you’re lifting-and-shifting or need SQL Server features not in SQL Database.

The trick is matching the workload to the service, then designing around cost.


3. When Azure SQL Serverless Is the Sweet Spot

Serverless is often the best starting point for self-service BI if:

  • Most activity happens during working hours.
  • Power BI refreshes are scheduled, not continuous.
  • There are short bursts of heavy queries and long idle periods.

3.1 Cost Characteristics

You pay for:

  • Compute: vCore-seconds used (auto-scales between min and max).
  • Storage: data + log storage (always on, independent of compute).
  • Auto-pause: when paused, you pay only for storage.

Cost levers:

  • Lower min vCores for light idle workloads.
  • Enable auto-pause if you can tolerate cold starts.
  • Use elastic pools if you have many small serverless databases.

3.2 Typical Self-Service BI Pattern with Serverless

Good fit for:

  • Power BI datasets refreshing 2–8 times per day.
  • 10–200 GB data marts.
  • Small-to-medium analyst teams.

Design pattern:

  1. Landing + Staging
    • Use Data Factory or Synapse Pipelines to land data into Azure Data Lake.
  2. Transform & Load
    • Use stored procedures or external tools (e.g., Data Factory mappings) to load into Azure SQL Serverless.
  3. Serve to Power BI
    • Power BI models connect via DirectQuery or Import.

Example: a nightly incremental load stored procedure that runs while the database scales up and then idles:

CREATE OR ALTER PROCEDURE etl.Load_FactSales_Incremental
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @LastLoadTime datetime2 = (
        SELECT MAX(LastLoadTime)
        FROM etl.LoadLog
        WHERE TableName = 'FactSales'
    );

    MERGE dbo.FactSales AS target
    USING (
        SELECT *
        FROM staging.SalesDelta
        WHERE ModifiedDate > @LastLoadTime
    ) AS src
    ON target.SalesKey = src.SalesKey
    WHEN MATCHED THEN
        UPDATE SET
            target.Amount = src.Amount,
            target.ModifiedDate = src.ModifiedDate
    WHEN NOT MATCHED BY TARGET THEN
        INSERT (SalesKey, CustomerKey, ProductKey, Amount, ModifiedDate)
        VALUES (src.SalesKey, src.CustomerKey, src.ProductKey, src.Amount, src.ModifiedDate);

    INSERT INTO etl.LoadLog(TableName, LastLoadTime)
    VALUES ('FactSales', SYSUTCDATETIME());
END;

3.3 When Serverless Is a Bad Idea

Avoid serverless when:

  • You have constant heavy workloads (24/7 dashboards, streaming, or many concurrent users).
  • You need predictable performance with tight SLAs.
  • Warm-up time after auto-pause is unacceptable.

In those cases, look at Hyperscale or MI.


4. Hyperscale for High-Concurrency, Large Models

Hyperscale shines when:

  • Your database is growing beyond a few hundred GB.
  • You have many concurrent queries from Power BI and other tools.
  • You want fast scale-out read replicas.

4.1 Cost & Performance Trade-Offs

You pay for:

  • Compute: vCores for primary and any read replicas.
  • Storage: highly scalable, but you pay for what you allocate/use.

Cost levers:

  • Use read replicas only when you actually need them.
  • Size vCores based on measured DTU/vCore usage, not guesswork.
  • Use partitioning and good indexing to keep IO under control.

4.2 Self-Service BI Pattern with Hyperscale

Good fit for:

  • Central enterprise data warehouse feeding many Power BI workspaces.
  • Mixed workloads: scheduled ETL + ad-hoc SQL + Power BI DirectQuery.
  • Data volumes in the hundreds of GB to multiple TB.

Typical pattern:

  1. Central DW in Hyperscale
    • Modeled in a star schema.
  2. Read replicas for BI
    • Power BI and analysts read from read replicas.
  3. Primary for ETL
    • ETL and heavy writes hit the primary.

Example: routing read-only queries to a replica using a read-only listener (simplified connection string concept):

import pyodbc

conn = pyodbc.connect(
    "DRIVER={ODBC Driver 18 for SQL Server};"
    "SERVER=yourserver.database.windows.net;"
    "DATABASE=DWProd;"
    "UID=bi_reader;PWD=yourStrongPassword;"
    "ApplicationIntent=ReadOnly;"  # Hint for read replica
)

4.3 When Hyperscale Is Overkill

Hyperscale is probably not for you if:

  • Your database is under ~100 GB and not growing quickly.
  • You have limited concurrency and no need for replicas.
  • You just migrated from on-prem and still rely on SQL Server-specific features (MI is better).

In those cases, serverless or provisioned single database can be cheaper and simpler.


5. Managed Instance for SQL Server Compatibility

Azure SQL Managed Instance is the right tool when:

  • You’re lifting-and-shifting existing SQL Server BI workloads.
  • You rely on features like SQL Agent, cross-database queries, CLR, or linked servers.
  • You need near-full compatibility with on-prem SQL Server.

5.1 Cost & Operational Profile

You pay for:

  • Compute: vCores (always on, no auto-pause).
  • Storage: per GB.

Cost levers:

  • Choose Business Critical vs General Purpose carefully.
  • Use reserved capacity if you know you’ll run it 24/7.
  • Consolidate multiple small databases into one MI if they share features/maintenance.

5.2 Self-Service BI Pattern with Managed Instance

Good fit for:

  • Existing SSIS/SSRS/SQL Agent-heavy environments moving to Azure.
  • Complex legacy queries and stored procedures that would be painful to refactor.

Typical pattern:

  1. Lift & Shift
    • Migrate your existing data warehouse databases to MI.
  2. Keep Existing Jobs
    • Keep SQL Agent jobs for ETL, partition management, etc.
  3. Expose to Power BI
    • Power BI connects via gateway or direct Azure connection.

Example: a SQL Agent job step to process a daily snapshot for Power BI:

EXEC etl.Process_Daily_Snapshot
    @SnapshotDate = CONVERT(date, GETDATE());

Managed Instance is rarely the cheapest option, but it often has the lowest migration friction when you’re coming from on-prem.


6. Choosing: Serverless vs Hyperscale vs Managed Instance

6.1 Quick Decision Matrix

Use this as a starting point:

  • Choose Serverless if:

    • Workload is bursty with long idle periods.
    • Data size is small-to-medium.
    • You want to minimize cost without strict 24/7 SLAs.
  • Choose Hyperscale if:

    • Data is large or growing fast.
    • Many concurrent Power BI users and/or heavy queries.
    • You want modern cloud-native scaling and can live without some SQL Server features.
  • Choose Managed Instance if:

    • You’re migrating existing SQL Server workloads with heavy feature usage.
    • You need cross-database queries, SQL Agent, or full compatibility.
    • You accept a more fixed, always-on cost for simpler migration.

6.2 Hybrid Patterns

You don’t have to pick exactly one. Common hybrid patterns:

  • MI for legacy + Serverless for new marts

    • Keep core DW on MI.
    • Build new subject-area marts on serverless, tuned for Power BI.
  • Hyperscale DW + Serverless sandboxes

    • Central DW on Hyperscale.
    • Department sandboxes on serverless for experimentation and self-service modeling.

7. Practical Cost-Control Techniques for Self-Service BI

Regardless of service, there are a few concrete tactics that always help.

7.1 Design for Import, Use DirectQuery Sparingly

  • Prefer Import mode in Power BI for most reports.
  • Use DirectQuery or Composite models only when:
    • Data is very large and can’t be aggregated.
    • You truly need near-real-time.

Every DirectQuery visual is a SQL query. On the wrong architecture, this gets expensive quickly.

7.2 Push Transformations Upstream

  • Do heavy transformations in:
    • Data Factory / Synapse pipelines
    • Power Query in your ETL layer
  • Keep Azure SQL focused on:
    • Clean star schema
    • Aggregations and indexing

Example: a Power Query step to pre-aggregate before loading into SQL:

let
    Source = Csv.Document(File.Contents("sales.csv"), [Delimiter=",", Columns=5, Encoding=65001, QuoteStyle=QuoteStyle.None]),
    PromotedHeaders = Table.PromoteHeaders(Source, [PromoteAllScalars=true]),
    ChangedTypes = Table.TransformColumnTypes(PromotedHeaders,
        {{"OrderDate", type date}, {"CustomerID", Int64.Type}, {"Amount", type number}}),
    Grouped = Table.Group(ChangedTypes, {"OrderDate", "CustomerID"}, {{"TotalAmount", each List.Sum([Amount]), type number}})
in
    Grouped

This reduces the volume and complexity of queries hitting Azure SQL.

7.3 Index for BI, Not OLTP

  • Create columnstore indexes on large fact tables.
  • Keep narrow nonclustered indexes for common filter columns.

Example: columnstore index on a fact table:

CREATE CLUSTERED COLUMNSTORE INDEX CCI_FactSales
ON dbo.FactSales;

Columnstore is often the single biggest performance win for BI workloads.

7.4 Schedule Around Business Hours

  • For serverless:
    • Set auto-pause to a reasonable idle time.
    • Schedule heavy ETL during off-peak hours.
  • For Hyperscale/MI:
    • Consider scale-up before heavy loads, scale-down afterwards (automate with Azure Automation or Functions).

8. One Concrete Takeaway: Prototype on Serverless, Then Prove You Need More

If you’re starting a new self-service BI platform and don’t have extreme scale requirements yet, begin with Azure SQL Serverless and a clean star schema. Implement:

  1. Columnstore indexes on fact tables.
  2. Import-mode Power BI datasets with incremental refresh.
  3. Auto-pause and conservative min vCores.

Measure performance and cost for a month. Only if you hit clear limits (concurrency, data size, feature gaps) should you move up to Hyperscale or Managed Instance. This “prove you need more” approach is usually the most cost-efficient way to grow a self-service BI platform on Azure.

Azure

New

Next Batches Now Live

Power BIPower BI
SQLSQL
Power AppsPower Apps
Power AutomatePower Automate
Microsoft FabricMicrosoft Fabrics
AzureAzure Data Engineering
Explore Dates & Reserve Your Spot → Reserve Your Spot →