Trong SQL, bạn mô tả “những gì” bạn muốn đạt được, chứ không phải “làm thế nào” nó sẽ được thực thi. Do đó, vấn đề phát triển các truy vấn SQL theo kiểu “như người ta nghe nói là viết như thế nào” chiếm vị trí quan trọng, cùng với Tính năng tính điều kiện trong SQL.
Ngày nay, bằng cách sử dụng các ví dụ cực kỳ đơn giản, hãy xem điều này có thể dẫn đến điều gì trong bối cảnh sử dụng GROUP/DISTINCT и LIMIT với họ.
Bây giờ, nếu bạn viết trong yêu cầu “Đầu tiên hãy kết nối các dấu hiệu này, sau đó loại bỏ tất cả các dấu hiệu trùng lặp, lẽ ra chỉ còn lại một sao chép cho mỗi phím" - đây chính xác là cách nó sẽ hoạt động, ngay cả khi không cần kết nối.
Và đôi khi bạn may mắn và nó “chỉ hoạt động”, đôi khi nó gây ảnh hưởng khó chịu đến hiệu suất và đôi khi nó mang lại những hiệu ứng hoàn toàn bất ngờ theo quan điểm của nhà phát triển.
Chà, có lẽ không ngoạn mục lắm, nhưng...
“Cặp đôi ngọt ngào”: JOIN + DISTINCT
SELECT DISTINCT
X.*
FROM
X
JOIN
Y
ON Y.fk = X.pk
WHERE
Y.bool_condition;
Sẽ rõ ràng họ muốn gì chọn bản ghi X có bản ghi trong Y liên quan đến điều kiện được đáp ứng. Đã viết yêu cầu qua JOIN — đã nhận được một số giá trị pk nhiều lần (chính xác có bao nhiêu mục phù hợp xuất hiện trong Y). Làm thế nào để loại bỏ? Chắc chắn DISTINCT!
Điều đặc biệt “thật vui” khi mỗi bản ghi X có hàng trăm bản ghi Y liên quan, sau đó các bản sao trùng lặp sẽ bị loại bỏ một cách hào hùng…
Làm thế nào để khắc phục? Để bắt đầu, hãy nhận ra rằng vấn đề có thể được sửa đổi thành “chọn bản ghi X mà trong Y có ÍT NHẤT MỘT bản ghi được liên kết với điều kiện được đáp ứng” - xét cho cùng, chúng tôi không cần bất cứ thứ gì từ bản ghi Y.
TỒN TẠI lồng nhau
SELECT
*
FROM
X
WHERE
EXISTS(
SELECT
NULL
FROM
Y
WHERE
fk = X.pk AND
bool_condition
LIMIT 1
);
Một số phiên bản của PostgreSQL hiểu rằng trong EXISTS chỉ cần tìm mục đầu tiên xuất hiện là đủ, những mục cũ hơn thì không. Vì vậy tôi thích luôn luôn chỉ ra LIMIT 1 bên trong EXISTS.
THAM GIA BÊN
SELECT
X.*
FROM
X
, LATERAL (
SELECT
Y.*
FROM
Y
WHERE
fk = X.pk AND
bool_condition
LIMIT 1
) Y
WHERE
Y IS DISTINCT FROM NULL;
“Tại sao phải trả nhiều tiền hơn”: DISTINCT [ON] + GIỚI HẠN 1
Một lợi ích bổ sung của việc chuyển đổi truy vấn như vậy là khả năng dễ dàng giới hạn việc tìm kiếm các bản ghi nếu chỉ cần một hoặc một vài trong số chúng, như trong trường hợp sau:
SELECT DISTINCT ON(X.pk)
*
FROM
X
JOIN
Y
ON Y.fk = X.pk
LIMIT 1;
Bây giờ chúng ta đọc yêu cầu và cố gắng hiểu DBMS được đề xuất làm gì:
kết nối các dấu hiệu
độc đáo bởi X.pk
từ các mục còn lại, chọn một
Vậy bạn đã nhận được gì? "Chỉ một mục thôi" từ những cái duy nhất - và nếu chúng ta lấy cái này trong những cái không phải duy nhất, liệu kết quả có thay đổi theo cách nào đó không?.. “Và nếu không có sự khác biệt, tại sao phải trả nhiều tiền hơn?”
SELECT
*
FROM
(
SELECT
*
FROM
X
-- сюда можно подсунуть подходящих условий
LIMIT 1 -- +1 Limit
) X
JOIN
Y
ON Y.fk = X.pk
LIMIT 1;
Và chính xác cùng một chủ đề với GROUP BY + LIMIT 1.
“Tôi chỉ cần hỏi thôi”: ngầm định NHÓM + GIỚI HẠN
Những điều tương tự xảy ra ở những nơi khác nhau kiểm tra không trống dấu hiệu hoặc CTE khi yêu cầu tiến triển:
...
CASE
WHEN (
SELECT
count(*)
FROM
X
LIMIT 1
) = 0 THEN ...
Chức năng tổng hợp (count/min/max/sum/...) được thực thi thành công trên toàn bộ tập hợp, ngay cả khi không có hướng dẫn rõ ràng GROUP BY. Chỉ với LIMIT họ không thân thiện lắm.
Nhà phát triển có thể nghĩ “nếu có hồ sơ ở đó thì tôi không cần nhiều hơn GIỚI HẠN”. Nhưng đừng làm thế! Bởi vì đối với cơ sở nó là:
đếm những gì họ muốn theo tất cả hồ sơ
đưa ra bao nhiêu dòng tùy thích
Tùy thuộc vào điều kiện mục tiêu, có thể thực hiện một trong các thay thế sau:
(count + LIMIT 1) = 0trênNOT EXISTS(LIMIT 1)
(count + LIMIT 1) > 0trênEXISTS(LIMIT 1)
count >= Ntrên(SELECT count(*) FROM (... LIMIT N))
“Treo bao nhiêu gam”: DISTINCT + LIMIT
SELECT DISTINCT
pk
FROM
X
LIMIT $1
Một nhà phát triển ngây thơ có thể tin tưởng một cách chân thành rằng yêu cầu sẽ ngừng thực thi. ngay khi chúng tôi tìm thấy $1 trong số các giá trị khác nhau đầu tiên xuất hiện.
Đôi khi trong tương lai điều này có thể và sẽ hoạt động nhờ vào một nút mới Quét bỏ qua chỉ mục, việc triển khai hiện đang được thực hiện nhưng vẫn chưa.
Bây giờ đầu tiên tất cả hồ sơ sẽ được lấy, là duy nhất và chỉ từ họ số tiền được yêu cầu mới được trả lại. Thật là buồn nếu chúng ta muốn một cái gì đó như $ 1 = 4, và có hàng trăm ngàn bản ghi trong bảng...