Kết quả là: scity pcity London London London Paris London Rome Paris London Paris Paris Chú ý rằng câu lệnh sau đây sẽ không cho kết quả đúng: SELECT DISTINCT supplier.city AS scity, p
Trang 1Bài 6
(Nội dung tiếp bài 5)
1 Điều hành dữ liệu – câu lệnh SELECT
Chúng ta có cả thảy 4 câu lệnh điều hành dữ liệu: SELECT, INSERT, UPDATE,
và DELETE, trong đó câu lệnh SELECT có cú pháp phức tạp nhất
Để dễ hiểu, trước hết ta đi qua một số ví dụ và tiếp đó ta xét cú pháp ở dạng đầy
đủ của nó
1.1 Các ví dụ về câu lệnh SELECT
VD 1 Lập danh mục Mầu và Tên thành phố đối với các phụ tùng không phải ở
Paris và có trọng lượng lớn hơn 10:
SELECT color, city FROM part
WHERE city <> 'Paris' AND weight > 10;
Kết quả:
color city
Red London Blue Rome Red London Red London
Chú ý các dòng lặp lại Nếu ta thay câu lệnh trên bằng
SELECT DISTINCT color, city FROM part
WHERE city <> 'Paris' AND weight > 10;
ta sẽ được kết quả sau:
color city
Red London Blue Rome
Đối với câu lệnh trên, kết quả không được sắp xếp Muốn có kết quả được sắp xếp, ta thêm mệnh đề ORDER BY:
Trang 2SELECT DISTINCT color, city FROM part
WHERE city <> 'Paris' AND weight > 10
ORDER BY color;
và kết quả là:
color city
Blue Rome Red London
VD 2 Lập danh sách số hiệu phụ tùng và trọng lượng của chúng đo theo gram (1
Trong câu lệnh trên chú ý phần weight*454 AS weight_gram. Đây
là trường hợp chỉ định cột tính toán và đặt tên cho cột đó
VD 3 Lấy toàn bộ thông tin của các nhà cung cấp
SELECT * FROM supplier Kết quả:
S2 Jones 10 Paris S3 Blake 30 Paris
Trang 3VD 4 Lấy toàn bộ thông tin các nhà cung cấp và phụ tùng sao cho nơi đặt phụ
s_id sname status city p_id pname color weight
S1 Smith 20 London P1 Nut Red 12 S4 Clark 20 London P1 Nut Red 12 S2 Jones 10 Paris P2 Bolt Green 17 S3 Blake 30 Paris P2 Bolt Green 17 S1 Smith 20 London P4 Screw Red 14 S4 Clark 20 London P4 Screw Red 14 S2 Jones 10 Paris P5 Cam Blue 12 S3 Blake 30 Paris P5 Cam Blue 12 S1 Smith 20 London P6 Cog Red 19 S4 Clark 20 London P6 Cog Red 19
Chú ý rằng hai câu lệnh sau tương đương với câu lệnh trên:
SELECT supplier.*, part.p_id, part.pname,
part.color, part.weight FROM supplier INNER JOIN part USING (city);
SELECT supplier.*, part.p_id, part.pname,
part.color, part.weight FROM supplier NATURAL JOIN part;
Ghi nhớ: Một cách đơn giản ta có thể nhận thấy rằng sau FROM là tích Đề
các, sau WHERE là Restrict và sau SELECT là một phép chiếu (Project) Như vậy, tổ hợp SELECT-FROM-WHERE tương đương
với phép chiếu của trích xuất tích Đề các
VD 5 Lập danh mục tên các cặp thành phố sao cho nhà cung cấp thuộc thành
phố thứ nhất cung cấp thiết bị đặt ở thành phố thứ hai
SELECT DISTINCT supplier.city AS scity,
part.city AS pcity FROM supplier INNER JOIN sp USING (s_id)
Trang 4Kết quả là:
scity pcity
London London London Paris London Rome Paris London Paris Paris
Chú ý rằng câu lệnh sau đây sẽ không cho kết quả đúng:
SELECT DISTINCT supplier.city AS scity,
part.city AS pcity FROM supplier NATURAL JOIN sp
NATURAL JOIN part ;
vì câu lệnh này sẽ sử dụng thêm trường city là trường để thực hiện phép toán JOIN
VD 6 Lập danh sách mã hiệu các cặp nhà cung cấp sao cho cả hai nhà cung cấp
ở cùng thành phố
SELECT first.s_id AS first_id,
second.s_id AS second_id FROM supplier AS first, supplier AS second WHERE first.city = second.city
AND first.s_id < second.s_id;
Kết quả là:
n
5
Chú ý trường hợp này: gọi hàm COUNT Hàm này là một trong các hàm tính giá
trị gộp (aggregate functions): COUNT, SUM, AVG, MAX, MIN
Trang 5• Nhìn chung, ta có thể đưa thêm mệnh đề DISTINCT để loại bỏ các bản ghi trùng (trừ MAX và MIN: vì mệnh đề này không có nghĩa đối với chúng)
• Hàm COUNT là hàm đặc biệt, không chấp nhận đưa thêm mệnh đề DISTINCT
và hàm này tính cả các giá trị NULL
• Các hàm khác đều loại bỏ NULL trước khi tính
• Nếu kết quả là tập hợp rỗng, hàm COUNT cho kết quả 0, còn các hàm khác cho kết quả NULL
VD 8 Lấy số lượng cao nhất và số lượng thấp nhất của phụ tùng có mã hiệu P2
SELECT MAX(qty) AS max_qty, MIN(qty) AS min_qty
FROM sp WHERE p_id = 'P2';
Kết quả là:
max_qty min_qty
VD 9 Lập danh sách phụ tùng gồm số hiệu và tổng số lượng trong kho
SELECT p_id, SUM(qty) AS total_qty FROM sp
GROUP BY p_id HAVING COUNT(s_id) > 1;
Trang 6Kết quả là:
p_id
P1 P2 P4 P5
1.2 Sơ lược về câu lệnh SELECT
SELECT [DISTINCT | ALL]
Một cách tổng quan chúng ta nhận thấy: mệnh đề SELECT chỉ ra các cột cần lấy, mệnh
đề FROM chỉ ra các bảng cần truy xuất, mệnh đề WHERE chỉ ra các bản ghi nào cần lấy Chỉ có mệnh đề SELECT và FROM là bắt buộc, còn WHERE và các mệnh đề khác
có thể không cần chỉ định (xem các ví dụ trên đây)
1.3 Mệnh đề SELECT
SELECT [ALL|DISTINCT] select-list
select-list là danh sách các cột, tách nhau bởi dấu phẩy Các chỉ định ALL và DISTINCT không bắt buộc DISTINCT chỉ định rằng không có các bản ghi trùng nhau Mặc định là ALL, nghĩa là lấy tất cả, kể cả các bản ghi trùng nhau
Ký tự ‘*’ chỉ định tất cả các cột, ví dụ SELECT * FROM sp, hoặcSELECT sp.* FROM sp là lấy tất cả các cột của bảng sp
Trang 7■ Tên tương quan
Trong phần tên bảng ta có thể thay tên mới, ví dụ:
SELECT s.name FROM supplier s
Hoặc
SELECT s.name FROM supplier AS s
Tên mới được gọi là tên tương quan
value-1 [NOT] BETWEEN value-2 AND value-3
Tương đương với:
value-1 >= value-2 AND value-1 <= value-3
Trang 8• IN
Toán tử IN kiểm tra xem giá trị chỉ ra có thuộc một danh sách hay không:
value-1 [NOT] IN ( value-2 [, value-3] )
Điều này cũng tương đương với việc kiểm tra xem value-1 có bằng value-2 hoặc bằng value-3, hay không, nó tương đương với:
value-1 = value-2 [ OR value-1 = value-3 ]
Dạng thức so sánh LIKE:
value-1 [NOT] LIKE value-2 [ESCAPE value-3]
Các giá trị phải là chuỗi ký tự value-2 là mẫu so sánh, value-1 là giá trị Tùy chọn ESCAPE cho phép sử dụng ký tự kèm để biến các ký tự '%'
và '_' như các ký tự bình thường
Trang 9Ví dụ, tìm chuỗi x kết thúc bằng dấu phần trăm:
Toán tử NOT đương nhiên làm cho kết quả ngược lại:
z NOT LIKE 'abc%'
tương đương với:
NOT (z LIKE 'abc%')
• Toán tử IS NULL
SQL không cho phép so sánh như sau:
WHERE qty = NULL
Ta phải sử dụng toán tử IS NULL:
value-1 IS [NOT] NULL
Phép tính trên kiểm tra xem value-1 có bằng NULL hay không Toán tử NOT đương nhiên là cho kết quả ngược lại :
value-1 IS NOT NULL
tương đương với:
NOT (value-1 IS NULL)
■ Các toán tử logic
• AND
predicate-1 AND predicate-2
AND cho kết quả:
o True nếu cả hai toán hạng là True
o False nếu một trong hai là False
Trang 10o NULL Nếu một trong hai toán hạng là NULL và toán hạng còn lại là
True hoặc cả hai đều NULL
Xem bảng giá trị của toán tử AND:
True False False False False False False True False
NULL False False
False NULL False
• Toán tử OR
predicate-1 OR predicate-2
OR cho kết quả:
o True Nếu một trong hai là True
o False Nếu cả hai là False
o NULL Cho các trường hợp còn lại (Một trong hai toán hạng là False
và toán hạng kia NULL hoặc cả hai đều NULL) Bảng giá trị OR:
False False False
AND có độ ưu tiên cao hơn OR, do đó:
Trang 111.6 Mệnh đề ORDER BY
Mệnh đề ORDER BY là mệnh đề tùy chọn (không bắt buộc phải có) nhưng nếu có mặt thì nó phải là mệnh đề cuối cùng của câu lệnh SELECT Mệnh đề này sắp xếp các bản ghi theo thứ tự nhất định
Khi không có mệnh đề ORDER BY thì kết quả sẽ không tuân theo một thứ tự nào cả Dạng thức của mệnh đề này như sau:
ORDER BY column-1 [ASC|DESC] [ column-2 [ASC|DESC] ]
trong đó column-1, column-2, là tên các cột cần sắp xếp và nó phải thuộc vào trong phần của mệnh đề SELECT Tùy chọn ASC chỉ định sắp xếp tăng dần và tùy chọn DESC chỉ định sắp xếp giảm dần Mặc định là ASC
Thứ tự ưu tiên từ trái sang phải Nghĩa là sắp xếp theo cột liệt kê đầu tiên, rồi đến cột tiếp theo,
Các cột có thể liệt kê theo tên hoặc theo số thứ tự (lấy từ mệnh đề SELECT) và bắt đầu
từ số 1
Ví dụ:
SELECT * FROM sp ORDER BY 3 DESC;
Trang 12SELECT * FROM sp ORDER BY qty DESC, s_id;
Trang 131.7 Các biểu thức
Trong các biểu thức, các toán hạng có thể là các tên cột, các giá trị cụ thể, các hàm, các giá trị hệ thống, một số câu lệnh đặc biệt Ta lần lượt xét các trường hợp này - trừ trường hợp các cột vì ta đã làm quen
1.8 Biểu diễn giá trị
Biểu diên giá trị tùy thuộc vào kiểu dữ liệu:
• Chuỗi ký tự (String) Chuỗi ký tự phải nằm giữa các dấu nháy đơn (') Nếu trong chuỗi ký tự đó có dấu nháy đơn thì dấu nháy đơn phải lặp ('')
• Số (Numeric) Biểu diễn theo cách thông thường:
• SUBSTRING(exp-1 FROM exp-2 [FOR exp-3])
Lấy dãy ký tự từ exp-1, bắt đầu bằng exp-2 và có độ dài là exp-3, đếm bắt đầu từ 1 Nếu không chỉ định exp-3, thì toàn bộ chuỗi bắt đầu từ exp-2 đến hết sẽ được lấy
• TRIM([LEADING|TRAILING|BOTH] [FROM] exp-1)
TRIM([LEADING|TRAILING|BOTH] exp-2 FROM exp-1)
Cắt các ký tự trắng bên trái, bên phải hoặc cả bên trái lẫn bên phải Mặc định là
BOTH (nghĩa là cắt cả hai bên)
• POSITION(exp-1 IN exp-2)
Trang 14Tìm exp-1, trong exp-2 Bắt đầu đếm từ 1 Nếu không tìm thấy, hàm cho giá trị 0
• CHAR_LENGTH(exp-1)
CHARACTER_LENGTH(exp-1)
Cho độ dài chuỗi ký tự exp-1
• OCTET_LENGTH(exp-1)
Cho kết quả là số các byte trong chuỗi exp-1
• EXTRACT(sub-field FROM exp-1)
Cho giá trị số lấy từ dữ liệu thời gian exp-1 Sub-field là một trong các từ khóa:
YEAR, QUARTER, MONTH, DAY, HOUR, MINUTE, SECOND,
• USER – Tên người dùng đang đăng nhập
• CURRENT_USER – giống như USER
• SESSION_USER –Tên gười dùng theo phiên làm việc hiện thời
• SYSTEM_USER – Tên người dùng hệ thống
• CURRENT_DATE – Ngày hiện tại
• CURRENT_TIME – Thời gian hiện tại
• CURRENT_TIMESTAMP – Timestamp hiện tại
1.11 Một số câu lệnh kết cấu biểu thức SQL đặc biệt
• CAST(exp-1 AS data-type)
Chuyển đổi biểu thức exp-1, về kiểu date-type
• COALESCE(exp-1, exp-2 [, exp-3] )
Cho exp-1 nếu không phải là null, nếu không cho exp-2 nếu nó không phải là null, Trả lại giá trị null nếu tất cả đều null
• CASE exp-1 { WHEN exp-2 THEN exp-3 } [ELSE exp-4] END
CASE { WHEN predicate-1 THEN exp-3 } [ELSE exp-4] END
Trang 15Trường hợp 1 so sánh exp-1 với exp-2 với mỗi mệnh đề WHEN Nếu bằng nhau, thì hàm sẽ cho kết quả là exp-3 từ mệnh đề THEN Nếu không tìm thấy thì hàm sẽ trả kết quả là exp-4 từ mệnh đề ELSE hoặc null nếu không chỉ định mệnh đề ELSE
Trường hợp 2, hàm đánh giá predicate-1 trong mỗi mệnh đề WHEN Nếu giá trị predicate tương ứng là true, thì hàm sẽ trả giá trị exp-3 tương ứng với mệnh đề THEN Nếu không có predicate nào cho giá trị true, thì hàm sẽ trả kết quả exp-4 từ mệnh đề ELSE hoặc trả giá trị NULL nếu mệnh đề ELSE không được chỉ định
1.12 Toán tử trong biểu thức
• Toán tử chuỗi (String Operators)
'ab' || 'cd' ==> 'abcd'
Chú ý: MySQL dùng hàm CONCAT thay thế cho toán tử chuỗi
• Toán tử số (Numeric operators)
o +
o -
o *
o /
Dùng cho kiểu dữ liệu số:
o TINYINT, SMALLINT, INT, BIGINT
o NUMERIC, DECIMAL
o FLOAT, DOUBLE, REAL
Dấu + và – có thể là dấu của một số dương, âm
Các phép toán đó cũng có thể áp dụng cho kiểu dữ liệu là thời gian, nhưng với vài điểm cần lưu ý Qui tắc cơ bản là:
o Thời gian khi cộng trừ một khoảng nào đó, sẽ ra kết quả là thời gian
o Thời gian trừ thời gian sẽ ra kết quả là khoảng thời gian
(datetime-1 - datetime-2) interval-qualifier Chúng ta chỉ đề cập đến interval-qualifier đối với các ứng dụng cụ thể
1.13 Liên kết bảng (Joining Tables)
Trong mệnh đề FROM, người ta cho phép sử dụng danh sách nhiều bảng Khi bản ghi của bảng này có mối tương quan với bản ghi của bảng kia thì ta có thể dùng toán tử liên kết (JOIN)
Trang 16Ví dụ:
SELECT * FROM sp, part;
cho kết quả 8 cột, 72 bản ghi (SV thử câu lệnh này)
Kết quả đó chính là tích Đề các Kết quả này ít có ý nghĩa trong thực tế
Câu lệnh sau có nghĩa hơn:: (SV hãy giải thích ý nghiã của câu lệnh sau)
SELECT *
FROM sp, part
WHERE sp.p_id = part.p_id
Cho kết quả:
s_id sp.p_id qty part.p_id pname color weight city
S1 P2 200 P2 Bolt Green 17 Paris
S1 P3 400 P3 Screw Blue 17 Rome
S1 P4 200 P4 Screw Red 14 London
S1 P5 100 P5 Cam Blue 12 Paris
S2 P2 400 P2 Bolt Green 17 Paris
S3 P2 200 P2 Bolt Green 17 Paris
S4 P2 200 P2 Bolt Green 17 Paris
S4 P4 300 P4 Screw Red 14 London
S4 P5 400 P5 Cam Blue 12 Paris
Ta thấy kết quả là hai cột p_id đều giống nhau Đây là kiểu liên kết bảng dựa trên cột
p_id và mệnh đề WHERE thể hiện điều đó
Phương thức liên kết này có tên gọi là inner equi-join equi có nghĩa là sử dụng so sánh
bằng (=) để liên kết các cột với nhau Các phương thức liên kết khác sử dụng các toán
tử so sánh một cách tổng quát (lớn hơn (>), nhỏ hơn (<), )
Từ inner có nghĩa là chỉ có các bản ghi thỏa mãn điều kiện so sánh mới liệt kê trong kết quả, các bản ghi khác sẽ bị loại bỏ Khác với liên kết inner là liên kết outer Liên kết outer cho kết quả liệt kê cả những bản ghi không thỏa mãn điều kiện (xem dưới đây)
Có thể liên kết nhiều hơn 2 bảng Ví dụ:
SELECT sname, qty, color
FROM supplier AS s, sp, part AS p
WHERE s.s_id = sp.s_id
AND sp.p_id = p.p_id
Trang 171.14 Liên kết ngoài (Outer Joins)
Liên kết ngoài có cú pháp như sau:
table-1 { LEFT | RIGHT | FULL } OUTER JOIN table-2 ON predicate-1
Trong đó predicate-1 là biểu thức điều kiện của OUTER JOIN
• LEFT – các bản ghi của bảng bên trái (table-1) được liệt kê
• RIGHT – các bản ghi của bảng bên phải (table-2) được liệt kê
• FULL – các bản ghi của cả hai bảng (table-1 và table-2) đều được liệt kê
SELECT part.p_id, color, s_id, qty
FROM part LEFT OUTER JOIN sp ON part.p_id = sp.p_id;
(SV kiểm tra kết quả câu lệnh trên)
1.15 Liên kết với chính nó (Self Joins)
Một hệ quả của các liên kết là ta có thể cho liên kết một bảng với chính nó Ví dụ, liệt
kê các mã phụ tùng có ít nhất hai nhà cung cấp cho phụ tùng đó
SELECT DISTINCT a.p_id
FROM sp a, sp b
WHERE a.p_id = b.p_id
AND a.s_id <> b.s_id;
Trang 181.16 Truy vấn nhóm (Grouping Queries)
Một trong những chức năng của của câu lệnh SELECT là gộp các bản ghi theo nhóm,
và tính năng này có tên là truy vấn nhóm
Các bản ghi được gộp theo nhóm dựa trên một đặc tính chung nào đó Và kết quả của truy vấn nhóm là chỉ đưa ra kết quả sau khi nhóm – không đưa ra chi tiết từng bản ghi thuộc nhóm
Trong từng bản ghi kết quả nhóm ta có thể sử dụng các hàm gộp (COUNT, AVG, SUM, MAX, MIN)
p_id s_id qty Nhóm theo p_id
Chú ý: Cách xử lý giá trị NULL như trên là theo chuẩn SQL92 – các HQT CSDL
trong thực tế có thể có cách xử lý khác Chúng ta cần tham khảo các tài liệu hướng dẫn sử dụng của các HQT CSDL đó
Trang 191.17 Mệnh đề GROUP BY
Mệnh đề GROUP BY phục vụ truy vấn nhóm Nếu có mệnh đề WHERE, thì mệnh đề GROUP BY phải đứng sau mệnh đề WHERE
GROUP BY column-1 [ASC | DESC], [, column-2 [ASC | DESC]]
Trong đó column-1 và column-2 là tên các cột dùng để nhóm Chú ý rằng
column-1 và column-2, phải là tên các cột SQL92 không chấp nhận biểu thức
Khi truy vấn nhóm, người ta thường sử dụng các hàm gộp
p_id s_id qty Nhóm theo p_id SUM(qty)