Zoeken…


Syntaxis

  • WITH QueryName [(ColumnName, ...)] AS (
    SELECTEER ...
    )
    SELECTEER ... VAN QueryName ...;

  • MET RECURSIEVE QueryName [(ColumnName, ...)] AS (
    SELECTEER ...
    UNION [ALL]
    SELECTEER ... VAN QueryName ...
    )
    SELECTEER ... VAN QueryName ...;

Opmerkingen

Officiële documentatie: MET clausule

Een algemene tabeluitdrukking is een tijdelijke resultatenset en kan het resultaat zijn van een complexe subquery. Het wordt gedefinieerd met de clausule WITH. CTE verbetert de leesbaarheid en het wordt gemaakt in het geheugen in plaats van de TempDB-database waar de Temp-tabel en de tabelvariabele worden gemaakt.

Sleutelconcepten van Common Table Expressions:

  • Kan worden gebruikt om complexe query's op te splitsen, met name complexe joins en subquery's.
  • Is een manier om een querydefinitie in te kapselen.
  • Alleen blijven bestaan tot de volgende query wordt uitgevoerd.
  • Correct gebruik kan leiden tot verbeteringen in zowel codekwaliteit / onderhoudbaarheid als snelheid.
  • Kan worden gebruikt om meerdere keren in dezelfde instructie naar de resulterende tabel te verwijzen (duplicatie in SQL elimineren).
  • Kan een vervanging zijn voor een weergave wanneer algemeen gebruik van een weergave niet vereist is; dat wil zeggen dat u de definitie niet hoeft op te slaan in metagegevens.
  • Wordt uitgevoerd wanneer aangeroepen, niet wanneer gedefinieerd. Als de CTE meerdere keren in een query wordt gebruikt, wordt deze meerdere keren uitgevoerd (mogelijk met verschillende resultaten).

Tijdelijke zoekopdracht

Deze gedragen zich op dezelfde manier als geneste subquery's, maar met een andere syntaxis.

WITH ReadyCars AS (
  SELECT *
  FROM Cars
  WHERE Status = 'READY'
)
SELECT ID, Model, TotalCost
FROM ReadyCars
ORDER BY TotalCost;
ID kaart Model Totale prijs
1 Ford F-150 200
2 Ford F-150 230

Gelijkwaardige syntaxis van de subquery

SELECT ID, Model, TotalCost
FROM (
  SELECT *
  FROM Cars
  WHERE Status = 'READY'
) AS ReadyCars
ORDER BY TotalCost

recursief omhoog in een boom

WITH RECURSIVE ManagersOfJonathon AS (
    -- start with this row
    SELECT *
    FROM Employees
    WHERE ID = 4

    UNION ALL

    -- get manager(s) of all previously selected rows
    SELECT Employees.*
    FROM Employees
    JOIN ManagersOfJonathon
        ON Employees.ID = ManagersOfJonathon.ManagerID
)
SELECT * FROM ManagersOfJonathon;
ID kaart FName lname Telefoonnummer BeheerderId DepartmentId
4 Johnathon smid 1212121212 2 1
2 John Johnson 2468101214 1 1
1 James smid 1234567890 NUL 1

waarden genereren

De meeste databases hebben geen eigen manier om een reeks getallen voor ad-hocgebruik te genereren; algemene tabelexpressies kunnen echter met recursie worden gebruikt om dat type functie te emuleren.

In het volgende voorbeeld wordt een algemene tabelexpressie met de naam Numbers gegenereerd met een kolom i met een rij voor getallen 1-5:

