我们可以将什么条件作为JOIN ON的一部分?

时间:2014-11-09 18:25:09

标签: mysql sql join left-join

我很困惑将WHERE子句的部分放在JOIN的ON中不起作用以及为什么 我想我已经在某处读过,最好将条款作为ON条款的一部分 我尝试了以下(琐碎的例子): 在下面我注意到,对于内部联接,它将连接相同名称的列,但这会导致左联接错误。
然后我注意到ON (Employee.sdetails_id=SalaryDetails.sdetails_id and Employee.status<>5)没有过滤状态为5的任何内容。我认为它等同于ON (Employee.sdetails_id=SalaryDetails.sdetails_id) WHERE Employee.status<>5,但事实并非如此。
如果我修改Employee表并将其作为主键primary key (id, status),那么我会遇到同样的问题 有人可以解释ON如何工作以及为什么在这种情况下状态不会过滤任何东西,即使它是主键的一部分?

mysql> select * from Employee JOIN SalaryDetails;
+----+------+--------+--------+-------------+-------------+-----------------------------------------------------+
| id | name | status | salary | sdetails_id | sdetails_id | details                                             |
+----+------+--------+--------+-------------+-------------+-----------------------------------------------------+
|  1 | John |      0 |   1000 |           1 |           1 | Hired with the contract of a perm                   |
|  1 | John |      0 |   1000 |           1 |           2 | Hired with perm contract and salary to be increased |
|  2 | Jim  |      0 |   1200 |           1 |           1 | Hired with the contract of a perm                   |
|  2 | Jim  |      0 |   1200 |           1 |           2 | Hired with perm contract and salary to be increased |
|  3 | Nick |      5 |   1500 |           2 |           1 | Hired with the contract of a perm                   |
|  3 | Nick |      5 |   1500 |           2 |           2 | Hired with perm contract and salary to be increased |
+----+------+--------+--------+-------------+-------------+-----------------------------------------------------+
6 rows in set (0.00 sec)

mysql> select * from Employee LEFT JOIN SalaryDetails;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
mysql> select * from Employee LEFT JOIN SalaryDetails ON (Employee.sdetails_id=SalaryDetails.sdetails_id);
+----+------+--------+--------+-------------+-------------+-----------------------------------------------------+
| id | name | status | salary | sdetails_id | sdetails_id | details                                             |
+----+------+--------+--------+-------------+-------------+-----------------------------------------------------+
|  1 | John |      0 |   1000 |           1 |           1 | Hired with the contract of a perm                   |
|  2 | Jim  |      0 |   1200 |           1 |           1 | Hired with the contract of a perm                   |
|  3 | Nick |      5 |   1500 |           2 |           2 | Hired with perm contract and salary to be increased |
+----+------+--------+--------+-------------+-------------+-----------------------------------------------------+
3 rows in set (0.00 sec)

mysql> select * from Employee LEFT JOIN SalaryDetails ON (Employee.sdetails_id=SalaryDetails.sdetails_id and Employee.status<>5);
+----+------+--------+--------+-------------+-------------+-----------------------------------+
| id | name | status | salary | sdetails_id | sdetails_id | details                           |
+----+------+--------+--------+-------------+-------------+-----------------------------------+
|  1 | John |      0 |   1000 |           1 |           1 | Hired with the contract of a perm |
|  2 | Jim  |      0 |   1200 |           1 |           1 | Hired with the contract of a perm |
|  3 | Nick |      5 |   1500 |           2 |        NULL | NULL                              |
+----+------+--------+--------+-------------+-------------+-----------------------------------+
3 rows in set (0.00 sec)

mysql> select * from Employee LEFT JOIN SalaryDetails ON (Employee.sdetails_id=SalaryDetails.sdetails_id) where  Employee.status<>5;
+----+------+--------+--------+-------------+-------------+-----------------------------------+
| id | name | status | salary | sdetails_id | sdetails_id | details                           |
+----+------+--------+--------+-------------+-------------+-----------------------------------+
|  1 | John |      0 |   1000 |           1 |           1 | Hired with the contract of a perm |
|  2 | Jim  |      0 |   1200 |           1 |           1 | Hired with the contract of a perm |
+----+------+--------+--------+-------------+-------------+-----------------------------------+
2 rows in set (0.00 sec)

mysql> show create table Employee;
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table    | Create Table                                                                                                                                                                                                                                                                                   |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Employee | CREATE TABLE `Employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `status` int(11) DEFAULT NULL,
  `salary` decimal(5,0) DEFAULT NULL,
  `sdetails_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 |
+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show create table SalaryDetails;
+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table         | Create Table                                                                                                                                                        |
+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SalaryDetails | CREATE TABLE `SalaryDetails` (
  `sdetails_id` int(11) NOT NULL DEFAULT '0',
  `details` text,
  PRIMARY KEY (`sdetails_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from Employee;
+----+------+--------+--------+-------------+
| id | name | status | salary | sdetails_id |
+----+------+--------+--------+-------------+
|  1 | John |      0 |   1000 |           1 |
|  2 | Jim  |      0 |   1200 |           1 |
|  3 | Nick |      5 |   1500 |           2 |
+----+------+--------+--------+-------------+
3 rows in set (0.00 sec)

mysql> select * from SalaryDetails;
+-------------+-----------------------------------------------------+
| sdetails_id | details                                             |
+-------------+-----------------------------------------------------+
|           1 | Hired with the contract of a perm                   |
|           2 | Hired with perm contract and salary to be increased |
+-------------+-----------------------------------------------------+
2 rows in set (0.00 sec)

2 个答案:

答案 0 :(得分:2)

ON子句指定决定何时连接一侧的行可以与连接另一侧的行匹配的逻辑。 WHERE子句指定整个结果集的过滤逻辑。

在使用ON (Employee.sdetails_id=SalaryDetails.sdetails_id and Employee.status<>5)的示例中,您选择从Employee开始的所有行,然后仅在找到匹配的SalaryDetail条目时要求数据库加入,其中ID为相同,状态不是5.正如您在结果集中看到的,Nick没有SalaryDetails表中的详细信息,因为这些值为null。没有结果,因为如果状态为5,则要求数据库不要加入。这不会阻止它从Employee表返回连接的“左”部分。

如果您只想包含实际发生加入的行,那么您将使用INNER JOIN而不是LEFT JOIN。我建议reading up on the different types of join

在许多RDBMS中,您实际上可以将所有逻辑放在WHERE子句中,但如果在编写SQL时在语义上将连接逻辑与行过滤分开,则(主观上)更清晰。您的数据库将在内部完成相同的工作量。

就我个人而言,我会将您的查询编写为INNER JOIN,ID为ONWHERE子句中的状态字段为过滤器。

答案 1 :(得分:0)

SELECT * FROM T1 LEFT JOIN T2 ON condition

从T1中选择所有内容,如果找到符合条件的内容,则加入

SELECT * FROM T1 INNER JOIN T2 ON condition

从T1中选择所有内容并加入T2,但只有在T2中找到匹配的部分

所以使用INNER JOIN过滤