Sự khác biệt giữa xem nội tuyến và mệnh đề VỚI?


9

Chế độ xem nội tuyến cho phép bạn chọn từ truy vấn con như thể đó là một bảng khác:

SELECT
    *
FROM /* Selecting from a query instead of table */
    (
        SELECT
            c1
        FROM
            t1
        WHERE
            c1 > 0
    ) a
WHERE
    a.c1 < 50;

Tôi đã thấy điều này được đề cập đến bằng cách sử dụng các thuật ngữ khác nhau: chế độ xem nội tuyến, mệnh đề VỚI, CTE và các bảng dẫn xuất. Đối với tôi có vẻ như họ là cú pháp cụ thể của nhà cung cấp khác nhau cho cùng một thứ.

Đây có phải là một giả định sai? Có sự khác biệt kỹ thuật / hiệu suất giữa những điều này?


5
Các tên "chính thức" từ SQL chuẩn là Bảngnguồn gốc (mà Oracle đặt tên là Inline View ) và Biểu thức bảng chung (= WITH...). Bạn có thể viết lại mọi Bảng có nguồn gốc dưới dạng CTE, nhưng có thể không phải là cách khác (ví dụ: CTE đệ quy hoặc sử dụng CTE nhiều lần)
vào

Câu trả lời:


8

Có một số khác biệt quan trọng giữa các khung nhìn nội tuyến (bảng dẫn xuất) và mệnh đề VỚI (CTE) trong Oracle. Một số trong số chúng khá phổ biến, tức là có thể áp dụng cho các RDBMS khác.

  1. WITH có thể được sử dụng để xây dựng các truy vấn con đệ quy, chế độ xem nội tuyến -không (theo như tôi biết tương tự đối với tất cả các RDBMS hỗ trợ CTE)
  2. Truy vấn trong WITHmệnh đề có nhiều khả năng được thực hiện trước tiên; trong nhiều trường hợp, việc chọn giữa WITHvà chế độ xem nội tuyến giúp trình tối ưu hóa chọn các gói thực thi khác nhau (tôi đoán đó là nhà cung cấp cụ thể, thậm chí có thể là phiên bản cụ thể).
  3. Truy vấn con WITHcó thể được cụ thể hóa thành một bảng tạm thời (Tôi không biết có nhà cung cấp nào khác ngoài Oracle hỗ trợ tính năng này không).
  4. Subquery trong WITHthể được tham chiếu nhiều lần, trong truy vấn con khác, và trong truy vấn chính (đúng đối với hầu hết các RDBMS).

MySQL (ít nhất là các phiên bản MariaDB mới nhất) có thể cụ thể hóa các bảng dẫn xuất (và thậm chí thêm các chỉ mục).
ypercubeᵀᴹ

3
Tôi muốn nói thêm rằng, như một lợi ích phụ, sử dụng CTE thường dễ đọc hơn cho con người.
Joishi Bodio

@JoishiBodio: Cá nhân tôi đồng ý với bạn, nhưng khả năng đọc là một vấn đề khá chủ quan. Tôi muốn tránh đề cập đến nó
a1ex07

Ngoài ra, CTE có thể tham chiếu CTE đã khai báo trước đó. Bảng dẫn xuất không thể tham chiếu bảng dẫn xuất được khai báo trước đó ở cùng cấp trừ khi LATERALđược sử dụng.
Lennart

8

Các câu trả lời khác bao gồm các khác biệt về cú pháp khá tốt vì vậy tôi sẽ không đi sâu vào vấn đề đó. Thay vào đó, câu trả lời này sẽ chỉ bao gồm hiệu suất trong Oracle.

Trình tối ưu hóa Oracle có thể chọn cụ thể hóa kết quả của CTE thành một bảng tạm thời nội bộ. Nó sử dụng một heuristic để làm điều này thay vì tối ưu hóa dựa trên chi phí. Các heuristic là một cái gì đó như "Vật chất hóa CTE nếu nó không phải là một biểu thức tầm thường và CTE được tham chiếu nhiều lần trong truy vấn". Có một số truy vấn mà việc cụ thể hóa sẽ cải thiện hiệu suất. Có một số truy vấn mà việc cụ thể hóa sẽ làm giảm đáng kể hiệu suất. Ví dụ sau đây là một chút giả định nhưng nó minh họa điểm rất rõ:

Đầu tiên tạo một bảng có khóa chính chứa các số nguyên từ 1 đến 10000:

CREATE TABLE N_10000 (NUM_ID INTEGER NOT NULL, PRIMARY KEY (NUM_ID));

INSERT /*+APPEND */ INTO N_10000
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= 10000
ORDER BY LEVEL;

COMMIT;

Hãy xem xét các truy vấn sau sử dụng hai bảng dẫn xuất:

SELECT t1.NUM_ID
FROM 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t1
LEFT OUTER JOIN 
(
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
) t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Chúng tôi có thể xem truy vấn này và nhanh chóng xác định rằng nó sẽ không trả về bất kỳ hàng nào. Oracle cũng có thể sử dụng chỉ mục để xác định điều đó. Trên máy của tôi, truy vấn kết thúc gần như ngay lập tức với gói sau:

kế hoạch tốt

Tôi không muốn lặp lại chính mình, vì vậy hãy thử truy vấn tương tự với CTE:

