Preskočiť na obsah
Skript

PlanLab – testovacia databáza pre recepty exekučných plánov

Skript vytvorí malú databázu PlanLab (zákazníci, produkty, 300 000 objednávok, 900 000 riadkov) so zámerne nerovnomernými dátami. Na nej bežia ukážky všetkých receptov kuchárky plánov.

planlab-testovacia-databaza-pre-recepty-exekucnych-planov.sql 102 riadkov · T-SQL
Parametre skriptu doplň a kopíruj hotový skript
/* DeadlockCafe | PlanLab – testovacia databáza pre recepty exekučných plánov
   Účel:  Skript vytvorí malú databázu PlanLab (zákazníci, produkty, 300 000 objednávok, 900 000 riadkov) so zámerne nerovnomernými dátami. Na nej bežia ukážky všetkých receptov kuchárky plánov.
   Režim: len testovací server | SQL Server 2016+
*/
USE master;
GO
/* PlanLab – testovacia databáza pre recepty kuchárky exekučných plánov.
   Vytvorí (alebo znova naplní) len databázu PlanLab. Nič iné na serveri nemení.
   Trvanie: zvyčajne do minúty. Miesto: cca 150 MB dát. */
IF DB_ID(N'PlanLab') IS NULL
    CREATE DATABASE PlanLab;