--Give a table name `Numbers" and a column `i` to hold the numbers
WITH Numbers(i) AS (
    --Starting number/index
    SELECT 1
    --Top-level UNION ALL operator required for recursion
    UNION ALL
    --Iteration expression:
    SELECT i + 1
    --Table expression we first declared used as source for recursion
    FROM Numbers
    --Clause to define the end of the recursion
    WHERE i < 5
)
--Use the generated table expression like a regular table
SELECT i FROM Numbers;
ik
1
2
3
4
5

Deze methode kan worden gebruikt met elk nummerinterval en met andere soorten gegevens.

recursief een subtree opsommen

WITH RECURSIVE ManagedByJames(Level, ID, FName, LName) AS (
    -- start with this row
    SELECT 1, ID, FName, LName
    FROM Employees
    WHERE ID = 1

    UNION ALL

    -- get employees that have any of the previously selected rows as manager
    SELECT ManagedByJames.Level + 1,
           Employees.ID,
           Employees.FName,
           Employees.LName
    FROM Employees
    JOIN ManagedByJames
        ON Employees.ManagerID = ManagedByJames.ID

    ORDER BY 1 DESC   -- depth-first search
)
SELECT * FROM ManagedByJames;
Niveau ID kaart FName lname
1 1 James smid
2 2 John Johnson
3 4 Johnathon smid
2 3 Michael Williams

Oracle CONNECT BY-functionaliteit met recursieve CTE's

De CONNECT BY-functionaliteit van Oracle biedt veel nuttige en niet-triviale functies die niet zijn ingebouwd bij het gebruik van SQL-standaard recursieve CTE's. Dit voorbeeld repliceert deze functies (met enkele toevoegingen omwille van de volledigheid), met behulp van SQL Server-syntaxis. Het is vooral handig voor Oracle-ontwikkelaars die veel functies missen in hun hiërarchische query's in andere databases, maar het dient ook om te laten zien wat er met een hiërarchische query in het algemeen kan worden gedaan.

  WITH tbl AS (
       SELECT id, name, parent_id
         FROM mytable)
     , tbl_hierarchy AS (
       /* Anchor */
       SELECT 1 AS "LEVEL"
            --, 1 AS CONNECT_BY_ISROOT
            --, 0 AS CONNECT_BY_ISBRANCH
            , CASE WHEN t.id IN (SELECT parent_id FROM tbl) THEN 0 ELSE 1 END AS CONNECT_BY_ISLEAF
            , 0 AS CONNECT_BY_ISCYCLE
            , '/' + CAST(t.id   AS VARCHAR(MAX)) + '/' AS SYS_CONNECT_BY_PATH_id
            , '/' + CAST(t.name AS VARCHAR(MAX)) + '/' AS SYS_CONNECT_BY_PATH_name
            , t.id AS root_id
            , t.*
         FROM tbl t
        WHERE t.parent_id IS NULL                            -- START WITH parent_id IS NULL
       UNION ALL
       /* Recursive */
       SELECT th."LEVEL" + 1 AS "LEVEL"
            --, 0 AS CONNECT_BY_ISROOT
            --, CASE WHEN t.id IN (SELECT parent_id FROM tbl) THEN 1 ELSE 0 END AS CONNECT_BY_ISBRANCH
            , CASE WHEN t.id IN (SELECT parent_id FROM tbl) THEN 0 ELSE 1 END AS CONNECT_BY_ISLEAF
            , CASE WHEN th.SYS_CONNECT_BY_PATH_id LIKE '%/' + CAST(t.id AS VARCHAR(MAX)) + '/%' THEN 1 ELSE 0 END AS CONNECT_BY_ISCYCLE
            , th.SYS_CONNECT_BY_PATH_id   + CAST(t.id   AS VARCHAR(MAX)) + '/' AS SYS_CONNECT_BY_PATH_id
            , th.SYS_CONNECT_BY_PATH_name + CAST(t.name AS VARCHAR(MAX)) + '/' AS SYS_CONNECT_BY_PATH_name
            , th.root_id
            , t.*
         FROM tbl t
              JOIN tbl_hierarchy th ON (th.id = t.parent_id) -- CONNECT BY PRIOR id = parent_id
        WHERE th.CONNECT_BY_ISCYCLE = 0)                     -- NOCYCLE
SELECT th.*
     --, REPLICATE(' ', (th."LEVEL" - 1) * 3) + th.name AS tbl_hierarchy
  FROM tbl_hierarchy th
       JOIN tbl CONNECT_BY_ROOT ON (CONNECT_BY_ROOT.id = th.root_id)
 ORDER BY th.SYS_CONNECT_BY_PATH_name;                       -- ORDER SIBLINGS BY name

CONNECT BY-functies hierboven aangetoond, met uitleg:

  • clausules
    • CONNECT BY: geeft de relatie aan die de hiërarchie definieert.
    • START MET: Specificeert de root nodes.
    • BESTEL SIBLINGS BIJ: Bestelt de resultaten correct.
  • parameters
    • NOCYCLE: stopt het verwerken van een vertakking wanneer een lus wordt gedetecteerd. Geldige hiërarchieën zijn Gerichte Acyclische grafieken en circulaire verwijzingen schenden dit construct.
  • operators
    • PRIOR: verkrijgt gegevens van de ouder van het knooppunt.
    • CONNECT_BY_ROOT: haalt gegevens op uit de root van het knooppunt.
  • pseudokolommen
    • NIVEAU: geeft de afstand van het knooppunt tot zijn root aan.
    • CONNECT_BY_ISLEAF: geeft een knooppunt aan zonder kinderen.
    • CONNECT_BY_ISCYCLE: geeft een knooppunt met een cirkelvormige verwijzing aan.
  • functies
    • SYS_CONNECT_BY_PATH: Retourneert een afgeplatte / aaneengeschakelde weergave van het pad naar het knooppunt vanaf de root.

Genereer recursief datums, uitgebreid met teamroostering als voorbeeld

DECLARE @DateFrom DATETIME = '2016-06-01 06:00'
DECLARE @DateTo DATETIME = '2016-07-01 06:00'
DECLARE @IntervalDays INT = 7

-- Transition Sequence = Rest & Relax into Day Shift into Night Shift
-- RR (Rest & Relax) = 1
-- DS (Day Shift) = 2
-- NS (Night Shift) = 3

;WITH roster AS
(
   SELECT @DateFrom AS RosterStart, 1 AS TeamA, 2 AS TeamB, 3 AS TeamC
   UNION ALL
   SELECT DATEADD(d, @IntervalDays, RosterStart),
          CASE TeamA WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 1 END AS TeamA,
          CASE TeamB WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 1 END AS TeamB,
          CASE TeamC WHEN 1 THEN 2 WHEN 2 THEN 3 WHEN 3 THEN 1 END AS TeamC
   FROM roster WHERE RosterStart < DATEADD(d, -@IntervalDays, @DateTo)
)

SELECT RosterStart,
       ISNULL(LEAD(RosterStart) OVER (ORDER BY RosterStart), RosterStart + @IntervalDays) AS RosterEnd,
       CASE TeamA WHEN 1 THEN 'RR' WHEN 2 THEN 'DS' WHEN 3 THEN 'NS' END AS TeamA,
       CASE TeamB WHEN 1 THEN 'RR' WHEN 2 THEN 'DS' WHEN 3 THEN 'NS' END AS TeamB,
       CASE TeamC WHEN 1 THEN 'RR' WHEN 2 THEN 'DS' WHEN 3 THEN 'NS' END AS TeamC
FROM roster

Resultaat

Dat wil zeggen voor week 1 TeamA is op R&R, TeamB is op Day Shift en TeamC is op Night Shift.

voer hier de afbeeldingsbeschrijving in

Een query opnieuw uitvoeren om gemeenschappelijke tabelexpressies te gebruiken

Stel dat we alle productcategorieën willen krijgen met een totale omzet van meer dan 20.

Hier is een zoekopdracht zonder Common Table Expressions:

SELECT category.description, sum(product.price) as total_sales
FROM sale
LEFT JOIN product on sale.product_id = product.id
LEFT JOIN category on product.category_id = category.id
GROUP BY category.id, category.description
HAVING sum(product.price) > 20

En een vergelijkbare zoekopdracht met Common Table Expressions:

WITH all_sales AS (
  SELECT product.price, category.id as category_id, category.description as category_description
  FROM sale
  LEFT JOIN product on sale.product_id = product.id
  LEFT JOIN category on product.category_id = category.id
)
, sales_by_category AS (
  SELECT category_description, sum(price) as total_sales
  FROM all_sales
  GROUP BY category_id, category_description
)
SELECT * from sales_by_category WHERE total_sales > 20

Voorbeeld van een complexe SQL met Common Table Expression

Stel dat we de "goedkoopste producten" uit de "topcategorieën" willen opvragen.

Hier is een voorbeeld van een zoekopdracht met Common Table Expressions

-- all_sales: just a simple SELECT with all the needed JOINS
WITH all_sales AS (
  SELECT
  product.price as product_price,
  category.id as category_id,
  category.description as category_description
  FROM sale
  LEFT JOIN product on sale.product_id = product.id
  LEFT JOIN category on product.category_id = category.id
)
-- Group by category
, sales_by_category AS (
  SELECT category_id, category_description,
  sum(product_price) as total_sales
  FROM all_sales
  GROUP BY category_id, category_description
)
-- Filtering total_sales > 20
, top_categories AS (
  SELECT * from sales_by_category WHERE total_sales > 20
)
-- all_products: just a simple SELECT with all the needed JOINS
, all_products AS (
  SELECT
  product.id as product_id,
  product.description as product_description,
  product.price as product_price,
  category.id as category_id,
  category.description as category_description
  FROM product
  LEFT JOIN category on product.category_id = category.id
)
-- Order by product price
, cheapest_products AS (
  SELECT * from all_products
  ORDER by product_price ASC
)
-- Simple inner join 
, cheapest_products_from_top_categories AS (
  SELECT product_description, product_price
  FROM cheapest_products
  INNER JOIN top_categories ON cheapest_products.category_id = top_categories.category_id
)
--The main SELECT
SELECT * from cheapest_products_from_top_categories


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow