수색…


통사론

  • WITH QueryName [(ColumnName, ...)] AS (
    고르다 ...
    )
    SELECT ... FROM QueryName ...;

  • RECURSIVE QueryName [(ColumnName, ...)] AS (
    고르다 ...
    UNION [전체]
    SELECT ... FROM QueryName ...
    )
    SELECT ... FROM QueryName ...;

비고

공식 문서 : WITH 절

공통 테이블 식은 임시 결과 집합이며 복잡한 하위 쿼리의 결과 일 수 있습니다. WITH 절을 사용하여 정의됩니다. CTE는 가독성을 향상시키고 Temp Table 및 Table 변수가 작성되는 TempDB 데이터베이스가 아닌 메모리에 작성됩니다.

공통 표 표현식의 주요 개념 :

  • 복잡한 쿼리, 특히 복잡한 조인과 하위 쿼리를 분해하는 데 사용할 수 있습니다.
  • 쿼리 정의를 캡슐화하는 방법입니다.
  • 다음 쿼리가 실행될 때까지 지속됩니다.
  • 올바른 사용은 코드 품질 / 유지 보수성 및 속도 향상을 가져올 수 있습니다.
  • 동일한 명령문에서 여러 번 결과 테이블을 참조하는 데 사용할 수 있습니다 (SQL에서 중복 제거).
  • 뷰의 일반적인 사용이 필요하지 않은 경우 뷰를 대체 할 수 있습니다. 즉, 메타 데이터에 정의를 저장할 필요가 없습니다.
  • 정의 된 시점이 아니라 호출 될 때 실행됩니다. CTE가 쿼리에서 여러 번 사용되면 여러 번 실행됩니다 (결과가 다를 수 있음).

임시 쿼리

이것은 중첩 된 하위 쿼리와 다른 방식으로 동작합니다.

WITH ReadyCars AS (
  SELECT *
  FROM Cars
  WHERE Status = 'READY'
)
SELECT ID, Model, TotalCost
FROM ReadyCars
ORDER BY TotalCost;
신분증 모델 총 비용
1 포드 F-150 200
2 포드 F-150 230

동등한 하위 쿼리 구문

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

재귀 적으로 나무에 올라간다.

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;
신분증 FName LName 전화 번호 관리자 ID DepartmentId
4 조나단 스미스 1212121212 2 1
2 남자 존슨 2468101214 1 1
1 제임스 스미스 1234567890 없는 1

값을 생성하다

대부분의 데이터베이스에는 임시 사용을위한 일련의 숫자를 생성하는 원래 방법이 없습니다. 그러나 공용 테이블 표현식을 재귀와 함 2 사용하여 해당 유형의 함수를에 D 레이트 할 수 있습니다.

다음 예제에서는 숫자 1 - 5의 행이있는 열 i 를 사용하여 Numbers 라는 공통 테이블 식을 생성합니다.

--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;
나는
1
2
4
5

이 방법은 임의의 번호 간격 및 다른 유형의 데이터와 함께 사용할 수 있습니다.

재귀 적으로 하위 트리를 열거 함

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;
수평 신분증 FName LName
1 1 제임스 스미스
2 2 남자 존슨
4 조나단 스미스
2 남자 이름 윌리엄스

재귀 CTE가있는 Oracle CONNECT BY 기능

오라클의 CONNECT BY 기능은 SQL 표준 재귀 CTE를 사용할 때 내장되지 않은 많은 유용하고 중요한 기능을 제공합니다. 이 예제는 SQL Server 구문을 사용하여 이러한 기능을 복제합니다 (완전성을 위해 몇 가지 추가 사항이 있음). 오라클 개발자는 다른 데이터베이스의 계층 적 쿼리에서 누락 된 많은 기능을 찾는데 가장 유용하지만 일반적으로 계층 적 쿼리로 수행 할 수있는 것을 보여 주기도합니다.

  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 기능 위에 설명 된 설명과 함께 :

  • 조항
    • CONNECT BY : 계층 구조를 정의하는 관계를 지정합니다.
    • START WITH : 루트 노드를 지정합니다.
    • ORDER SIBLINGS BY : 주문 결과가 올바르게 표시됩니다.
  • 매개 변수
    • NOCYCLE : 루프가 감지되면 분기 처리를 중지합니다. 유효한 계층 구조는 Directed Acyclic Graphs이며 순환 참조가이 구문을 위반합니다.
  • 연산자
    • PRIOR : 노드의 부모로부터 데이터를 얻습니다.
    • CONNECT_BY_ROOT : 노드의 루트에서 데이터를 확보합니다.
  • 의사 열
    • LEVEL : 루트에서 노드까지의 거리를 나타냅니다.
    • CONNECT_BY_ISLEAF : 자식이없는 노드를 나타냅니다.
    • CONNECT_BY_ISCYCLE : 순환 참조가있는 노드를 나타냅니다.
  • 기능들
    • SYS_CONNECT_BY_PATH : 루트에서 노드에 대한 경로의 병합 / 연결 표현을 반환합니다.

재귀 적으로 날짜 생성, 예를 들어 팀 로스터 링을 포함하도록 확장

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

결과

즉 1 주째 TeamA는 R & R에, TeamB는 Day Shift에, TeamC는 Night Shift에 있습니다.

여기에 이미지 설명을 입력하십시오.

공통 테이블 식을 사용하도록 쿼리 리팩토링

총 판매량이 20보다 큰 모든 제품 카테고리를 얻고 싶다고 가정 해 보겠습니다.

다음은 공통 테이블 표현식이없는 쿼리입니다.

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

공통 테이블 표현식을 사용하는 동일한 쿼리 :

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

공통 테이블식이있는 복합 SQL의 예

"최상위 범주"에서 "가장 저렴한 제품"을 쿼리하려고한다고 가정합니다.

다음은 공통 테이블 표현식을 사용하는 쿼리의 예입니다.

-- 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
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow