Szukaj…


Składnia

  • WITH QueryName [(ColumnName, ...)] AS (
    WYBIERZ ...
    )
    SELECT ... FROM QueryName ...;

  • Z RECURSIVE QueryName [(ColumnName, ...)] AS (
    WYBIERZ ...
    UNIA [WSZYSTKO]
    WYBIERZ ... NAZWA Zapytania ...
    )
    SELECT ... FROM QueryName ...;

Uwagi

Dokumentacja urzędowa : Z klauzulą

Wyrażenie wspólnej tabeli jest tymczasowym zestawem wyników i może być wynikiem złożonego zapytania podrzędnego. Jest zdefiniowany przy użyciu klauzuli WITH. CTE poprawia czytelność i jest tworzony w pamięci, a nie w bazie danych TempDB, w której tworzona jest Tabela temperatur i Zmienna tabeli.

Kluczowe pojęcia wspólnych wyrażeń tabelowych:

  • Może być używany do dzielenia złożonych zapytań, szczególnie złożonych sprzężeń i pod-zapytań.
  • Jest sposobem enkapsulacji definicji zapytania.
  • Trwać tylko do momentu uruchomienia następnego zapytania.
  • Prawidłowe użycie może prowadzić do poprawy zarówno jakości / konserwacji kodu, jak i szybkości.
  • Może być używany do wielokrotnego odwoływania się do wynikowej tabeli w tej samej instrukcji (wyeliminuj powielanie w SQL).
  • Może zastąpić widok, gdy ogólne użycie widoku nie jest wymagane; to znaczy, nie musisz przechowywać definicji w metadanych.
  • Będzie uruchamiany po wywołaniu, a nie po zdefiniowaniu. Jeśli CTE zostanie użyte wiele razy w zapytaniu, zostanie uruchomione wiele razy (być może z różnymi wynikami).

Tymczasowe zapytanie

Zachowują się one w taki sam sposób jak zagnieżdżone podzapytania, ale mają inną składnię.

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

Równoważna składnia podzapytania

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

rekurencyjnie wspinając się na drzewo

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 FName LName Numer telefonu ManagerId DepartmentId
4 Johnathon Kowal 1212121212 2) 1
2) Jan Johnson 2468101214 1 1
1 James Kowal 1234567890 ZERO 1

generowanie wartości

Większość baz danych nie ma natywnego sposobu generowania szeregu liczb do użytku ad-hoc; jednak wspólne wyrażenia tabelowe mogą być używane z rekurencją do emulacji tego typu funkcji.

Poniższy przykład generuje wspólne wyrażenie tabeli o nazwie Numbers z kolumną i która ma wiersz dla liczb 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;
ja
1
2)
3)
4
5

Ta metoda może być używana z dowolnym interwałem liczbowym, a także z innymi typami danych.

rekurencyjnie wylicza poddrzewo

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;
Poziom ID FName LName
1 1 James Kowal
2) 2) Jan Johnson
3) 4 Johnathon Kowal
2) 3) Michael Williams

Funkcja Oracle CONNECT BY z rekurencyjnymi CTE

Funkcja CONNECT BY firmy Oracle zapewnia wiele użytecznych i nietrywialnych funkcji, które nie są wbudowane przy użyciu standardowych rekurencyjnych CTE SQL. Ten przykład replikuje te funkcje (z kilkoma dodatkami ze względu na kompletność), używając składni SQL Server. Jest to najbardziej przydatne dla programistów Oracle, którzy stwierdzili brak wielu funkcji w swoich zapytaniach hierarchicznych w innych bazach danych, ale służy także do pokazania, co można zrobić za pomocą zapytania hierarchicznego w ogóle.

  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

Funkcje CONNECT BY przedstawione powyżej wraz z objaśnieniami:

  • Klauzule
    • CONNECT BY: Określa relację, która definiuje hierarchię.
    • ZACZNIJ Z: Określa węzły główne.
    • ZAMÓW SIBLINGS BY: Poprawnie porządkuje wyniki.
  • Parametry
    • NOCYCLE: Zatrzymuje przetwarzanie gałęzi po wykryciu pętli. Prawidłowe hierarchie to ukierunkowane wykresy acykliczne, a odwołania cykliczne naruszają tę konstrukcję.
  • Operatorzy
    • PRIOR: Uzyskuje dane od rodzica węzła.
    • CONNECT_BY_ROOT: Uzyskuje dane z katalogu głównego węzła.
  • Pseudokolumny
    • POZIOM: Wskazuje odległość węzła od jego pierwiastka.
    • CONNECT_BY_ISLEAF: wskazuje węzeł bez dzieci.
    • CONNECT_BY_ISCYCLE: Wskazuje węzeł z referencją cykliczną.
  • Funkcje
    • SYS_CONNECT_BY_PATH: Zwraca spłaszczoną / połączoną reprezentację ścieżki do węzła z jego katalogu głównego.

Rekurencyjnie generuj daty, rozszerzając o przykład dyżurów zespołu

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

Wynik

To znaczy w tygodniu 1 TeamA jest na R&R, TeamB na Day Shift, a TeamC na Night Shift.

wprowadź opis zdjęcia tutaj

Refaktoryzacja zapytania w celu użycia wspólnych wyrażeń tabelowych

Załóżmy, że chcemy uzyskać wszystkie kategorie produktów o łącznej sprzedaży przekraczającej 20.

Oto zapytanie bez wspólnych wyrażeń tabelowych:

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

I równoważne zapytanie przy użyciu wspólnych wyrażeń tabelowych:

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

Przykład złożonego SQL z Common Table Expression

Załóżmy, że chcemy zapytać o „najtańsze produkty” z „najlepszych kategorii”.

Oto przykład zapytania używającego wspólnych wyrażeń tabelowych

-- 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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow