Thứ Tư, 5 tháng 8, 2020

SQL cơ bản - Bài 6: Toán tử (Operators)

Trong bài viết này, chúng ta hãy xem các toán tử tập hợp SQL có sẵn trong Oracle.

  • Thiết lập môi trường
  • Giới thiệu
  • UNION
  • UNION ALL
  • INTERSECT
  • MINUS
  • ORDER BY
  • Data Types

Thiết lập môi trường

Bạn có thể thực hiện tất cả các truy vấn trực tuyến miễn phí bằng SQL Fiddle .
Các ví dụ trong bài viết này yêu cầu phải có các bảng sau đây.
--DROP TABLE employees PURGE;
--DROP TABLE departments PURGE;

CREATE TABLE departments (
  department_id   NUMBER(2) CONSTRAINT departments_pk PRIMARY KEY,
  department_name VARCHAR2(14),
  location        VARCHAR2(13)
);

INSERT INTO departments VALUES (10,'ACCOUNTING','NEW YORK');
INSERT INTO departments VALUES (20,'RESEARCH','DALLAS');
INSERT INTO departments VALUES (30,'SALES','CHICAGO');
INSERT INTO departments VALUES (40,'OPERATIONS','BOSTON');
COMMIT;


CREATE TABLE employees (
  employee_id   NUMBER(4) CONSTRAINT employees_pk PRIMARY KEY,
  employee_name VARCHAR2(10),
  job           VARCHAR2(9),
  manager_id    NUMBER(4),
  hiredate      DATE,
  salary        NUMBER(7,2),
  commission    NUMBER(7,2),
  department_id NUMBER(2) CONSTRAINT emp_department_id_fk REFERENCES departments(department_id)
);

INSERT INTO employees VALUES (7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),800,NULL,20);
INSERT INTO employees VALUES (7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),1600,300,30);
INSERT INTO employees VALUES (7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);
INSERT INTO employees VALUES (7566,'JONES','MANAGER',7839,to_date('2-4-1981','dd-mm-yyyy'),2975,NULL,20);
INSERT INTO employees VALUES (7654,'MARTIN','SALESMAN',7698,to_date('28-9-1981','dd-mm-yyyy'),1250,1400,30);
INSERT INTO employees VALUES (7698,'BLAKE','MANAGER',7839,to_date('1-5-1981','dd-mm-yyyy'),2850,NULL,30);
INSERT INTO employees VALUES (7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),2450,NULL,10);
INSERT INTO employees VALUES (7788,'SCOTT','ANALYST',7566,to_date('13-JUL-87','dd-mm-rr')-85,3000,NULL,20);
INSERT INTO employees VALUES (7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),5000,NULL,10);
INSERT INTO employees VALUES (7844,'TURNER','SALESMAN',7698,to_date('8-9-1981','dd-mm-yyyy'),1500,0,30);
INSERT INTO employees VALUES (7876,'ADAMS','CLERK',7788,to_date('13-JUL-87', 'dd-mm-rr')-51,1100,NULL,20);
INSERT INTO employees VALUES (7900,'JAMES','CLERK',7698,to_date('3-12-1981','dd-mm-yyyy'),950,NULL,30);
INSERT INTO employees VALUES (7902,'FORD','ANALYST',7566,to_date('3-12-1981','dd-mm-yyyy'),3000,NULL,20);
INSERT INTO employees VALUES (7934,'MILLER','CLERK',7782,to_date('23-1-1982','dd-mm-yyyy'),1300,NULL,10);
COMMIT;
Các bảng này là một biến thể của các bảng EMP và DEPT từ lược đồ SCOTT. Bạn sẽ thấy rất nhiều ví dụ của Oracle trên internet bằng cách sử dụng các bảng từ lược đồ SCOTT. Bạn có thể tìm thấy các định nghĩa bảng gốc trong tập lệnh "$ ORACLE_HOME/rdbms/admin/utlsampl.sql".