GO
ALTER DATABASE PlanLab SET RECOVERY SIMPLE;
GO
USE PlanLab;
GO
DROP TABLE IF EXISTS dbo.OrderLines;
DROP TABLE IF EXISTS dbo.Orders;
DROP TABLE IF EXISTS dbo.Customers;
DROP TABLE IF EXISTS dbo.Products;
GO
CREATE TABLE dbo.Customers (
    CustomerID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Customers PRIMARY KEY,
    Name       nvarchar(100) NOT NULL,
    City       varchar(40)   NOT NULL,
    Email      varchar(200)  NOT NULL,      -- zámerne varchar (recept Implicitná konverzia)
    Country    char(2)       NOT NULL,
    CreatedAt  datetime2(0)  NOT NULL
);
CREATE TABLE dbo.Products (
    ProductID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Products PRIMARY KEY,
    Name      nvarchar(100) NOT NULL,
    Category  varchar(30)   NOT NULL,
    ListPrice decimal(10,2) NOT NULL
);
CREATE TABLE dbo.Orders (
    OrderID     int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Orders PRIMARY KEY CLUSTERED,
    CustomerID  int           NOT NULL,
    OrderDate   date          NOT NULL,
    Status      tinyint       NOT NULL,     -- 1 nová, 3 odoslaná, 4 zrušená
    Amount      decimal(12,2) NOT NULL,
    AccountCode varchar(20)   NOT NULL,     -- zámerne varchar
    Note        varchar(200)  NULL
);
CREATE TABLE dbo.OrderLines (
    OrderID   int           NOT NULL,
    LineNum    smallint      NOT NULL,
    ProductID int           NOT NULL,
    Qty       int           NOT NULL,
    Price     decimal(10,2) NOT NULL,
    CONSTRAINT PK_OrderLines PRIMARY KEY CLUSTERED (OrderID, LineNum)
);
GO
-- ---------- dáta (generátor čísel z katalógu, bez cyklov) ----------
WITH n AS (SELECT TOP (20000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
           FROM sys.all_columns AS a CROSS JOIN sys.all_columns AS b)
INSERT dbo.Customers WITH (TABLOCK) (Name, City, Email, Country, CreatedAt)
SELECT CONCAT(N'Zákazník ', n),
       CHOOSE(n % 5 + 1, 'Bratislava', 'Košice', 'Žilina', 'Nitra', 'Praha'),
       CONCAT('zakaznik', n, '@example.com'),
       CASE WHEN n % 5 = 4 THEN 'CZ' ELSE 'SK' END,
       DATEADD(DAY, -(n % 2000), CAST('2026-10-31' AS datetime2(0)))
FROM n;

WITH n AS (SELECT TOP (500) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM sys.all_columns)
INSERT dbo.Products WITH (TABLOCK) (Name, Category, ListPrice)
SELECT CONCAT(N'Produkt ', n), CHOOSE(n % 4 + 1, 'kava', 'caj', 'pecivo', 'doplnky'), 2 + n % 40
FROM n;

-- 300 000 objednávok: každá tretia patrí zákazníkovi 1 (100 000), ostatné sú rozhádzané po pár kusoch
WITH n AS (SELECT TOP (300000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n
           FROM sys.all_columns AS a CROSS JOIN sys.all_columns AS b)
INSERT dbo.Orders WITH (TABLOCK) (CustomerID, OrderDate, Status, Amount, AccountCode, Note)
SELECT CASE WHEN n % 3 = 0 THEN 1 ELSE 2 + (n * 7919) % 19999 END,
       DATEADD(DAY, -(n % 1460), CAST('2026-10-31' AS date)),
       CASE WHEN n % 50 = 0 THEN 4 WHEN n % 10 = 0 THEN 1 ELSE 3 END,
       CAST(10 + (n * 37) % 100000 / 100.0 AS decimal(12,2)),
       CONCAT('AC', RIGHT(CONCAT('000000', n % 50000), 6)),
       CASE WHEN n % 7 = 0 THEN 'expres' END
FROM n;

-- 3 riadky na objednávku = 900 000
INSERT dbo.OrderLines WITH (TABLOCK) (OrderID, LineNum, ProductID, Qty, Price)
SELECT o.OrderID, l.LineNum, 1 + (o.OrderID * 31 + l.LineNum * 17) % 500, 1 + (o.OrderID + l.LineNum) % 5, 5 + (o.OrderID * l.LineNum) % 200
FROM dbo.Orders AS o
CROSS JOIN (VALUES (CAST(1 AS smallint)), (CAST(2 AS smallint)), (CAST(3 AS smallint))) AS l(LineNum);
GO
-- ---------- indexy (OrderLines.ProductID zámerne bez indexu) ----------
CREATE INDEX IX_Orders_CustomerID  ON dbo.Orders (CustomerID);
CREATE INDEX IX_Orders_OrderDate   ON dbo.Orders (OrderDate);
CREATE INDEX IX_Orders_AccountCode ON dbo.Orders (AccountCode);
CREATE INDEX IX_Customers_Email    ON dbo.Customers (Email);
GO
-- ---------- kontrola ----------
SELECT t.name AS tabulka, SUM(p.rows) AS riadky
FROM sys.tables AS t
JOIN sys.partitions AS p ON p.object_id = t.object_id AND p.index_id IN (0, 1)
GROUP BY t.name
ORDER BY t.name;

SELECT name, compatibility_level FROM sys.databases WHERE name = N'PlanLab';
> [!TIP]
> Recept z [kuchárky exekučných plánov](script.php?id=64&s=exekucne-plany-kucharka-zacni-tu). Toto je laboratórium, na ktoré stavajú ukážky všetkých receptov.

## Na čo to je

Recepty kuchárky ukazujú operátory a problémy na **rovnakých dátach**, aby sa dali porovnávať. Skript vytvorí malú databázu **PlanLab** so štyrmi tabuľkami. Spúšťaj ho na **testovacom** alebo vývojovom serveri (alebo lokálne v SQL Server Developer edition). Nič mimo databázy PlanLab nemení – žiadne nastavenia servera, žiadne čistenie cache celého servera.

![Schéma testovacej databázy PlanLab](uploads/2026/11/plan-l0.svg "Štyri tabuľky PlanLab, ich indexy a počty riadkov")

## Čo vznikne

| Tabuľka | Riadky | Prečo takto |
|---|---|---|
| `dbo.Customers` | 20 000 | `Email` je `varchar` – implicitná konverzia pri porovnaní s `nvarchar` |
| `dbo.Products` | 500 | malá tabuľka pre spojenia |
| `dbo.Orders` | 300 000 | zákazník 1 má **100 000** objednávok, ostatní po ~10 → nerovnomerné dáta pre parameter sniffing; `AccountCode` je `varchar` |
| `dbo.OrderLines` | 900 000 | bez indexu na `ProductID` → sken, Hash Match, návrh chýbajúceho indexu |

Indexy: klastrované primárne kľúče, `IX_Orders_CustomerID`, `IX_Orders_OrderDate`, `IX_Orders_AccountCode`, `IX_Customers_Email`. Štatistiky vzniknú automaticky pri vytvorení indexov (full scan).

## Ako používať

1. Spusti skript (celý, aj s `GO`). Na konci uvidíš počty riadkov a compat level databázy PlanLab.
2. Recepty, ktoré potrebujú konkrétny **compat level** (napr. 150 pre SQL Server 2019+ funkcie), to majú v ukážke: `ALTER DATABASE PlanLab SET COMPATIBILITY_LEVEL = …`. Hodnota nesmie byť vyššia, ako dovoľuje tvoj server (2016 = 130, 2017 = 140, 2019 = 150, 2022 = 160, 2025 = 170).
3. Plány v ukážkach zobrazuj ako **skutočné**: v SSMS **Ctrl+M** (Include Actual Execution Plan) a potom spusti.
4. Keď sa ti dáta „pokazia“ (recepty o INSERT/UPDATE niečo menia), spusti skript znova – tabuľky vytvorí odznova.
5. Na čistenie plánov **len tejto databázy** používaj `ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;` (spustené v PlanLab). `DBCC FREEPROCCACHE` čistí cache celého servera – v kuchárke ho nepoužívame.
6. Po skončení databázu zmaž: `DROP DATABASE PlanLab;` (z databázy `master`).

> [!WARNING]
> Čísla v receptoch (počty riadkov, percentá, časy) sú orientačné – závisia od verzie, edície, počtu CPU a pamäte. Dôležitý je tvar plánu a pomer odhad / skutočnosť, nie presné hodnoty.

## Súvisiace

- [Exekučné plány – kuchárka (začni tu)](script.php?id=64&s=exekucne-plany-kucharka-zacni-tu)
- [Ako získať exekučný plán](script.php?id=66&s=ako-ziskat-exekucny-plan-ssms-plan-cache-query-store-extended-events)
- [SET STATISTICS IO, TIME](script.php?id=67&s=set-statistics-io-time-citanie-vystupu-vedla-planu)
- [Inventár databáz – compat level](script.php?id=12&s=inventar-databaz-recovery-model-compat-level-rcsi)

## Zdroje

- [Microsoft Learn – ALTER DATABASE compatibility level](https://learn.microsoft.com/sql/t-sql/statements/alter-database-transact-sql-compatibility-level)
- [Microsoft Learn – ALTER DATABASE SCOPED CONFIGURATION](https://learn.microsoft.com/sql/t-sql/statements/alter-database-scoped-configuration-transact-sql)
- [Microsoft Learn – Statistics](https://learn.microsoft.com/sql/relational-databases/statistics/statistics)

_Texty sú vlastné, tvrdenia overené proti uvedeným zdrojom (11/2026). T-SQL je staticky skontrolovaný, nie spustený na živom serveri – vyskúšaj ho najprv v PlanLab._