Sök…


Syntax

  • MED QueryName [(ColumnName, ...)] AS (
    VÄLJ ...
    )
    VÄLJ ... FRÅN Fråga namn ...;

  • MED RECURSIVE QueryName [(ColumnName, ...)] AS (
    VÄLJ ...
    UNION [ALL]
    VÄLJ ... FRÅN Fråga namn ...
    )
    VÄLJ ... FRÅN Fråga namn ...;

Anmärkningar

Officiell dokumentation: MED klausul

Ett vanligt tabelluttryck är en tillfällig resultatset, och det kan vara resultatet av en komplex undersökning. Det definieras med hjälp av MED-klausulen. CTE förbättrar läsbarheten och det skapas i minnet snarare än TempDB-databasen där Temp Table och Table variabel skapas.

Nyckelbegrepp för vanliga tabelluttryck:

  • Kan användas för att dela upp komplexa frågor, särskilt komplexa sammanfogningar och subfrågor.
  • Är ett sätt att kapsla in en frågdefinition.
  • Fortsätt bara tills nästa fråga körs.
  • Korrekt användning kan leda till förbättringar av både kodkvalitet / underhåll och hastighet.
  • Kan användas för att referera till den resulterande tabellen flera gånger i samma uttalande (eliminera duplicering i SQL).
  • Kan ersätta en vy när den allmänna användningen av en vy inte krävs; det vill säga att du inte behöver lagra definitionen i metadata.
  • Körs när den kallas, inte när den definieras. Om CTE används flera gånger i en fråga körs den flera gånger (eventuellt med olika resultat).

Tillfällig fråga

Dessa uppför sig på samma sätt som kapslade underkällor men med en annan syntax.

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

Motsvarande subquery-syntax

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

rekursivt gå upp i ett träd

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 Telefonnummer Administratörs DepartmentId
4 Johnathon Smed 1212121212 2 1
2 John Johnson 2468101214 1 1
1 James Smed 1234567890 NULL 1

generera värden

De flesta databaser har inget sätt att generera en serie nummer för ad-hoc-användning. vanliga tabelluttryck kan emellertid användas med rekursion för att emulera den typen av funktion.

Följande exempel genererar ett vanligt tabelluttryck som heter Numbers med en kolumn i som har en rad för siffrorna 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;
jag
1
2
3
4
5

Den här metoden kan användas med valfritt antal intervall, liksom andra typer av data.

rekursivt uppräkning av ett underlag

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;
Nivå ID fNAME LNAME
1 1 James Smed
2 2 John Johnson
3 4 Johnathon Smed
2 3 Michael Williams

Oracle CONNECT BY-funktion med rekursiva CTE: er

Oracle's CONNECT BY-funktionalitet ger många användbara och icke-triviala funktioner som inte är inbyggda när du använder SQL-standard rekursiva CTE: er. Detta exempel replikerar dessa funktioner (med några kompletteringar för fullständighetens skull) med hjälp av SQL Server-syntax. Det är mest användbart för Oracle-utvecklare att hitta många funktioner som saknas i deras hierarkiska frågor på andra databaser, men det tjänar också till att visa vad som kan göras med en hierarkisk fråga i allmänhet.

  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-funktioner som visas ovan, med förklaringar:

  • Klausuler
    • CONNECT BY: Anger förhållandet som definierar hierarkin.
    • START MED: Anger rotnoderna.
    • BESTÄLLNINGSSIBLINGAR AV: Beställer resultaten ordentligt.
  • parametrar
    • NOCYCLE: Stoppar behandlingen av en gren när en slinga detekteras. Giltiga hierarkier är riktade Acyclic Grafer, och cirkulära referenser bryter med denna konstruktion.
  • operatörer
    • PRIOR: Hämtar data från nodens överordnade.
    • CONNECT_BY_ROOT: Hämtar data från nodens rot.
  • Pseudocolumns
    • NIVÅ: Anger nodens avstånd från roten.
    • CONNECT_BY_ISLEAF: Anger en nod utan barn.
    • CONNECT_BY_ISCYCLE: Indikerar en nod med en cirkulär referens.
  • funktioner
    • SYS_CONNECT_BY_PATH: Returnerar en platt / sammanlagd representation av sökvägen till noden från dess rot.

Rekursivt generera datum, förlängs till att inkludera gruppöverföring som exempel

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

Resultat

Dvs. för vecka 1 TeamA är på R&R, TeamB är på Day Shift och TeamC är på Night Shift.

ange bildbeskrivning här

Återaktivera en fråga för att använda vanliga tabelluttryck

Anta att vi vill få alla produktkategorier med en total försäljning över 20.

Här är en fråga utan vanliga tabelluttryck:

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

Och en motsvarande fråga med vanliga tabelluttryck:

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

Exempel på en komplex SQL med Common Table Expression

Anta att vi vill fråga de "billigaste produkterna" från "toppkategorierna".

Här är ett exempel på fråga som använder 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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow