Tôi muốn tham gia đầy đủ bên ngoài Tham gia vào MySQL. Điều này có thể không? Là một tham gia đầy đủ bên ngoài được hỗ trợ bởi MySQL?
Tôi muốn tham gia đầy đủ bên ngoài Tham gia vào MySQL. Điều này có thể không? Là một tham gia đầy đủ bên ngoài được hỗ trợ bởi MySQL?
Câu trả lời:
Bạn không có FULL THAM GIA trên MySQL, nhưng bạn có thể chắc chắn mô phỏng chúng .
Đối với một mã SAMPLE được sao chép từ câu hỏi SO này, bạn có:
với hai bảng t1, t2:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Truy vấn trên hoạt động đối với các trường hợp đặc biệt trong đó thao tác FULL OUTER THAM GIA sẽ không tạo ra bất kỳ hàng trùng lặp nào. Truy vấn trên phụ thuộc vào UNION
toán tử tập hợp để loại bỏ các hàng trùng lặp được giới thiệu bởi mẫu truy vấn. Chúng tôi có thể tránh giới thiệu các hàng trùng lặp bằng cách sử dụng mẫu chống tham gia cho truy vấn thứ hai và sau đó sử dụng toán tử UNION ALL để kết hợp hai bộ. Trong trường hợp tổng quát hơn, trong đó FULL OUTER THAM GIA sẽ trả về các hàng trùng lặp, chúng ta có thể làm điều này:
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION ALL
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.id IS NULL
(SELECT ... FROM tbl1 LEFT JOIN tbl2 ...) UNION ALL (SELECT ... FROM tbl1 RIGHT JOIN tbl2 ... WHERE tbl1.col IS NULL)
t1
và t2
, truy vấn trong câu trả lời này sẽ trả về một tập kết quả mô phỏng FULL OUTER THAM GIA. Nhưng trong trường hợp tổng quát hơn, ví dụ, danh sách CHỌN không chứa đủ các cột / biểu thức để làm cho các hàng được trả về là duy nhất, thì mẫu truy vấn này không đủ để tạo lại tập hợp sẽ được tạo bởi a FULL OUTER JOIN
. Để có được một mô phỏng trung thực hơn, chúng ta cần một UNION ALL
toán tử được thiết lập và một trong các truy vấn sẽ cần một mẫu chống tham gia . Nhận xét từ Pavle Lekic (ở trên) đưa ra mẫu truy vấn chính xác .
Câu trả lời mà Pablo Santa Cruz đưa ra là chính xác; tuy nhiên, trong trường hợp bất kỳ ai vấp ngã trên trang này và muốn làm rõ hơn, đây là một sự cố chi tiết.
Giả sử chúng ta có các bảng sau:
-- t1
id name
1 Tim
2 Marta
-- t2
id name
1 Tim
3 Katarina
Một tham gia bên trong, như thế này:
SELECT *
FROM `t1`
INNER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Sẽ chỉ cho chúng tôi các bản ghi xuất hiện trong cả hai bảng, như thế này:
1 Tim 1 Tim
Các liên kết bên trong không có hướng (như trái hoặc phải) vì chúng rõ ràng là hai chiều - chúng tôi yêu cầu một trận đấu ở cả hai bên.
Mặt khác, các phép nối ngoài là để tìm các bản ghi có thể không khớp trong bảng khác. Như vậy, bạn phải chỉ định phía nào của phép nối được phép có một bản ghi bị thiếu.
LEFT JOIN
và RIGHT JOIN
là tốc ký cho LEFT OUTER JOIN
và RIGHT OUTER JOIN
; Tôi sẽ sử dụng tên đầy đủ của họ dưới đây để củng cố khái niệm về các phép nối bên ngoài so với các phép nối bên trong.
Một tham gia bên ngoài bên trái, như thế này:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... sẽ giúp chúng tôi có tất cả các bản ghi từ bảng bên trái bất kể họ có trận đấu ở bảng bên phải hay không, như sau:
1 Tim 1 Tim
2 Marta NULL NULL
Một tham gia bên ngoài bên phải, như thế này:
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
... sẽ giúp chúng tôi có tất cả các hồ sơ từ bảng bên phải bất kể họ có trận đấu ở bảng bên trái hay không, như thế này:
1 Tim 1 Tim
NULL NULL 3 Katarina
Một kết nối bên ngoài đầy đủ sẽ cung cấp cho chúng tôi tất cả các bản ghi từ cả hai bảng, cho dù chúng có khớp trong bảng kia hay không, với NULL ở cả hai bên không có kết quả khớp. Kết quả sẽ như thế này:
1 Tim 1 Tim
2 Marta NULL NULL
NULL NULL 3 Katarina
Tuy nhiên, như Pablo Santa Cruz đã chỉ ra, MySQL không hỗ trợ điều này. Chúng ta có thể mô phỏng nó bằng cách thực hiện UNION của một liên kết bên trái và một tham gia bên phải, như thế này:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`;
Bạn có thể nghĩ về một UNION
ý nghĩa "chạy cả hai truy vấn này, sau đó xếp kết quả lên nhau"; một số hàng sẽ đến từ truy vấn đầu tiên và một số từ truy vấn thứ hai.
Cần lưu ý rằng một UNION
trong MySQL sẽ loại bỏ các bản sao chính xác: Tim sẽ xuất hiện trong cả hai truy vấn ở đây, nhưng kết quả của việc UNION
chỉ liệt kê anh ta một lần. Đồng nghiệp guru cơ sở dữ liệu của tôi cảm thấy rằng hành vi này không nên được dựa vào. Vì vậy, để rõ ràng hơn về nó, chúng ta có thể thêm một WHERE
mệnh đề vào truy vấn thứ hai:
SELECT *
FROM `t1`
LEFT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
UNION
SELECT *
FROM `t1`
RIGHT OUTER JOIN `t2` ON `t1`.`id` = `t2`.`id`
WHERE `t1`.`id` IS NULL;
Mặt khác, nếu bạn muốn xem các bản sao vì một số lý do, bạn có thể sử dụng UNION ALL
.
FULL OUTER JOIN
. Không có gì sai khi thực hiện các truy vấn theo cách đó và sử dụng UNION để xóa các bản sao đó. Nhưng để thực sự sao chép a FULL OUTER JOIN
, chúng ta cần một trong các truy vấn là chống tham gia.
UNION
thao tác sẽ loại bỏ các bản sao đó; nhưng nó cũng loại bỏ TẤT CẢ các hàng trùng lặp, bao gồm các hàng trùng lặp sẽ được trả về bởi FULL OUTER THAM GIA. Để mô phỏng a FULL JOIN b
, mô hình chính xác là (a LEFT JOIN b) UNION ALL (b ANTI JOIN a)
.
Sử dụng union
truy vấn sẽ loại bỏ các bản sao và điều này khác với hành vi full outer join
không bao giờ xóa bất kỳ bản sao nào:
[Table: t1] [Table: t2]
value value
------- -------
1 1
2 2
4 2
4 5
Đây là kết quả mong đợi của full outer join
:
value | value
------+-------
1 | 1
2 | 2
2 | 2
Null | 5
4 | Null
4 | Null
Đây là kết quả của việc sử dụng left
và right Join
với union
:
value | value
------+-------
Null | 5
1 | 1
2 | 2
4 | Null
Truy vấn đề xuất của tôi là:
select
t1.value, t2.value
from t1
left outer join t2
on t1.value = t2.value
union all -- Using `union all` instead of `union`
select
t1.value, t2.value
from t2
left outer join t1
on t1.value = t2.value
where
t1.value IS NULL
Kết quả của truy vấn trên giống như kết quả mong đợi:
value | value
------+-------
1 | 1
2 | 2
2 | 2
4 | NULL
4 | NULL
NULL | 5
@Steve Chambers : [Từ ý kiến, với nhiều lời cảm ơn!]
Lưu ý: Đây có thể là giải pháp tốt nhất, cả về hiệu quả và tạo ra kết quả tương tự như aFULL OUTER JOIN
. Bài đăng trên blog này cũng giải thích rất rõ - trích dẫn từ Phương pháp 2: "Điều này xử lý các hàng trùng lặp chính xác và không bao gồm bất cứ điều gì không nên. Nó cần thiết để sử dụngUNION ALL
thay vì đơn giảnUNION
, sẽ loại bỏ các bản sao tôi muốn giữ. có thể hiệu quả hơn đáng kể trên các tập kết quả lớn, vì không cần phải sắp xếp và loại bỏ các bản sao. "
Tôi quyết định thêm một giải pháp khác đến từ full outer join
trực quan hóa và toán học, nó không tốt hơn ở trên nhưng dễ đọc hơn:
Tham gia đầy đủ bên ngoài có nghĩa là
(t1 ∪ t2)
: tất cả trongt1
hoặc trongt2
(t1 ∪ t2) = (t1 ∩ t2) + t1_only + t2_only
: tất cả trong cả hait1
vàt2
cộng với tất cả trongt1
đó không có trongt2
và cộng với tất cả trongt2
đó không có trongt1
:
-- (t1 ∩ t2): all in both t1 and t2
select t1.value, t2.value
from t1 join t2 on t1.value = t2.value
union all -- And plus
-- all in t1 that not exists in t2
select t1.value, null
from t1
where not exists( select 1 from t2 where t2.value = t1.value)
union all -- and plus
-- all in t2 that not exists in t1
select null, t2.value
from t2
where not exists( select 1 from t1 where t2.value = t1.value)
FULL OUTER JOIN
. Bài đăng trên blog này cũng giải thích rất rõ - trích dẫn từ Phương pháp 2: "Điều này xử lý các hàng trùng lặp chính xác và không bao gồm bất cứ điều gì không nên. Cần sử dụng UNION ALL thay vì UNION đơn giản, loại bỏ các trùng lặp tôi muốn giữ lại. Điều này có thể hiệu quả hơn đáng kể trên các tập kết quả lớn, vì không cần phải sắp xếp và loại bỏ các bản sao. "
MySql không có cú pháp FULL-OUTER-THAM GIA. Bạn phải mô phỏng bằng cách thực hiện cả TRÁI PHẢI và THAM GIA ĐÚNG như sau-
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
Nhưng MySql cũng không có cú pháp RIGHT THAM GIA. Theo đơn giản hóa kết nối ngoài của MySql , phép nối bên phải được chuyển đổi thành phép nối trái tương đương bằng cách chuyển t1 và t2 trong mệnh đề FROM
and ON
trong truy vấn. Do đó, Trình tối ưu hóa truy vấn MySql dịch truy vấn ban đầu thành sau -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
Bây giờ, không có hại khi viết truy vấn ban đầu, nhưng hãy nói nếu bạn có các vị từ như mệnh đề WHERE, đó là một vị từ trước khi tham gia hoặc một vị từ AND trên ON
mệnh đề, đó là một vị từ trong quá trình tham gia , thì bạn có thể muốn nhìn vào quỷ dữ; đó là chi tiết.
Trình tối ưu hóa truy vấn MySql thường xuyên kiểm tra các vị từ nếu chúng bị từ chối . Bây giờ, nếu bạn đã thực hiện RIGHT THAM GIA, nhưng với vị từ WHERE trên cột từ t1, thì bạn có thể gặp rủi ro khi chạy vào một kịch bản bị từ chối .
Ví dụ: truy vấn sau đây -
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t1
RIGHT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
được dịch sang phần sau bởi Trình tối ưu hóa truy vấn-
SELECT * FROM t1
LEFT JOIN t2 ON t1.id = t2.id
WHERE t1.col1 = 'someValue'
UNION
SELECT * FROM t2
LEFT JOIN t1 ON t2.id = t1.id
WHERE t1.col1 = 'someValue'
Vì vậy, thứ tự của các bảng đã thay đổi, nhưng vị từ vẫn được áp dụng cho t1, nhưng t1 hiện nằm trong mệnh đề 'ON'. Nếu t1.col1 được định nghĩa là NOT NULL
cột, thì truy vấn này sẽ bị từ chối .
Bất kỳ tham gia ngoài nào (trái, phải, đầy đủ) bị từ chối null đều được MySql chuyển đổi thành tham gia bên trong.
Do đó, kết quả mà bạn có thể mong đợi có thể hoàn toàn khác với những gì MySql đang trả về. Bạn có thể nghĩ rằng đó là một lỗi với RIGHT THAM GIA của MySql, nhưng điều đó không đúng. Nó chỉ là cách trình tối ưu hóa truy vấn MySql hoạt động. Vì vậy, nhà phát triển phụ trách phải chú ý đến những sắc thái này khi anh ta đang xây dựng truy vấn.
Trong SQLite, bạn nên làm điều này:
SELECT *
FROM leftTable lt
LEFT JOIN rightTable rt ON lt.id = rt.lrid
UNION
SELECT lt.*, rl.* -- To match column set
FROM rightTable rt
LEFT JOIN leftTable lt ON lt.id = rt.lrid
Không có câu trả lời nào ở trên là thực sự chính xác, vì chúng không tuân theo ngữ nghĩa khi có các giá trị trùng lặp.
Đối với một truy vấn, chẳng hạn như (từ bản sao này ):
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.Name = t2.Name;
Tương đương chính xác là:
SELECT t1.*, t2.*
FROM (SELECT name FROM t1 UNION -- This is intentionally UNION to remove duplicates
SELECT name FROM t2
) n LEFT JOIN
t1
ON t1.name = n.name LEFT JOIN
t2
ON t2.name = n.name;
Nếu bạn cần điều này để làm việc với NULL
các giá trị (cũng có thể cần thiết), thì hãy sử dụng NULL
toán tử so sánh an toàn, <=>
thay vì =
.
FULL OUTER JOIN
bất cứ khi nào name
cột không có giá trị. Các union all
truy vấn với-tham gia chống mẫu nên tái tạo bên ngoài tham gia hành vi một cách chính xác, nhưng mà giải pháp là hơn thích hợp phụ thuộc vào bối cảnh và những hạn chế đang hoạt động trên các bảng.
union all
, nhưng câu trả lời đó bỏ lỡ một mẫu chống tham gia trong truy vấn đầu tiên hoặc truy vấn thứ hai sẽ giữ các bản sao hiện có nhưng ngăn không cho thêm các câu hỏi mới. Tùy thuộc vào bối cảnh, các giải pháp khác (như giải pháp này) có thể phù hợp hơn.
Truy vấn của shA.t đã sửa đổi để rõ ràng hơn:
-- t1 left join t2
SELECT t1.value, t2.value
FROM t1 LEFT JOIN t2 ON t1.value = t2.value
UNION ALL -- include duplicates
-- t1 right exclude join t2 (records found only in t2)
SELECT t1.value, t2.value
FROM t1 RIGHT JOIN t2 ON t1.value = t2.value
WHERE t2.value IS NULL
Bạn nói gì về giải pháp tham gia Cross ?
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON 1=1;
select (select count(*) from t1) * (select count(*) from t2))
các hàng trong tập kết quả.
SELECT
a.name,
b.title
FROM
author AS a
LEFT JOIN
book AS b
ON a.id = b.author_id
UNION
SELECT
a.name,
b.title
FROM
author AS a
RIGHT JOIN
book AS b
ON a.id = b.author_id
Cũng có thể, nhưng bạn phải đề cập đến cùng tên trường trong phần chọn.
SELECT t1.name, t2.name FROM t1
LEFT JOIN t2 ON t1.id = t2.id
UNION
SELECT t1.name, t2.name FROM t2
LEFT JOIN t1 ON t1.id = t2.id
Tôi sửa phản hồi và các tác phẩm bao gồm tất cả các hàng (dựa trên phản hồi của Pavle Lekic)
(
SELECT a.* FROM tablea a
LEFT JOIN tableb b ON a.`key` = b.key
WHERE b.`key` is null
)
UNION ALL
(
SELECT a.* FROM tablea a
LEFT JOIN tableb b ON a.`key` = b.key
where a.`key` = b.`key`
)
UNION ALL
(
SELECT b.* FROM tablea a
right JOIN tableb b ON b.`key` = a.key
WHERE a.`key` is null
);
tablea
đó không có kết quả khớp tableb
và ngược lại. Các bạn cố gắng UNION ALL
, sẽ chỉ hoạt động nếu hai bảng này có các cột được sắp xếp tương đương, không được đảm bảo.
Câu trả lời:
SELECT * FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;
Có thể được tạo lại như sau:
SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
LEFT JOIN t1 ON t1.id = tmp.id
LEFT JOIN t2 ON t2.id = tmp.id;
Sử dụng câu trả lời UNION hoặc UNION ALL không bao gồm trường hợp cạnh trong đó các bảng cơ sở có các mục trùng lặp.
Giải trình:
Có một trường hợp cạnh mà UNION hoặc UNION ALL không thể bao gồm. Chúng tôi không thể kiểm tra điều này trên mysql vì nó không hỗ trợ FULL OUTER THAM GIA, nhưng chúng tôi có thể minh họa điều này trên cơ sở dữ liệu hỗ trợ nó:
WITH cte_t1 AS
(
SELECT 1 AS id1
UNION ALL SELECT 2
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 6
),
cte_t2 AS
(
SELECT 3 AS id2
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
UNION ALL SELECT 6
)
SELECT * FROM cte_t1 t1 FULL OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2;
This gives us this answer:
id1 id2
1 NULL
2 NULL
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Giải pháp của UNION:
SELECT * FROM cte_t1 t1 LEFT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
UNION
SELECT * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
Đưa ra một câu trả lời không chính xác:
id1 id2
NULL 3
NULL 4
1 NULL
2 NULL
5 5
6 6
Giải pháp UNION ALL:
SELECT * FROM cte_t1 t1 LEFT OUTER join cte_t2 t2 ON t1.id1 = t2.id2
UNION ALL
SELECT * FROM cte_t1 t1 RIGHT OUTER JOIN cte_t2 t2 ON t1.id1 = t2.id2
Cũng không đúng.
id1 id2
1 NULL
2 NULL
5 5
6 6
6 6
6 6
6 6
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Trong khi đó truy vấn này:
SELECT t1.*, t2.*
FROM (SELECT * FROM t1 UNION SELECT name FROM t2) tmp
LEFT JOIN t1 ON t1.id = tmp.id
LEFT JOIN t2 ON t2.id = tmp.id;
Cung cấp như sau:
id1 id2
1 NULL
2 NULL
NULL 3
NULL 4
5 5
6 6
6 6
6 6
6 6
Thứ tự là khác nhau, nhưng mặt khác phù hợp với câu trả lời chính xác.
UNION ALL
giải pháp. Ngoài ra, nó trình bày một giải pháp sử dụng UNION
sẽ chậm hơn trên các bảng nguồn lớn do việc sao chép được yêu cầu. Cuối cùng, nó sẽ không biên dịch, vì trường id
không tồn tại trong truy vấn con tmp
.
UNION ALL
Giải pháp: ... Cũng không chính xác." Mã bạn trình bày loại bỏ giao lộ loại trừ khỏi tham gia phải ( where t1.id1 is null
) phải được cung cấp trong UNION ALL
. Có thể nói, giải pháp của bạn hơn hẳn các giải pháp khác, chỉ khi một trong những giải pháp đó được thực hiện không chính xác. Về "sự dễ thương", điểm lấy. Đó là vô cớ, lời xin lỗi của tôi.
Tiêu chuẩn SQL cho biết full join on
là inner join on
các hàng union all
không khớp với các hàng của bảng bên trái được mở rộng bằng null union all
các hàng của bảng bên phải được mở rộng bằng null. Tức là inner join on
hàng union all
hàng trong left join on
nhưng không inner join on
union all
hàng trong right join on
nhưng không inner join on
.
Tức là left join on
hàng union all
right join on
hàng không trong inner join on
. Hoặc nếu bạn biết inner join on
kết quả của mình không thể có null trong một cột trong bảng bên phải cụ thể thì " right join on
hàng không trong inner join on
" là các hàng right join on
với on
điều kiện được mở rộng bởi and
cột đó is null
.
Tức là hàng right join on
union all
thích hợp tương tự left join on
.
Từ sự khác biệt giữa các thành viên của IN IN THAM GIA và trực tiếp ra mắt là gì? :
(Tiêu chuẩn SQL 2006 SQL / Foundation 7.7 Quy tắc cú pháp 1, Quy tắc chung 1 b, 3 c & d, 5 b.)