Làm thế nào để chọn các hàng không có mục phù hợp trong bảng khác?


323

Tôi đang thực hiện một số công việc bảo trì trên một ứng dụng cơ sở dữ liệu và tôi đã phát hiện ra rằng, niềm vui của niềm vui, mặc dù các giá trị từ một bảng đang được sử dụng theo kiểu khóa ngoại, không có ràng buộc khóa ngoại trên các bảng.

Tôi đang cố gắng thêm các ràng buộc FK trên các cột này, nhưng tôi thấy rằng, vì đã có toàn bộ tải dữ liệu xấu trong các bảng từ các lỗi trước đó đã được sửa một cách ngây thơ, tôi cần tìm các hàng không được sửa phù hợp với bảng khác và sau đó xóa chúng.

Tôi đã tìm thấy một số ví dụ về loại truy vấn này trên web, nhưng tất cả chúng dường như cung cấp các ví dụ thay vì giải thích và tôi không hiểu tại sao chúng hoạt động.

Ai đó có thể giải thích cho tôi cách tạo một truy vấn trả về tất cả các hàng không khớp trong bảng khác không và nó đang làm gì để tôi có thể tự thực hiện các truy vấn này, thay vì chạy đến SO cho mỗi bảng trong mớ hỗn độn này không có ràng buộc FK?

Câu trả lời:


614

Đây là một truy vấn đơn giản:

SELECT t1.ID
FROM Table1 t1
    LEFT JOIN Table2 t2 ON t1.ID = t2.ID
WHERE t2.ID IS NULL

Những điểm chính là:

  1. LEFT JOINĐược sử dụng; điều này sẽ trả về TẤT CẢ các hàng từ Table1, bất kể có hàng phù hợp hay không Table2.

  2. Các WHERE t2.ID IS NULLkhoản; điều này sẽ hạn chế kết quả được trả về chỉ những hàng mà ID được trả về Table2là null - nói cách khác, KHÔNG có bản ghi nào Table2cho ID cụ thể đó từ đó Table1. Table2.IDsẽ được trả về dưới dạng NULL cho tất cả các bản ghi từ Table1nơi ID không khớp Table2.


4
Thất bại nếu ID là NULL
Michael

169
@Michael - Nếu có NULLID hợp lệ trong lược đồ của bạn, bạn có thể gặp vấn đề lớn hơn, bạn có đồng ý không? :)
rinogo

1
điều này sẽ làm việc ngay cả khi bảng1 có nhiều bản ghi hơn bảng2? nếu bảng1 có 100 bản ghi và bảng2 có 200 bản ghi (100 bản khớp / tham gia và 100 bản ghi không khớp / tham gia) thì chúng tôi có nhận được tất cả 200 bản ghi không?
Juan Velez

1
Tôi thường thích bọc tham gia bên trái dưới dạng xem truy vấn con / nội tuyến để đảm bảo không có sự tương tác giữa mệnh đề WHERE và THAM GIA TRÁI.
Andrew Wolfe

1
@Jas Điểm chính 1 của câu trả lời, TẤT CẢ các hàng từ bảng đầu tiên, ngay cả những hàng không khớp với điều kiện t1.ID = t2.ID của phép nối trái. Nếu bạn thay đổi dòng đầu tiên thành SELECT t1.ID, t2.IDvà xóa dòng WHERE, bạn sẽ hiểu rõ hơn về cách thức hoạt động của nó.
Peter Laboš

97

Tôi sẽ sử dụng EXISTSbiểu thức vì nó mạnh mẽ hơn, tức là bạn có thể chọn chính xác hơn các hàng bạn muốn tham gia, trong trường hợp LEFT JOINbạn phải lấy mọi thứ trong bảng đã tham gia. Hiệu quả của nó có lẽ giống như trong trường hợp LEFT JOINvới kiểm tra null.

SELECT t1.ID
FROM Table1 t1
WHERE NOT EXISTS (SELECT t2.ID FROM Table2 t2 WHERE t1.ID = t2.ID)

Một cái gì đó đơn giản này dễ dàng được xử lý bởi trình tối ưu hóa truy vấn để thực hiện tốt nhất.
Andrew Wolfe

2
Vâng, lợi thế chính của EXISTSnó là sự thay đổi của nó.
Ondrej Bozek

1
Đơn giản, thanh lịch và nó đã giải quyết vấn đề của tôi! Đẹp quá
MikeMighty

2
Trên thực tế đã giảm tốc độ của một truy vấn tôi có từ 7 giây xuống còn 200ms ... (so với WHERE t2.id IS NULL) Cảm ơn.
Moti Korets

4
@MotiKorets bạn có nghĩa là tăng tốc độ :)
Ondrej Bozek

14
SELECT id FROM table1 WHERE foreign_key_id_column NOT IN (SELECT id FROM table2)

