What this skill tests
Four objective groups: database objects (tables, specialized tables, JSON, constraints, sequences,
partitioning), programmability objects (views, functions, procedures, triggers), advanced
T-SQL (CTEs, window, JSON, regex and fuzzy functions, graph MATCH, correlated queries, error
handling) and AI-assisted tools. The T-SQL family lives in its own
deep dive; this page covers the design decisions and summarises it.
Tables, data types and indexes
Table design questions are usually about size and correctness: the smallest type that fits, exact
numerics for money, Unicode only where users type text, and a clustered key that is narrow, unique, static and
ever-increasing. Rows live on 8 KB pages, and a row's fixed in-row data is limited to 8,060 bytes — large
(max) values move off-row.
| Need | Choose | Avoid, and why |
|---|---|---|
| Whole numbers | tinyint 1 B · smallint 2 B · int 4 B · bigint 8 B | Oversizing every key to bigint widens every index that carries it |
| Money, quantities | decimal(p, s) | float/real are approximate; money loses precision in division |
| Date and time | date 3 B, datetime2(n) 6–8 B, datetimeoffset for time zones | datetime: 3.33 ms precision, larger range than needed |
| Text | varchar for ASCII codes; nvarchar or a _UTF8 collation for international text | nvarchar(max) everywhere: cannot be an index key, blocks some optimizations |
| Semi-structured data | json (SQL Server 2025+, Azure SQL, Fabric) | nvarchar(max) JSON: reparsed on every read |
| Embeddings | vector(n) — n must match the model's dimensions | varbinary or JSON arrays: no vector functions, no vector index |
| Concurrency token | rowversion | Timestamps compared by value: clock skew and ties |
-- Applies to: all four platforms (vector and json need SQL Server 2025 or later)
CREATE TABLE dbo.Product
(
ProductID int IDENTITY(1, 1) NOT NULL,
Sku varchar(20) NOT NULL, -- ASCII codes: varchar, not nvarchar
Name nvarchar(200) NOT NULL, -- user-facing text: Unicode
Description nvarchar(max) NULL, -- LOB, stored off-row when large
ListPrice decimal(10, 2) NOT NULL, -- exact numeric; avoid float and money
CategoryID smallint NOT NULL, -- smallest type that fits
Attributes json NULL, -- native binary JSON
Embedding vector(1536) NULL, -- float32 elements; size the model's dimensions
CreatedAt datetime2(3) NOT NULL
CONSTRAINT DF_Product_CreatedAt DEFAULT (SYSUTCDATETIME()),
RowVer rowversion NOT NULL, -- optimistic concurrency token
CONSTRAINT PK_Product PRIMARY KEY CLUSTERED (ProductID),
CONSTRAINT UQ_Product_Sku UNIQUE (Sku),
CONSTRAINT CK_Product_ListPrice CHECK (ListPrice >= 0),
CONSTRAINT FK_Product_Category FOREIGN KEY (CategoryID)
REFERENCES dbo.Category (CategoryID) ON DELETE NO ACTION
);
-- Covering nonclustered index for a frequent lookup
CREATE NONCLUSTERED INDEX IX_Product_Category
ON dbo.Product (CategoryID) INCLUDE (Name, ListPrice)
WHERE ListPrice > 0; -- filtered index - Clustered index — the table itself, sorted by the key; one per table. The clustering key is copied into every nonclustered index.
- Nonclustered index — up to 999 per table;
INCLUDEcolumns make it covering,WHEREmakes it filtered. - Unique constraint vs unique index — a constraint is implemented as a unique index; use the constraint when it expresses a business rule.
Columnstore indexes
Columnstore stores each column separately in compressed rowgroups of up to 1,048,576 rows and runs queries in batch mode. A clustered columnstore index (CCI) suits fact and history tables; a nonclustered columnstore (NCCI) on an OLTP table gives real-time operational analytics without an ETL copy. New rows land in a delta store until the tuple mover compresses them.
-- Real-time operational analytics: a nonclustered columnstore index on an OLTP table
CREATE NONCLUSTERED COLUMNSTORE INDEX NCCI_SalesOrderLine
ON dbo.SalesOrderLine (OrderDate, ProductID, Quantity, LineTotal);
-- Analytics table: clustered columnstore, ordered to improve segment elimination
-- (ordered CCI: SQL Server 2022+ and Azure SQL; ordered NCCI: SQL Server 2025)
CREATE CLUSTERED COLUMNSTORE INDEX CCI_FactSales
ON dbo.FactSales
ORDER (OrderDateKey); Specialized tables
The outline lists five specialized table types. Each answers one specific requirement — pick it from the requirement, not from the feature list.
flowchart LR R1["Query rows as they were<br/>at a past time"] --> T["Temporal table<br/>SYSTEM_VERSIONING = ON"] R2["Prove rows were never<br/>altered or deleted"] --> L["Ledger table<br/>updatable or append-only"] R3["Extreme OLTP latency,<br/>hot latch contention"] --> M["Memory-optimized table<br/>MEMORY_OPTIMIZED = ON"] R4["Query files in a data lake<br/>without loading them"] --> E["External table<br/>or OPENROWSET"] R5["Traverse many-to-many<br/>relationships"] --> G["Graph tables<br/>AS NODE · AS EDGE"] R6["None of these"] --> N["Ordinary rowstore table<br/>plus the right indexes"]
Temporal tables
A system-versioned temporal table keeps the current row and moves every previous version to a history
table, stamped with the period columns. Query the past with FOR SYSTEM_TIME
(AS OF, FROM … TO, BETWEEN … AND, CONTAINED IN, ALL).
HIDDEN period columns keep SELECT * unchanged for existing applications.
-- Applies to: all four platforms
CREATE TABLE dbo.Price
(
ProductID int NOT NULL PRIMARY KEY CLUSTERED,
ListPrice decimal(10, 2) NOT NULL,
ValidFrom datetime2 GENERATED ALWAYS AS ROW START HIDDEN NOT NULL,
ValidTo datetime2 GENERATED ALWAYS AS ROW END HIDDEN NOT NULL,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (SYSTEM_VERSIONING = ON (
HISTORY_TABLE = dbo.PriceHistory,
HISTORY_RETENTION_PERIOD = 2 YEARS));
-- What was the price on 1 March?
SELECT ProductID, ListPrice
FROM dbo.Price FOR SYSTEM_TIME AS OF '2026-03-01T00:00:00'
WHERE ProductID = 42; Ledger tables
Ledger makes tampering detectable: each transaction is hashed into a blockchain-like structure, and database digests stored outside the database (immutable Azure Blob storage or Azure Confidential Ledger) let you prove the data has not changed. An updatable ledger table keeps history; an append-only one blocks updates and deletes. Ledger cannot be turned off for a table once it is on.
-- Applies to: SQL Server 2022+, Azure SQL Database, Azure SQL MI (not SQL database in Fabric)
-- Updatable ledger table: keeps a history table and a ledger view
CREATE TABLE dbo.AccountBalance
(
AccountID int NOT NULL PRIMARY KEY,
Balance decimal(19, 4) NOT NULL
)
WITH (SYSTEM_VERSIONING = ON, LEDGER = ON);
-- Append-only ledger table: INSERT only, UPDATE and DELETE are blocked
CREATE TABLE dbo.AuditEvent
(
EventID bigint IDENTITY NOT NULL,
EventTime datetime2 NOT NULL DEFAULT SYSUTCDATETIME(),
Detail nvarchar(4000) NOT NULL
)
WITH (LEDGER = ON (APPEND_ONLY = ON));
-- Verify the database against its stored digests
EXECUTE sys.sp_verify_database_ledger_from_digest_storage; Memory-optimized tables
In-Memory OLTP keeps the table in memory with lock- and latch-free structures — for contention hot spots such as
session state, shopping carts and high-rate ingestion. Every memory-optimized table needs at least one index
(hash for point lookups, nonclustered for ranges); SCHEMA_ONLY durability trades persistence for speed.
-- Applies to: SQL Server (needs a MEMORY_OPTIMIZED_DATA filegroup),
-- Azure SQL Database Premium/Business Critical, Azure SQL MI Business Critical.
-- Not available in SQL database in Fabric.
CREATE TABLE dbo.ShoppingCart
(
CartID int NOT NULL PRIMARY KEY NONCLUSTERED HASH WITH (BUCKET_COUNT = 1000000),
CustomerID int NOT NULL INDEX IX_Customer NONCLUSTERED,
CreatedAt datetime2 NOT NULL
)
WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA); -- SCHEMA_ONLY: data lost on restart
-- Natively compiled procedure for the hot path
CREATE PROCEDURE dbo.usp_AddCart @CartID int, @CustomerID int
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english')
INSERT dbo.ShoppingCart (CartID, CustomerID, CreatedAt)
VALUES (@CartID, @CustomerID, SYSUTCDATETIME());
END; External tables
An external table maps files in a data lake — Parquet or CSV in Azure Blob storage or ADLS Gen2 — so they can be
queried with T-SQL without loading. OPENROWSET(BULK …) is the ad hoc alternative that needs only the data
source. Use the endpoint-specific prefixes abs:// and adls://; the generic
https:// prefix is not accepted for external tables.
-- Data virtualization: Parquet files in ADLS Gen2
-- GA in SQL Server 2022+ and Azure SQL MI, preview in Azure SQL Database;
-- SQL database in Fabric reads Parquet and CSV from OneLake.
CREATE EXTERNAL DATA SOURCE SalesLake
WITH (LOCATION = 'adls://sales@contosolake.dfs.core.windows.net'); -- endpoint-specific prefix, not https://
CREATE EXTERNAL FILE FORMAT ParquetFormat WITH (FORMAT_TYPE = PARQUET);
CREATE EXTERNAL TABLE dbo.ext_WebClicks
(
ClickTime datetime2,
Url nvarchar(400),
UserId int
)
WITH (LOCATION = 'clicks/2026/*.parquet', DATA_SOURCE = SalesLake, FILE_FORMAT = ParquetFormat);
-- Ad hoc exploration without an external table
SELECT TOP (10) *
FROM OPENROWSET(BULK 'clicks/2026/*.parquet', DATA_SOURCE = 'SalesLake', FORMAT = 'parquet') AS f; Graph tables
Graph tables model entities as node tables and relationships as edge tables, each
with hidden pseudo-columns ($node_id, $from_id, $to_id). An edge
constraint restricts which node types an edge may connect. Traverse them with MATCH and
SHORTEST_PATH — see the deep dive.
-- Applies to: SQL Server 2017+, Azure SQL Database, Azure SQL MI
CREATE TABLE dbo.Person (PersonID int PRIMARY KEY, Name nvarchar(100)) AS NODE;
CREATE TABLE dbo.Skill (SkillID int PRIMARY KEY, Name nvarchar(100)) AS NODE;
CREATE TABLE dbo.HasSkill
(
Level tinyint,
CONSTRAINT EC_HasSkill CONNECTION (dbo.Person TO dbo.Skill) ON DELETE CASCADE
) AS EDGE;
-- Edges are inserted with the pseudo-columns $from_id and $to_id
INSERT dbo.HasSkill ($from_id, $to_id, Level)
SELECT p.$node_id, s.$node_id, 4
FROM dbo.Person AS p, dbo.Skill AS s
WHERE p.PersonID = 1 AND s.SkillID = 7; JSON columns and indexes
The native json type stores documents in a parsed binary form: faster reads, in-place updates through
the modify method, and better compression. A JSON index indexes paths inside the
document so one index serves JSON_VALUE, JSON_PATH_EXISTS and JSON_CONTAINS
predicates. A json column cannot be the key of an ordinary index.
-- Applies to: SQL Server 2025, Azure SQL Database, Azure SQL MI (2025 or AUTD policy),
-- SQL database in Fabric. A JSON index needs a clustered primary key.
CREATE TABLE dbo.Orders
(
OrderID int IDENTITY PRIMARY KEY CLUSTERED,
OrderDetails json NOT NULL
);
CREATE JSON INDEX IX_Orders_Details
ON dbo.Orders (OrderDetails)
FOR ('$.customer.id', '$.status'); -- optional: index only these paths
-- Served by the JSON index
SELECT OrderID
FROM dbo.Orders
WHERE JSON_VALUE(OrderDetails, '$.status') = 'shipped';
-- JSON stored as nvarchar (any version): validate it and index one property
ALTER TABLE dbo.LegacyOrders
ADD CONSTRAINT CK_LegacyOrders_IsJson CHECK (ISJSON(Payload) = 1),
CustomerId AS CAST(JSON_VALUE(Payload, '$.customer.id') AS int);
CREATE INDEX IX_LegacyOrders_CustomerId ON dbo.LegacyOrders (CustomerId); Constraints
PRIMARY KEY- Unique and not null; one per table; clustered by default unless you say
NONCLUSTERED. FOREIGN KEY-
References a primary key or unique constraint. Referential actions
ON DELETE/ON UPDATE:NO ACTION(default),CASCADE,SET NULL,SET DEFAULT. Index the foreign key column yourself — SQL Server does not. UNIQUE- Allows a single
NULL(use a filtered unique index to allow many). CHECK- A row-level predicate; it passes when the expression is true or unknown — so
NULLpasses. DEFAULT- Used only when the column is omitted from the
INSERT, not whenNULLis supplied explicitly.
Trusted versus untrusted constraints
ALTER TABLE … WITH NOCHECK ADD CONSTRAINT skips validating existing rows and leaves the constraint
not trusted (is_not_trusted = 1), so the optimizer cannot use it. Re-enable with
WITH CHECK CHECK CONSTRAINT to validate the data and make it trusted again.
Sequences
A SEQUENCE is a schema-bound number generator independent of any table. Choose it over
IDENTITY when you need the number before the insert, one series shared across
tables, a defined cycle (CYCLE), or a block of values at once
(sp_sequence_get_range).
-- Applies to: all four platforms
CREATE SEQUENCE dbo.OrderNumber
AS bigint
START WITH 100000
INCREMENT BY 1
CACHE 50; -- faster, but cached values are lost on an unexpected shutdown (gaps)
CREATE TABLE dbo.OrderHeader
(
OrderNo bigint NOT NULL
CONSTRAINT DF_OrderHeader_OrderNo DEFAULT (NEXT VALUE FOR dbo.OrderNumber),
OrderDate date NOT NULL,
CONSTRAINT PK_OrderHeader PRIMARY KEY (OrderNo)
);
-- The same sequence can number rows in several tables, and you can take the
-- value before the INSERT — neither is possible with IDENTITY.
DECLARE @next bigint = NEXT VALUE FOR dbo.OrderNumber; Neither sequences nor IDENTITY are gap-free
Rolled-back transactions consume values, and CACHE loses unused cached values on an unexpected
shutdown. If the requirement is "no gaps" (invoice numbers in some jurisdictions), you need a serialized counter
table, not a sequence.
Partitioning tables and indexes
Partitioning splits one table into ranges by a column — almost always a date — using a partition function (the boundaries) and a partition scheme (where each partition lives). Benefits are manageability (truncate, switch, rebuild one partition) and partition elimination; it is not a substitute for indexing. Indexes created on the same scheme are aligned, which partition switching requires.
-- Applies to: all four platforms; Azure SQL Database and Fabric have only the PRIMARY
-- filegroup, and SQL database in Fabric does not allow SWITCH PARTITION.
CREATE PARTITION FUNCTION pfOrderMonth (date)
AS RANGE RIGHT FOR VALUES ('2026-01-01', '2026-02-01', '2026-03-01'); -- boundary starts a partition
CREATE PARTITION SCHEME psOrderMonth
AS PARTITION pfOrderMonth ALL TO ([PRIMARY]);
CREATE TABLE dbo.OrderLine
(
OrderDate date NOT NULL,
OrderNo bigint NOT NULL,
LineNo int NOT NULL,
Amount decimal(19, 4) NOT NULL,
CONSTRAINT PK_OrderLine PRIMARY KEY CLUSTERED (OrderDate, OrderNo, LineNo) -- partitioning column in the key
) ON psOrderMonth (OrderDate);
-- Which partition holds a row, and fast removal of old data
SELECT $PARTITION.pfOrderMonth('2026-02-14') AS PartitionNumber;
TRUNCATE TABLE dbo.OrderLine WITH (PARTITIONS (1)); RANGE RIGHT: a boundary value belongs to the partition on its right — natural for dates.- A partitioned unique index must include the partitioning column in its key.
- Sliding window:
SPLIT RANGEto add a new boundary,SWITCHout the oldest partition,MERGE RANGEto remove it.
Programmability objects
| Object | Use it for | Know this |
|---|---|---|
| View | Encapsulating a query, simplifying permissions, a stable contract over changing tables | WITH SCHEMABINDING blocks changes to underlying columns; WITH CHECK OPTION rejects writes that would fall outside the view; an indexed view needs SCHEMABINDING and a unique clustered index |
| Scalar function | A reusable expression returning one value | Called per row; scalar UDF inlining (compat 150+) rewrites eligible functions into the query; avoid data access inside them |
| Inline table-valued function | A parameterized view — a single SELECT | Expanded into the calling query and optimized with it; the preferred TVF |
| Multi-statement TVF | Procedural logic that fills a @table | Interleaved execution (compat 140+) fixes its old fixed cardinality guess, but it is still a black box to the optimizer |
| Stored procedure | Transactions, multiple statements, output parameters, a permission boundary | SET NOCOUNT ON, SET XACT_ABORT ON, TRY…CATCH + THROW; ownership chaining lets callers run it without table permissions |
| DML trigger | Enforcing rules or auditing on INSERT/UPDATE/DELETE | Fires once per statement; read inserted and deleted set-based; INSTEAD OF makes a multi-table view updatable |
| DDL trigger | Auditing or blocking schema changes | Database-scoped only in Azure SQL Database and Fabric; reads EVENTDATA() |
-- Indexed view: SCHEMABINDING, two-part names, COUNT_BIG(*) with GROUP BY
CREATE OR ALTER VIEW dbo.vw_SalesByProduct
WITH SCHEMABINDING
AS
SELECT ProductID, SUM(LineTotal) AS Total, COUNT_BIG(*) AS LineCount
FROM dbo.SalesOrderLine
GROUP BY ProductID;
GO
CREATE UNIQUE CLUSTERED INDEX UCX_vw_SalesByProduct ON dbo.vw_SalesByProduct (ProductID);
GO
-- Inline table-valued function: one SELECT, optimized like a parameterized view
CREATE OR ALTER FUNCTION dbo.tvf_OrdersForCustomer (@CustomerID int)
RETURNS TABLE
AS
RETURN
SELECT OrderNo, OrderDate, Total
FROM dbo.OrderHeader
WHERE CustomerID = @CustomerID;
GO
-- Stored procedure skeleton the exam expects
CREATE OR ALTER PROCEDURE dbo.usp_PlaceOrder
@CustomerID int,
@OrderNo bigint OUTPUT
AS
BEGIN
SET NOCOUNT ON; -- no "n rows affected" messages
SET XACT_ABORT ON; -- any runtime error dooms and rolls back the transaction
BEGIN TRY
BEGIN TRANSACTION;
SET @OrderNo = NEXT VALUE FOR dbo.OrderNumber;
INSERT dbo.OrderHeader (OrderNo, CustomerID, OrderDate) VALUES (@OrderNo, @CustomerID, GETUTCDATE());
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
THROW; -- re-raise the original error to the caller
END CATCH;
END; -- AFTER trigger: set-based, works for multi-row statements
CREATE OR ALTER TRIGGER dbo.trg_Product_Audit
ON dbo.Product
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF NOT UPDATE(ListPrice) RETURN;
INSERT dbo.PriceChange (ProductID, OldPrice, NewPrice, ChangedAt)
SELECT d.ProductID, d.ListPrice, i.ListPrice, SYSUTCDATETIME()
FROM inserted AS i
JOIN deleted AS d ON d.ProductID = i.ProductID
WHERE i.ListPrice <> d.ListPrice;
END; Triggers that assume one row
SELECT @id = ProductID FROM inserted silently handles only one arbitrary row of a multi-row update. The
exam's "fix the trigger" items are almost always this: rewrite it as a set-based INSERT … SELECT joining
inserted and deleted.
Advanced T-SQL at a glance
The outline names eight families. Each has worked examples in the Advanced T-SQL deep dive; this is the one-line summary of each.
| Family | Reach for it when | Platform line |
|---|---|---|
| CTEs | Naming a step, or walking a hierarchy recursively | All |
| Window functions | Ranking, running totals, previous/next row, top-n per group — without collapsing rows | All |
| JSON functions | Building JSON (JSON_OBJECT, JSON_ARRAY, JSON_ARRAYAGG) or reading it (OPENJSON, JSON_VALUE, JSON_CONTAINS) | Aggregates and JSON_CONTAINS: SQL Server 2025+, Azure SQL, Fabric |
| Regular expressions | Validating, extracting, replacing or splitting by pattern | SQL Server 2025+, Azure SQL, Fabric (GA) |
| Fuzzy matching | Typos and near-duplicates: EDIT_DISTANCE, JARO_WINKLER_* | Preview everywhere |
Graph MATCH | Traversing node and edge tables, shortest paths | SQL Server 2017+, Azure SQL |
| Correlated queries | Per-row subqueries: EXISTS, APPLY | All |
| Error handling | TRY…CATCH, THROW, XACT_STATE() | All |
SQL solutions with AI-assisted tools
The exam covers two assistant families — GitHub Copilot (in VS Code with the MSSQL extension, in Visual Studio and in SSMS 22) and Copilot in Fabric (in the SQL database query editor) — and how to give them real context safely: a chosen model, instruction files and MCP tools.
flowchart TB
DEV["Developer prompt<br/>ask · edit · agent · plan"] --> INSTR
subgraph HOST["Copilot chat session"]
MODEL["Model picker<br/>choose per task"]
INSTR["Instruction files<br/>copilot-instructions.md"]
TOOLS["Tools picker<br/>MCP tools on or off"]
end
TOOLS --> MSSQL["MSSQL extension tools<br/>connect · run_query"]
TOOLS --> SQLMCP["SQL MCP Server<br/>Data API builder /mcp"]
TOOLS --> FAB["Fabric data agent<br/>as an MCP server"] Security impact
- What leaves your machine: the prompt, the code being edited, content from open files and — when connected — schema information. Prompts and responses are not used to train the models, and traffic is encrypted.
- Data classification and compliance: check whether policy allows AI assistance on databases holding PII, financial or health data, and whether schema is itself considered confidential.
- Credentials: never paste passwords or connection strings into prompts or commit them in
mcp.json; use input prompts (${input:…}), environment variables or a secret store. - Least privilege for MCP: a dedicated identity per MCP server, read-only and schema-only where that is all the workflow needs, and dev or test databases — not production — for routine work.
- Review every suggestion: check generated
GRANTstatements, dynamic SQL (parameterizedsp_executesql, never concatenation) and views that expose more columns than intended. - Approvals: agent mode asks before each tool call; "always allow" removes that safeguard for the tool, so grant it only for read-only tools.
Enable GitHub Copilot and Copilot in Fabric
| Assistant | Prerequisites | How it is switched on |
|---|---|---|
| GitHub Copilot in SSMS 22 | A GitHub account with a Copilot plan (Free, Pro, Business or Enterprise) | Visual Studio Installer → Modify SSMS → AI Assistance workload; then sign in from the Copilot badge |
| GitHub Copilot in VS Code | The same Copilot plan, plus the MSSQL extension for database context | Install Copilot and sign in; right-click a connected database → Chat with this database, or use @mssql |
| Copilot in Fabric | A paid Fabric capacity (F2 or higher) or Power BI Premium (P1 or higher) | Fabric admin portal → tenant setting Users can use Copilot and other features powered by Azure OpenAI, scoped to security groups; then the Copilot button in the SQL database query editor |
Organization policies decide what users can switch on
For Copilot Business and Enterprise, organization and enterprise owners control policies such as which models are available, whether MCP servers may be used, and whether suggestions matching public code are allowed. A developer who cannot see a model or an MCP option in the picker is usually blocked by policy, not by the IDE.
Models and MCP tools in a chat session
- Model picker: choose a stronger reasoning model for design and optimization, a faster one for routine completions.
- Modes: ask for a single answer, edit for changes across files, agent for multi-step work that calls tools, plan to produce a written plan before any DDL.
- Tools picker (agent mode): lists every MCP server and extension tool; enable only what the task
needs. The MSSQL extension contributes tools such as
connect,list_databasesandrun_queryautomatically — no@mssqlmention needed in agent mode. - Schema awareness: the
@mssqlparticipant and agent tools see your connected schema; inline completions do not. - Copilot in Fabric already has the workspace's context; to use a Fabric data agent from outside Fabric, publish it and copy its MCP server URL from Settings → Model Context Protocol.
Copilot instruction files
| File | Scope | Notes |
|---|---|---|
.github/copilot-instructions.md | Every request in the repository | Plain Markdown; the team's conventions |
.github/instructions/NAME.instructions.md | Files matching applyTo globs | YAML frontmatter; combined with the repository-wide file when both apply |
AGENTS.md | Agents; the nearest file in the tree wins | Can sit in any folder |
.github/prompts/NAME.prompt.md | A reusable task, run on demand | Invoked as /NAME in chat; inputs use ${input:name} |
<!-- .github/copilot-instructions.md — applies to every chat request in the repository -->
# Database conventions
- Target platform: Azure SQL Database, compatibility level 170.
- Always schema-qualify objects (dbo.Customer) and list columns explicitly; never SELECT *.
- Stored procedures: SET NOCOUNT ON, SET XACT_ABORT ON, TRY...CATCH with THROW.
- Parameterize everything; build dynamic SQL only with sp_executesql and parameters.
- Never emit GRANT ... TO public, credentials, or connection strings. ---
applyTo: "src/**/*.sql"
---
<!-- .github/instructions/tsql.instructions.md — only for files that match applyTo -->
Use the json data type for semi-structured columns and add a JSON index for queried paths.
Prefer CREATE OR ALTER for programmability objects. Good instructions are specific (exact naming patterns, not "use good names"), give an example of the preferred pattern and put the most important rules first. Full samples, including a prompt file, are in the Configuration reference.
Connect to MCP server endpoints
| Target | MCP option | How you connect |
|---|---|---|
| SQL Server, Azure SQL, SQL database in Fabric | MSSQL extension tools | Built in: connect in the extension and use agent mode — no separate server to configure |
| SQL Server, Azure SQL, SQL database in Fabric | SQL MCP Server (Data API builder 2.x) | Enable runtime.mcp in dab-config.json, run dab start, then add the /mcp URL (HTTP) or run DAB with --mcp-stdio |
| Fabric lakehouse, warehouse, semantic model | Fabric data agent published as an MCP server | Publish the data agent, copy the MCP server URL, add it as an HTTP server; sign in with Entra ID |
| Azure resources broadly | Azure MCP Server | Install it, sign in to Azure, then use agent mode |
In VS Code, add servers with MCP: Add Server or by editing .vscode/mcp.json, then check in
agent mode's tools picker that the server is running and its tools are listed. Troubleshooting follows the usual
order: JSON syntax (server missing), expired sign-in (authentication failed), firewall (timeout), permissions (empty
schema).
SQL MCP Server tools are generated from configuration
SQL MCP Server exposes seven DML tools — describe_entities, create_record,
read_records, update_record, delete_record, execute_entity and
aggregate_records — and only for entities and roles your dab-config.json allows. Agents
never see raw SQL access, so DAB permissions are the guardrail. Turning delete-record off globally is a
common production setting.