Giới thiệu

Toán tử tập hợp cho phép bạn kết hợp các kết quả của nhiều truy vấn riêng biệt thành một tập kết quả duy nhất.
Hai truy vấn sau đây sẽ được sử dụng cho hầu hết các ví dụ trong bài viết này. Cái đầu tiên trả về các bộ phận 10, 20 và 30. Thứ hai trả về các bộ phận 20, 30 và 40. Như bạn có thể thấy, các bộ phận 20 và 30 là chung cho cả hai bộ kết quả.
-- Department 10, 20 và 30.
SELECT department_id, department_name
FROM   departments
WHERE  department_id <= 30;

DEPARTMENT_ID DEPARTMENT_NAM
------------- --------------
           10 ACCOUNTING
           20 RESEARCH
           30 SALES

3 rows selected.

SQL>

--Department 20, 30 và 40.
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20;

DEPARTMENT_ID DEPARTMENT_NAM
------------- --------------
           20 RESEARCH
           30 SALES
           40 OPERATIONS

3 rows selected.

SQL>

UNION 

Toán tử UNION trả về tất cả các hàng riêng biệt bởi một trong hai câu hỏi được chọn. Điều đó có nghĩa là bất kỳ hàng trùng lặp sẽ được loại bỏ.
Trong ví dụ dưới đây, thông báo chỉ có một hàng duy nhất cho mỗi bộ phận 20 và 30, thay vì hai hàng.
SELECT department_id, department_name
FROM   departments
WHERE  department_id <= 30
UNION
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY 1;

DEPARTMENT_ID DEPARTMENT_NAM
------------- --------------
           10 ACCOUNTING
           20 RESEARCH
           30 SALES
           40 OPERATIONS

4 rows selected.

SQL>
Việc loại bỏ các bản sao đòi hỏi phải xử lý thêm, vì vậy bạn nên cân nhắc sử dụng UNION ALL nếu có thể.

UNION ALL

Toán tử UNION ALL trả về tất cả các hàng bởi một trong hai câu hỏi được chọn. Điều đó có nghĩa là bất kỳ bản sao nào sẽ vẫn còn trong tập kết quả cuối cùng.
Trong ví dụ dưới đây, chú ý có hai hàng cho mỗi bộ phận 20 và 30.
SELECT department_id, department_name
FROM   departments
WHERE  department_id <= 30
UNION ALL
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY 1;

DEPARTMENT_ID DEPARTMENT_NAM
------------- --------------
           10 ACCOUNTING
           20 RESEARCH
           20 RESEARCH
           30 SALES
           30 SALES
           40 OPERATIONS

6 rows selected.

SQL>

INTERSECT

INTERSECT trả về tất cả các hàng riêng biệt bởi cả hai truy vấn lựa chọn. Điều đó có nghĩa là chỉ những hàng chung cho cả hai truy vấn sẽ có mặt trong tập kết quả cuối cùng.
Trong ví dụ dưới đây, chú ý có một hàng cho các bộ phận 20 và 30, vì cả hai hàng này đều xuất hiện trong tập kết quả cho các truy vấn tương ứng của chúng.
SELECT department_id, department_name
FROM   departments
WHERE  department_id <= 30
INTERSECT
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY 1;

DEPARTMENT_ID DEPARTMENT_NAM
------------- --------------
           20 RESEARCH
           30 SALES

2 rows selected.

SQL>

MINUS

MINUS trả về tất cả các hàng riêng biệt bằng cách truy vấn đầu tiên nhưng không phải là thứ hai được chọn. Đây là chức năng tương đương với toán tử ANSI set EXCEPT DISTINCT.
Trong ví dụ dưới đây, truy vấn đầu tiên sẽ trả về các bộ phận 10, 20, 30, nhưng các bộ phận 20 và 30 bị xóa vì chúng được trả về bởi truy vấn thứ hai. Điều này để lại một hàng duy nhất cho bộ phận 10.
SELECT department_id, department_name
FROM   departments
WHERE  department_id <= 30
MINUS
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY 1;