WITH N_10000_CTE AS (
  SELECT n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Đây là kế hoạch:

kế hoạch xấu

Đó là một kế hoạch thực sự tồi tệ. Thay vì sử dụng chỉ mục, Oracle cụ thể hóa 10000 X 10000 = 100000000 hàng thành một bảng tạm thời để cuối cùng trả về 0 hàng. Chi phí của kế hoạch này là khoảng 6 M, cao hơn nhiều so với truy vấn khác. Truy vấn mất 68 giây để hoàn thành trên máy của tôi.

Lưu ý rằng truy vấn có thể đã thất bại nếu không có đủ bộ nhớ hoặc không gian trống trong không gian bảng tạm thời.

Tôi có thể sử dụng INLINEgợi ý không có giấy tờ để không cho phép trình tối ưu hóa thực hiện CTE:

WITH N_10000_CTE AS (
  SELECT /*+ INLINE */ n1.NUM_ID
  FROM N_10000 n1
  CROSS JOIN N_10000 n2
)
SELECT t1.NUM_ID
FROM N_10000_CTE t1
LEFT JOIN N_10000_CTE t2 ON t1.NUM_ID = t2.NUM_ID
WHERE t1.NUM_ID <= 0;

Truy vấn đó có thể sử dụng chỉ mục và kết thúc gần như ngay lập tức. Chi phí của truy vấn vẫn giống như trước, 11. Vì vậy, đối với truy vấn thứ hai, heuristic được sử dụng bởi Oracle đã dẫn đến việc nó chọn một truy vấn có chi phí ước tính là 6 M thay vì truy vấn có chi phí ước tính là 11.


1

Đối với SQL Server, WITH CTEchỉ định tập kết quả được đặt tên tạm thời, nhưng chỉ được yêu cầu cho lần đầu tiên CTE. I E

WITH CTE AS (SELECT .... FROM), 
CTE2 AS (SELECT .... FROM)

SELECT CTE.Column, CTE2.Column
FROM CTE
INNER JOIN CTE2 on CTE.Column = CTE2.Column

Nhưng đây không phải là truy vấn phụ, hoặc truy vấn phụ tương quan. Có những điều bạn có thể làm với CTE những gì bạn không thể làm với truy vấn phụ trong SQL Server, như cập nhật các Bảng được tham chiếu trong CTE. Dưới đây là một ví dụ về việc cập nhật bảng với CTE.

Một truy vấn con sẽ giống như

SELECT
   C1,
   (SELECT C2 FROM SomeTable) as C2
FROM Table

Hoặc truy vấn phụ tương quan là những gì bạn đã cung cấp trong OP của mình nếu bạn tham khảo / tham gia / giới hạn kết quả của mình dựa trên a.c1.

Vì vậy, chúng chắc chắn không giống nhau, mặc dù trong nhiều trường hợp bạn có thể sử dụng một hoặc nhiều phương pháp này để đạt được kết quả tương tự. Nó chỉ phụ thuộc vào kết quả cuối cùng là gì.


1

Sự khác biệt chính giữa withmệnh đề và truy vấn con trong Oracle là bạn có thể tham chiếu một truy vấn trong mệnh đề nhiều lần. Sau đó, bạn có thể thực hiện một số tối ưu hóa với nó như biến nó thành một bảng tạm thời bằng cách sử dụng materializegợi ý. Bạn cũng có thể thực hiện các truy vấn đệ quy với nó bằng cách tham chiếu chính nó trong một withmệnh đề. Bạn không thể làm điều đó với một cái nhìn nội tuyến.

Thêm thông tin có thể được tìm thấy ở đâyở đây .


Nói chung vật chất gợi ý là không cần thiết. Theo mặc định, trình tối ưu hóa của Oracle quyết định liệu có hợp lý hóa CTE hay không - nhưng bạn có thể ghi đè lên đánh giá trình tối ưu hóa với sự tôn trọng gợi ý MATERIALIZE. INLINEcho ngược lại.
Womsfried Domscheit

@WernfriedDomscheit đó là sự thật. Nhưng đôi khi trình tối ưu hóa không chọn thực hiện CTE và trong trường hợp đó, sử dụng materializegợi ý là tùy chọn hợp lệ. Đôi khi tôi cần chỉ định nó khi tối ưu hóa các truy vấn rất phức tạp nơi tôi biết việc cụ thể hóa CTE sẽ có lợi cho kế hoạch thực hiện.
Marko Vodopija

0

Bạn cần cẩn thận với CTE trong máy chủ SQL không chỉ là lời tiên tri, có những trường hợp truy vấn hoạt động kém hơn nhiều khi sử dụng CTE so với truy vấn con, áp dụng chéo, v.v.

Giống như mọi khi, điều quan trọng là phải kiểm tra bất kỳ truy vấn nào trong các điều kiện tải khác nhau để xác định xem truy vấn nào hoạt động tốt nhất.

Tương tự như @scsimon với oracle, đôi khi máy chủ MS SQL không làm những gì bạn mong đợi liên quan đến việc sử dụng chỉ mục.

Nếu bạn sẽ sử dụng cùng một dữ liệu nhiều lần, CTE có thể hữu ích hơn, nếu bạn chỉ sử dụng một lần, thường thì truy vấn con sẽ nhanh hơn trong các bộ dữ liệu lớn.

ví dụ: chọn * từ (truy vấn phụ của tôi) tham gia vào một cái gì đó khác ...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.