Bảng 1 có một cột mà bạn muốn thêm ràng buộc khóa ngoài vào, nhưng các giá trị trong foreign_key_id_columnkhông hoàn toàn khớp với một idtrong bảng 2.

  1. Lựa chọn ban đầu liệt kê các ids từ bảng1. Đây sẽ là những hàng chúng tôi muốn xóa.
  2. Các NOT INđiều khoản trong bản tuyên bố nơi hạn chế truy vấn để chỉ hàng nơi các giá trị trong foreign_key_id_columnkhông có trong danh sách của bảng 2 ids.
  3. Câu SELECTlệnh trong ngoặc đơn sẽ nhận được một danh sách tất cả các ids trong bảng 2.

@ zb226: Liên kết của bạn phải thực hiện với các giới hạn về INmệnh đề với danh sách các giá trị bằng chữ. Nó không áp dụng cho việc sử dụng INmệnh đề với kết quả của truy vấn phụ. Câu trả lời được chấp nhận cho câu hỏi đó thực sự giải quyết vấn đề bằng cách sử dụng truy vấn phụ. (Một danh sách lớn các giá trị bằng chữ có vấn đề vì nó tạo ra một biểu thức SQL rất lớn. Một truy vấn phụ hoạt động tốt bởi vì, ngay cả khi danh sách kết quả lớn, chính biểu thức SQL là nhỏ.)
Kannan Goundan 8/2/19

@KannanGoundan Bạn hoàn toàn đúng. Rút lại những bình luận thiếu sót.
zb226

8

Trong trường hợp T2là bảng mà bạn đang thêm các hạn chế:

SELECT *
FROM T2
WHERE constrained_field NOT
IN (
    SELECT DISTINCT t.constrained_field
    FROM T2 
    INNER JOIN T1 t
    USING ( constrained_field )
)

Và xóa kết quả.


4

Hãy để chúng tôi có 2 bảng sau (lương và nhân viên) nhập mô tả hình ảnh ở đây

Bây giờ tôi muốn những hồ sơ từ bảng nhân viên không có lương. Chúng tôi có thể làm điều này theo 3 cách:

  1. Sử dụng bên trong Tham gia
select * from employee
where id not in(select e.id from employee e inner join salary s on e.id=s.id)

nhập mô tả hình ảnh ở đây

  1. Sử dụng nối ngoài trái
select * from employee e 
left outer join salary s on e.id=s.id  where s.id is null

nhập mô tả hình ảnh ở đây

  1. Sử dụng tham gia đầy đủ
select * from employee e
full outer join salary s on e.id=s.id where e.id not in(select id from salary)

nhập mô tả hình ảnh ở đây


2

Từ câu hỏi tương tự ở đây MySQL Truy vấn tham gia bên trong để nhận bản ghi không có trong bảng khác Tôi đã làm điều này để làm việc

SELECT * FROM bigtable 
LEFT JOIN smalltable ON bigtable.id = smalltable.id 
WHERE smalltable.id IS NULL

smalltablelà nơi bạn có hồ sơ bị thiếu, bigtablelà nơi bạn có tất cả các hồ sơ. Truy vấn liệt kê tất cả các bản ghi không tồn tại smalltablenhưng tồn tại trên bigtable. Bạn có thể thay thế idbởi bất kỳ tiêu chí phù hợp khác.


0

Bạn có thể chọn Chế độ xem như hiển thị bên dưới:

CREATE VIEW AuthorizedUserProjectView AS select t1.username as username, t1.email as useremail, p.id as projectid, 
(select m.role from userproject m where m.projectid = p.id and m.userid = t1.id) as role 
FROM authorizeduser as t1, project as p

và sau đó làm việc trên khung nhìn để chọn hoặc cập nhật:

select * from AuthorizedUserProjectView where projectid = 49

mà mang lại kết quả như trong hình bên dưới, tức là đối với cột không khớp không được điền vào.

[Result of select on the view][1]

0

Tôi không biết cái nào được tối ưu hóa (so với @AdaTheDev) nhưng cái này có vẻ nhanh hơn khi tôi sử dụng (ít nhất cho tôi)

SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2

Nếu bạn muốn nhận bất kỳ thuộc tính cụ thể nào khác, bạn có thể sử dụng:

SELECT COUNT(*) FROM table_1 where id in (SELECT id  FROM  table_1 EXCEPT SELECT DISTINCT (table1_id) table1_id FROM table_2);


-2

Bạn có thể làm một cái gì đó như thế này

   SELECT IFNULL(`price`.`fPrice`,100) as fPrice,product.ProductId,ProductName 
          FROM `products` left join `price` ON 
          price.ProductId=product.ProductId AND (GeoFancingId=1 OR GeoFancingId 
          IS NULL) WHERE Status="Active" AND Delete="No"

-6

Làm cách nào để chọn các hàng không có mục phù hợp trong cả hai bảng?

    chọn * từ [dbo]. [EmppDetails] e
     phải tham gia [Nhân viên]. [Giới tính] d trên e.Gid = d.Gid
    trong đó e.Gid là Null

    liên hiệp 
    chọn * từ [dbo]. [EmppDetails] e
     còn lại tham gia [Nhân viên]. [Giới tính] d trên e.Gid = d.Gid
    trong đó d.Gid là Null

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.