Toán tử tập hợp là các toán tử SQL xử lý việc kết hợp, theo những cách khác nhau, các tập kết quả khác nhau. Giả sử bạn có hai SELECT
khác nhau mà bạn muốn kết hợp thành một tập kết quả duy nhất, các toán tử tập hợp sẽ có tác dụng. MariaDB đã và đang hỗ trợ UNION
và UNION ALL
toán tử set trong một thời gian dài và đây là những toán tử set SQL phổ biến nhất.
Nhưng chúng ta đang vượt lên chính mình ở đây, trước tiên hãy để tôi giải thích các toán tử bộ SQL mà chúng ta có và cách chúng hoạt động. Nếu muốn dùng thử, bạn có thể sử dụng triển khai MariaDB Server hiện có của mình hoặc dùng thử trong cơ sở dữ liệu đám mây MariaDB SkySQL.
UNION và UNION ALL
UNION
và UNION ALL
toán tử tập hợp thêm kết quả của hai hoặc nhiều tập kết quả. Hãy bắt đầu với UNION ALL
và UNION
sau đó sẽ là một biến thể của UNION ALL
.
Hãy xem nó trông như thế nào trong SQL. Giả sử chúng tôi điều hành một webhop và đối với các sản phẩm chúng tôi bán, chúng tôi có một khoảng không quảng cáo. Bây giờ chúng tôi muốn xem tất cả các sản phẩm đang đặt hàng hoặc đang tồn kho, một truy vấn cho điều này sẽ giống như sau:
CHỌN oi.
Theo lý thuyết tập hợp, đây là UNION
bộ sản phẩm đã được đặt hàng và bộ sản phẩm đang tồn kho. Về lý thuyết thì điều này tốt, nhưng có một vấn đề với kết quả của truy vấn này. Vấn đề là một sản phẩm xuất hiện trong cả đơn đặt hàng và khoảng không quảng cáo, hoặc ở nhiều vị trí trong khoảng không quảng cáo, sẽ xuất hiện nhiều hơn một lần trong đầu ra. Sự cố này là lý do tại sao UNION ALL
không được sử dụng nhiều và thay vào đó là UNION DISTINCT
(DISTINCT
là mặc định và có thể được bỏ qua) được sử dụng. Ví dụ:
CHỌN oi.prod_id, p.prod_name TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id UNION CHỌN i.prod_id, p.prod_name TỪ kho hàng tôi THAM GIA sản phẩm p ON i.prod_id =p.id;
Với truy vấn này, một sản phẩm theo đơn đặt hàng hoặc tồn tại trong kho chỉ được liệt kê một lần. Lưu ý rằng khi chúng tôi xóa các bản sao ở đây, đó là các giá trị được so sánh, vì vậy hai hàng có cùng giá trị trong cùng một cột được coi là bằng nhau, mặc dù các giá trị đến từ các bảng hoặc cột khác nhau.
Thành thật mà nói, không có gì trong truy vấn ở trên không thể thực hiện được với SELECT
thông thường từ sản phẩm bảng và một số liên kết. Theo một số cách, một UNION
có thể dễ đọc hơn. Mặt khác, nếu chúng ta muốn có danh sách các sản phẩm theo đơn đặt hàng hoặc trong kho và cũng muốn biết đó là sản phẩm nào, thì một truy vấn sẽ như sau:
CHỌN 'Theo đơn đặt hàng', oi.prod_id, p.prod_name TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id UNION CHỌN 'Khoảng không quảng cáo', i.prod_id, p.prod_name TỪ khoảng không quảng cáo tôi THAM GIA sản phẩm p BẬT i.prod_id =p.id;
Đây là một truy vấn không dễ thực hiện với một SELECT
đơn giản từ sản phẩm khi chúng ta đang xem xét cùng một hàng từ bảng sản phẩm hai lần (một lần cho order_items và một lần cho khoảng không quảng cáo ).
Các toán tử bộ SQL khác
Với MariaDB Server 10.3 đã xuất hiện hai toán tử bộ SQL mới, phần lớn được giới thiệu để nâng cao khả năng tương thích với Oracle, nhưng các toán tử này hữu ích theo cách riêng của chúng. MariaDB Server 10.4 sau đó bổ sung khả năng kiểm soát mức độ ưu tiên của toán tử đã đặt. Chúng tôi cũng sẽ xem xét điều đó. Nếu không có khả năng kiểm soát mức độ ưu tiên của toán tử, các toán tử đã đặt không phải lúc nào cũng hoạt động như bạn mong muốn hoặc mong đợi.
Các toán tử bộ SQL mới là INTERSECT
và EXCEPT
và chúng hữu ích, đặc biệt khi sử dụng phân tích. Ngoài ra, mặc dù JOIN
s và các cấu trúc khác thường có thể được sử dụng thay thế, các toán tử bộ SQL cho phép tạo ra cú pháp SQL có thể dễ đọc và dễ hiểu hơn. Và, nếu bạn có các ứng dụng Oracle mà bạn đang chuyển sang MariaDB, thì tính hữu ích của các toán tử này là rõ ràng.
Toán tử bộ INTERSECT
INTERSECT
toán tử sẽ trả về tất cả các mục tồn tại trong hai hoặc nhiều tập hợp, hoặc theo thuật ngữ SQL, tất cả các hàng tồn tại trong hai tập kết quả. Trong trường hợp này, một mặt cắt của hai bộ mục được tạo ra. Theo thuật ngữ SQL, điều đó có nghĩa là chỉ các hàng tồn tại trong cả hai tập hợp mới được trả về, vì vậy nếu tôi muốn kiểm tra sản phẩm nào tôi có theo đơn đặt hàng và sản phẩm nào cũng có trong kho, một truy vấn có thể trông giống như sau:
CHỌN oi.prod_id, p.prod_name TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id INTERSECT CHỌN i.prod_id, p.prod_name TỪ kho hàng tôi THAM GIA sản phẩm p BẬT i.prod_id =p.id;
Một lần nữa, truy vấn này có thể được tạo bằng cách sử dụng JOIN
trên sản phẩm nhưng truy vấn ở trên rõ ràng hơn một chút về những gì chúng tôi đang cố gắng đạt được.
Toán tử bộ EXCEPT
Trong trường hợp của EXCEPT
toán tử, chúng tôi muốn các mục nằm trong một trong các tập hợp, nhưng không nằm trong tập hợp kia. Vì vậy, một lần nữa bằng cách sử dụng ví dụ trên, nếu chúng ta muốn xem các sản phẩm mà chúng ta có theo đơn đặt hàng nhưng chúng ta không có hàng tồn kho, chúng ta có thể viết một truy vấn như sau:
CHỌN oi.prod_id, p.prod_name TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id NGOẠI TRỪ CHỌN i.prod_id, p.prod_name TỪ kho hàng tôi THAM GIA sản phẩm p BẬT i.prod_id =p.id;
Một lần nữa, có nhiều cách khác để viết truy vấn cụ thể này, nhưng đối với các truy vấn khác, nâng cao hơn khi chúng tôi kết hợp dữ liệu từ hai bảng khác nhau, thì đây không phải là trường hợp.
Kết hợp nhiều toán tử tập hợp
Bạn có thể kết hợp nhiều hơn 2 toán tử tập hợp nếu điều này hữu ích. Ví dụ:hãy để chúng tôi xem liệu chúng tôi có thể tìm thấy các sản phẩm theo đơn đặt hàng và đã được giao hoặc còn hàng hay không. SQL cho điều này sẽ trông giống như sau:
CHỌN oi.prod_id, p.prod_name TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name TỪ việc giao hàng d THAM GIA sản phẩm p ON d.prod_id =p.id UNION SELECT i.prod_id, p.prod_name TỪ khoảng không quảng cáo tôi THAM GIA sản phẩm p BẬT i.prod_id =p.id;
Để diễn đạt điều này bằng ngôn ngữ đơn giản, điều đang diễn ra là trước tiên tôi kiểm tra sản phẩm nào đang đặt hàng và sản phẩm nào đã được giao, sau đó tôi kết hợp bộ sản phẩm này với tất cả các sản phẩm trong kho. Bất kỳ sản phẩm nào không có trong tập kết quả đều không có trong kho nhưng có thể là theo đơn đặt hàng hoặc có thể đã được giao, nhưng không phải cả hai.
Nhưng bây giờ, hãy diễn đạt điều này theo cách khác và xem điều gì sẽ xảy ra. Tôi muốn một danh sách tất cả các sản phẩm có trong kho hoặc đã được giao và đang đặt hàng. SQL sau đó sẽ giống như thế này, tương tự như SQL ở trên nhưng hơi khác một chút:
CHỌN i.prod_id, p.prod_name TỪ kho hàng tôi THAM GIA sản phẩm p ON i.prod_id =p.id UNION SELECT oi.prod_id, p.prod_name FROM order_items oi Tham gia sản phẩm p ON oi.prod_id =p.id INTERSECT SELECT d.prod_id, p.prod_name TỪ việc giao hàng d THAM GIA sản phẩm p BẬT d.prod_id =p.id;
Làm thế nào để bạn giải thích điều này sau đó? Bạn có liệt kê các sản phẩm có trong kho và đang đặt hàng và các sản phẩm đang được giao không? Đây là những gì nó trông như thế này, phải không? Nó chỉ là INTERSECT
(và EXCEPT
cho vấn đề đó) có ưu tiên trên UNION
. Hai câu lệnh SQL tạo ra cùng một tập kết quả, ít nhất là trong MariaDB và đây là cách Tiêu chuẩn SQL cho biết mọi thứ sẽ hoạt động. Nhưng có một ngoại lệ, Oracle.
Cách này hoạt động trong Oracle
Oracle đã có tất cả bốn toán tử tập hợp SQL (UNION
, UNION ALL
, INTERSECT
và EXCEPT
) trong một thời gian dài, rất lâu trước khi chúng được tiêu chuẩn hóa, vì vậy cách triển khai của chúng có một chút khác biệt. Hãy thử với các bảng ở trên và chèn một số dữ liệu vào chúng. Dữ liệu rất đơn giản và phản ánh một công ty không quá thành công, nhưng nó hoạt động như một ví dụ và chúng tôi chỉ hiển thị các cột có liên quan ở đây.
Table | sản phẩm | order_items | khoảng không quảng cáo | giao hàng | ||
Cột | prod_id | prod_name | order_id | prod_id | prod_id | prod_id |
Dữ liệu | 1 | Bình màu xanh lam | 1 | 1 | 1 | 2 |
2 | Bình màu đỏ | 2 | 1 | 2 | 3 | |
3 | Thảm đỏ | 2 | 3 |
Với dữ liệu đã có, hãy cùng xem lại câu lệnh SQL cuối cùng ở trên. Có một tính năng cho phép bạn kiểm soát mức độ ưu tiên và đó là sử dụng dấu ngoặc đơn hoặc dấu ngoặc (Được giới thiệu trong MariaDB 10.4, xem https://jira.mariadb.org/browse/MDEV-11953) và sử dụng chúng để minh họa chuyện gì đang xảy ra, câu lệnh sẽ trông như thế này:
MariaDB> CHỌN i.prod_id, p.prod_name -> TỪ kho hàng tôi THAM GIA sản phẩm p ON i.prod_id =p.id -> UNION -> (CHỌN oi.prod_id, p.prod_name -> TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id -> INTERSECT -> SELECT d.prod_id, p.prod_name -> FROM giao hàng d THAM GIA sản phẩm p ON d.prod_id =p.id); + --------- + ------------ + | prod_id | prod_name | + --------- + ------------ + | 1 | Bình màu xanh lam | | 2 | Bình màu đỏ | | 3 | Thảm đỏ | + --------- + ------------ + 3 hàng trong bộ (0,001 giây)
Bây giờ, hãy sử dụng kỹ thuật tương tự để thực thi ba thành phần của truy vấn hoạt động theo thứ tự nghiêm ngặt:
MariaDB> (CHỌN i.prod_id, p.prod_name -> TỪ hàng tồn kho tôi THAM GIA sản phẩm p BẬT i.prod_id =p.id -> UNION -> CHỌN oi.prod_id, p.prod_name -> TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id) -> INTERSECT -> CHỌN d.prod_id, p.prod_name -> TỪ việc giao hàng d THAM GIA sản phẩm p ON d.prod_id =p.id; + --------- + ------------ + | prod_id | prod_name | + --------- + ------------ + | 2 | Bình màu đỏ | | 3 | Thảm đỏ | + --------- + ------------ + 2 hàng trong bộ (0,001 giây)
Và cuối cùng không có dấu ngoặc đơn:
MariaDB [test]> CHỌN i.prod_id, p.prod_name -> TỪ kho hàng tôi THAM GIA sản phẩm p ON i.prod_id =p.id -> UNION -> SELECT oi.prod_id, p.prod_name -> FROM order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id -> INTERSECT -> CHỌN d.prod_id, p.prod_name -> TỪ việc giao hàng d THAM GIA sản phẩm p ON d.prod_id =p.id; + --------- + ------------ + | prod_id | prod_name | + --------- + ------------ + | 1 | Bình màu xanh lam | | 2 | Bình màu đỏ | | 3 | Thảm đỏ | + --------- + ------------ + 3 hàng trong bộ (0,001 giây)
Chúng tôi thấy rằng MariaDB, tuân theo tiêu chuẩn, đã giả định rằng INTERSECT
được ưu tiên hơn UNION
. Điều này đưa chúng ta đến với Oracle. Hãy thử SQL ở trên trong Oracle bằng sqlplus:
SQL> CHỌN i.prod_id, p.prod_name 2 TỪ kho hàng i THAM GIA sản phẩm p ON i.prod_id =p.id 3 UNION 4 CHỌN oi.prod_id, p.prod_name 5 TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id 6 INTERSECT 7 CHỌN d.prod_id, p.prod_name 8 TỪ việc giao hàng d THAM GIA sản phẩm p ON d.prod_id =p.id; PROD_ID PROD_NAME ------------------------------ 2 Bình Đỏ 3 Thảm đỏBạn hỏi điều gì đang xảy ra ở đây? Chà, Oracle không tuân theo tiêu chuẩn. Các toán tử tập hợp khác nhau được coi là bình đẳng và không có toán tử nào được ưu tiên hơn các toán tử khác. Đây là một vấn đề khi bạn di chuyển các ứng dụng từ Oracle sang MariaDB, và điều tồi tệ hơn là sự khác biệt này rất khó tìm. Không có lỗi nào được tạo ra và dữ liệu được trả về và trong nhiều trường hợp, dữ liệu phù hợp được trả về. Tuy nhiên, trong một số trường hợp, khi dữ liệu giống như trong ví dụ của chúng tôi ở trên, dữ liệu sai được trả về, đó là một vấn đề.
Ảnh hưởng đến việc di chuyển dữ liệu
Vì vậy, làm thế nào để chúng ta giải quyết vấn đề này nếu chúng ta đang di chuyển một ứng dụng từ Oracle sang MariaDB? Có một số tùy chọn:
- Viết lại ứng dụng để ứng dụng giả định rằng dữ liệu trả về từ một truy vấn như thế này phù hợp với Tiêu chuẩn SQL và MariaDB.
- Viết lại các câu lệnh SQL, sử dụng dấu ngoặc để MariaDB trả về cùng một dữ liệu như Oracle
- Hoặc và đây là cách thông minh nhất, hãy sử dụng MariaDB
SQL_MODE=Oracle
thiết lập.
Đối với cách cuối cùng và thông minh nhất, để hoạt động, chúng tôi cần chạy với MariaDB 10.3.7 trở lên (điều này được bạn thực sự đề xuất trong https://jira.mariadb.org/browse/MDEV-13695). Hãy kiểm tra cách hoạt động của điều này. So sánh kết quả của SELECT
này với Oracle một ở trên (tạo ra cùng một kết quả) và một từ MariaDB ở trên (mà không):
MariaDB> đặt SQL_MODE =Oracle; Truy vấn OK, 0 hàng bị ảnh hưởng (0,001 giây) MariaDB> CHỌN i.prod_id, p.prod_name -> TỪ kho hàng tôi THAM GIA sản phẩm p ON i.prod_id =p.id -> UNION -> CHỌN oi.prod_id, p.prod_name -> TỪ order_items oi THAM GIA sản phẩm p ON oi.prod_id =p.id -> INTERSECT -> CHỌN d.prod_id, p.prod_name -> TỪ việc giao hàng d THAM GIA sản phẩm p ON d.prod_id =p.id; + --------- + ------------ + | prod_id | prod_name | + --------- + ------------ + | 2 | Bình màu đỏ | | 3 | Thảm đỏ | + --------- + ------------ + 2 hàng trong bộ (0,002 giây)
Như bạn có thể thấy, khi SQL_MODE
được đặt thành Oracle
, MariaDB thực sự hoạt động giống như Oracle. Đây không phải là thứ duy nhất mà SQL_MODE=Oracle
rõ ràng là có, nhưng đó là một trong những lĩnh vực ít được biết đến hơn.
Kết luận
Các toán tử tập hợp INTERSECT
và EXCEPT
không được sử dụng nhiều, mặc dù chúng xuất hiện ở đây và ở đó, và có một số công dụng cho chúng. Các ví dụ trong blog này có nhiều hơn để minh họa cách các toán tử này hoạt động hơn là chỉ ra các công dụng thực sự tốt cho chúng. Có lẽ có những ví dụ tốt hơn. Tuy nhiên, khi bạn đang di chuyển từ Oracle sang MariaDB, các toán tử tập hợp SQL thực sự hữu ích vì nhiều ứng dụng Oracle sử dụng chúng và có thể thấy, MariaDB có thể bị lừa để hoạt động giống như Oracle, không phải là Tiêu chuẩn nhưng vẫn phục vụ một mục đích. Tuy nhiên, theo mặc định, tất nhiên MariaDB hoạt động như bình thường và tuân theo Chuẩn SQL.
Chúc mừng SQL’ing
/ Karlsson