DEPARTMENT_ID DEPARTMENT_NAM
------------- --------------
           10 ACCOUNTING

1 row selected.

SQL>

ORDER BY

 ORDER BY được áp dụng cho tất cả các hàng trả lại trong tập kết quả cuối cùng. Các cột trong mệnh đề ORDER BY có thể được tham chiếu bằng tên cột hoặc bí danh cột có trong truy vấn đầu tiên của câu lệnh, vì chúng được chuyển đến tập kết quả cuối cùng. Thông thường, bạn sẽ thấy mọi người sử dụng vị trí cột vì nó ít gây nhầm lẫn khi dữ liệu được lấy từ các vị trí khác nhau cho mỗi khối truy vấn.
-- Tên cột
SELECT employee_id, employee_name
FROM   employees
WHERE  department_id = 10
UNION ALL
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY employee_id;

EMPLOYEE_ID EMPLOYEE_NAME
----------- --------------
         20 RESEARCH
         30 SALES
         40 OPERATIONS
       7782 CLARK
       7839 KING
       7934 MILLER

6 rows selected.

SQL>


-- Bí danh cột
SELECT employee_id AS emp_id, employee_name
FROM   employees
WHERE  department_id = 10
UNION ALL
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY emp_id;

    EMP_ID EMPLOYEE_NAME
---------- --------------
        20 RESEARCH
        30 SALES
        40 OPERATIONS
      7782 CLARK
      7839 KING
      7934 MILLER

6 rows selected.

SQL>


-- Vị trí cột
SELECT employee_id, employee_name
FROM   employees
WHERE  department_id = 10
UNION ALL
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY 1;

EMPLOYEE_ID EMPLOYEE_NAME
----------- --------------
         20 RESEARCH
         30 SALES
         40 OPERATIONS
       7782 CLARK
       7839 KING
       7934 MILLER

6 rows selected.

SQL>
Tham chiếu tên cột hoặc bí danh cột trong các truy vấn sau này không được phép.
-- Tên cột sai.
SELECT employee_id, employee_name
FROM   employees
WHERE  department_id = 10
UNION ALL
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY department_id;

ORDER BY department_id
         *
ERROR at line 8:
ORA-00904: "DEPARTMENT_ID": invalid identifier

SQL>


-- Wrong column alias
SELECT employee_id, employee_name
FROM   employees
WHERE  department_id = 10
UNION ALL
SELECT department_id AS dept_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY dept_id;

ORDER BY dept_id
         *
ERROR at line 8:
ORA-00904: "DEPT_ID": invalid identifier


SQL>

Loại dữ liệu

Các kiểu dữ liệu phải khớp với cùng một vị trí cột trong mỗi truy vấn. Một số chuyển đổi ngầm sẽ có thể giữa một số loại dữ liệu, ví dụ CHARVARCHAR2 hoặc chuyển đổi giữa một số loại số.
Trong ví dụ sau, chúng tôi chuyển đổi cột EMPLOYEE_ID sang VARCHAR2 sử dụng hàm TO_CHAR, ngắt câu lệnh.

SELECT TO_CHAR(employee_id) AS employee_id, employee_name
FROM   employees
WHERE  department_id = 10
UNION ALL
SELECT department_id, department_name
FROM   departments
WHERE  department_id >= 20
ORDER BY 1;

SELECT TO_CHAR(employee_id) AS employee_id, employee_name
       *
ERROR at line 1:
ORA-01790: expression must have same datatype as corresponding expression

SQL>
@ Trần Văn Bình - Founder of "Oracle DBA Việt Nam" #OracleTutorial #OracleDBA #OracleDatabaseAdministration #học oracle database #oca #ocp #oce #ocm

ĐỌC NHIỀU

Trần Văn Bình - Oracle Database Master