Dưới đây là 11 tùy chọn để trả về các hàng trùng lặp trong Cơ sở dữ liệu Oracle khi các hàng đó có khóa chính hoặc một số cột định danh duy nhất khác và bạn muốn bỏ qua nó.
Dữ liệu Mẫu
Chúng tôi sẽ sử dụng dữ liệu sau cho các ví dụ của chúng tôi:
SELECT * FROM Dogs;
Kết quả:
DOGID | FIRSTNAME | LASTNAME |
---|---|---|
1 | Vỏ cây | Smith |
2 | Vỏ cây | Smith |
3 | Gâu | Jones |
4 | Ruff | Robinson |
5 | Wag | Johnson |
6 | Wag | Johnson |
7 | Wag | Johnson |
Hai hàng đầu tiên là bản sao và ba hàng cuối cùng là bản sao. Các hàng trùng lặp chia sẻ chính xác các giá trị giống nhau trên tất cả các cột ngoại trừ khóa chính / cột ID duy nhất của chúng.
Cột khóa chính đảm bảo rằng không có hàng trùng lặp, đây là phương pháp hay trong RDBMS, vì khóa chính giúp thực thi tính toàn vẹn của dữ liệu. Nhưng thực tế là các khóa chính chứa các giá trị duy nhất có nghĩa là chúng ta cần bỏ qua cột đó khi tìm kiếm các bản sao.
Trong bảng của chúng tôi ở trên, cột khóa chính là một số tăng dần và giá trị của nó không mang ý nghĩa và không quan trọng. Do đó, chúng tôi có thể bỏ qua dữ liệu của cột đó khi tìm kiếm các bản sao.
Tùy chọn 1
Đây là tùy chọn đầu tiên của chúng tôi để trả lại các bản sao:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
ORDER BY Count DESC;
Kết quả:
FIRSTNAME | LASTNAME | COUNT |
---|---|---|
Wag | Johnson | 3 |
Vỏ cây | Smith | 2 |
Ruff | Robinson | 1 |
Gâu | Jones | 1 |
Ở đây, chúng tôi đã tạo truy vấn của mình với GROUP BY
để kết quả đầu ra được nhóm theo các cột có liên quan. Chúng tôi cũng đã sử dụng COUNT ()
hàm trả về số lượng hàng giống nhau. Và chúng tôi đã sắp xếp nó theo số lượng theo thứ tự giảm dần để các bản sao xuất hiện đầu tiên.
Kết quả cho chúng ta biết rằng có ba hàng chứa Wag Johnson và hai hàng chứa Bark Smith. Đây là các bản sao (hoặc bản sao ba bản trong trường hợp của Wag Johnson). Hai hàng còn lại không có bất kỳ bản sao nào.
Tùy chọn 2
Chúng ta có thể thêm HAVING
mệnh đề cho ví dụ trước của chúng tôi để loại trừ các trường hợp không trùng lặp khỏi đầu ra:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1
ORDER BY Count DESC;
Kết quả:
FIRSTNAME | LASTNAME | COUNT |
---|---|---|
Wag | Johnson | 3 |
Vỏ cây | Smith | 2 |
Tùy chọn 3
Chúng tôi cũng có thể kiểm tra các bản sao trên các cột được nối. Trong trường hợp này, chúng tôi sử dụng DISTINCT
từ khóa để nhận các giá trị riêng biệt, sau đó sử dụng COUNT ()
hàm để trả về số lượng:
SELECT
DISTINCT FirstName || ' ' || LastName AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName || ' ' || LastName
ORDER BY Count DESC;
Kết quả:
DOGNAME | COUNT |
---|---|
Wag Johnson | 3 |
Bark Smith | 2 |
Ruff Robinson | 1 |
Woof Jones | 1 |
Tùy chọn 4
Mỗi hàng trong Oracle có một rowid
cột giả trả về địa chỉ của hàng. rowid
là mã định danh duy nhất cho các hàng trong bảng và thường giá trị của nó xác định duy nhất một hàng trong cơ sở dữ liệu (mặc dù điều quan trọng cần lưu ý là các hàng trong các bảng khác nhau được lưu trữ cùng nhau trong cùng một cụm có thể có cùng một rowid ).
Dù sao, chúng ta có thể tạo một truy vấn sử dụng rowid
nếu chúng ta muốn:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.rowid > d2.rowid
);
Kết quả:
DOGID | FIRSTNAME | LASTNAME |
---|---|---|
2 | Vỏ cây | Smith |
6 | Wag | Johnson |
7 | Wag | Johnson |
Chúng tôi có thể thay thế SELECT *
với DELETE
để thực hiện thao tác khử trùng lặp trên bảng.
Lưu ý rằng chúng tôi có thể đã sử dụng DogId
cột (khóa chính của chúng tôi) thay vì rowid
nếu chúng tôi muốn. Điều đó nói rằng, rowid
có thể hữu ích nếu bạn không thể sử dụng cột khóa chính vì lý do nào đó hoặc nếu bảng không có khóa chính.
Tùy chọn 5
Đây là một truy vấn khác sử dụng rowid
:
SELECT * FROM Dogs
WHERE rowid > (
SELECT MIN(rowid) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Kết quả:
DOGID | FIRSTNAME | LASTNAME |
---|---|---|
2 | Vỏ cây | Smith |
6 | Wag | Johnson |
7 | Wag | Johnson |
Như với ví dụ trước, chúng ta có thể thay thế SELECT *
với DELETE
để xóa các hàng trùng lặp.
Tùy chọn 6
Hai rowid
các tùy chọn trên là tuyệt vời nếu bạn phải hoàn toàn bỏ qua khóa chính trong truy vấn của mình (hoặc nếu bạn hoàn toàn không có cột khóa chính). Tuy nhiên, như đã đề cập, vẫn có tùy chọn thay thế rowid
với cột khóa chính - trong trường hợp của chúng tôi là DogId
cột:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.DogId > d2.DogId
);
Kết quả:
DOGID | FIRSTNAME | LASTNAME |
---|---|---|
2 | Vỏ cây | Smith |
6 | Wag | Johnson |
7 | Wag | Johnson |
Tùy chọn 7
Và đây là truy vấn khác với rowid
được thay thế bằng DogId
cột:
SELECT * FROM Dogs
WHERE DogId > (
SELECT MIN(DogId) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Kết quả:
DOGID | FIRSTNAME | LASTNAME |
---|---|---|
2 | Vỏ cây | Smith |
6 | Wag | Johnson |
7 | Wag | Johnson |
Tùy chọn 8
Một cách khác để tìm các bản sao là sử dụng ROW_NUMBER ()
chức năng cửa sổ:
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs;
Kết quả:
DOGID | FIRSTNAME | LASTNAME | ROW_NUM |
---|---|---|---|
1 | Vỏ cây | Smith | 1 |
2 | Vỏ cây | Smith | 2 |
4 | Ruff | Robinson | 1 |
7 | Wag | Johnson | 1 |
5 | Wag | Johnson | 2 |
6 | Wag | Johnson | 3 |
3 | Gâu | Jones | 1 |
Sử dụng PARTITION
mệnh đề dẫn đến một cột mới được thêm vào, với số hàng tăng lên mỗi khi có một bản sao, nhưng đặt lại lần nữa khi có một hàng duy nhất.
Trong trường hợp này, chúng tôi không nhóm các kết quả, có nghĩa là chúng tôi có thể thấy từng hàng trùng lặp, bao gồm cả cột định danh duy nhất của nó.
Tùy chọn 9
Chúng ta cũng có thể sử dụng ví dụ trước làm biểu thức bảng chung trong một truy vấn lớn hơn:
WITH cte AS
(
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs
)
SELECT * FROM cte WHERE row_num <> 1;
Kết quả:
DOGID | FIRSTNAME | LASTNAME | ROW_NUM |
---|---|---|---|
2 | Vỏ cây | Smith | 2 |
5 | Wag | Johnson | 2 |
6 | Wag | Johnson | 3 |
Truy vấn đó loại trừ các mục không trùng lặp khỏi đầu ra và nó loại trừ một hàng của mỗi trùng lặp khỏi đầu ra.
Tùy chọn 10
Đây là một cách khác để có được đầu ra giống như ví dụ trước:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
MINUS SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Kết quả:
DOGID | FIRSTNAME | LASTNAME |
---|---|---|
2 | Vỏ cây | Smith |
6 | Wag | Johnson |
7 | Wag | Johnson |
Ví dụ này sử dụng MINUS
của Oracle toán tử, chỉ trả về các hàng duy nhất được trả về bởi truy vấn đầu tiên nhưng không trả về bởi truy vấn thứ hai.
MINUS
toán tử tương tự với EXCEPT
toán tử trong các DBMS khác, chẳng hạn như SQL Server, MariaDB, PostgreSQL và SQLite.
Tùy chọn 11
Dưới đây là một tùy chọn khác để chọn các bản sao từ bảng của chúng tôi:
SELECT *
FROM Dogs d1, Dogs d2
WHERE d1.FirstName = d2.FirstName
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId
AND d1.DogId = (
SELECT MAX(DogId)
FROM Dogs d3
WHERE d3.FirstName = d1.FirstName
AND d3.LastName = d1.LastName
);
Kết quả:
DOGID | FIRSTNAME | LASTNAME | CHÓ | FIRSTNAME | LASTNAME |
---|---|---|---|---|---|
2 | Vỏ cây | Smith | 1 | Vỏ cây | Smith |
7 | Wag | Johnson | 5 | Wag | Johnson |
7 | Wag | Johnson | 6 | Wag | Johnson |