sql中having和where可以一起用么

记得上次帮同事调试SQL的时候,他那个查询跑了半天,结果发现WHERE和HAVING用反了。
具体是啥部门来着?好像是个旅游公司,数据表是tour_departments,他想要筛选出人数超过1 0人的部门,结果却没筛选出来。
我一看,WHERE后面直接写了人数条件,这肯定不行啊。
他当时就挠头了,说是不是HAVING也可以用条件啊。
我说,WHERE是过滤原始数据,你得先用WHERE把不想要的先筛掉,然后GROUP BY分组,最后用HAVING对分组结果再筛一遍。
他听完才恍然大悟,赶紧改了代码,那查询一下就出来了。
不过话说回来,为啥用HAVING不用WHERE来过滤人数条件呢?难道是因为如果直接用WHERE,比如COUNT()>1 0,那岂不是每次都要统计所有行的总人数,然后再过滤?这样会不会更慢?

sql中on和where的区别 连接条件与过滤条件的本质差异

这SQL的ON和WHERE,当年我刚学的时候也晕得不行。
让我跟你唠唠我当年踩过的坑。

就说前几年吧,我帮一个朋友做报表,他非要我查某个月的订单,但客户得是VIP的。
我一开始就想啊,得先筛选出VIP客户,再找他们那个月的订单。
我写了这么个SQL:
sql SELECT FROM orders JOIN customers ON orders.customer_id = customers.id AND customers.is_vip = 1 WHERE orders.date BETWEEN '2 02 3 -01 -01 ' AND '2 02 3 -01 -3 1 ';
结果你猜怎么着?跑半天,数据不对。
后来他告诉我,得先把订单表和客户表连起来,再筛VIP客户。
得改写成这样:
sql SELECT FROM orders JOIN customers ON orders.customer_id = customers.id WHERE customers.is_vip = 1 AND orders.date BETWEEN '2 02 3 -01 -01 ' AND '2 02 3 -01 -3 1 ';
你看,ON里面是连接条件,WHERE是过滤条件。
ON决定了哪些订单能连到客户表上,WHERE是连完之后再筛一遍。
我那个朋友用的数据库是MySQL,他说他查了文档,书上写ON是在连接的时候用,WHERE是在连接完之后用。

还有一次,我处理一个LEFT JOIN,得查所有员工,就算他们没部门也要显示。
一开始我这么写:
sql SELECT FROM employees LEFT JOIN departments ON employees.department_id = departments.id WHERE departments.name = 'Engineering';
结果发现,那些没部门的员工直接没了。
后来我才明白,WHERE是在连接完之后过滤的,LEFT JOIN要保留左表所有行,但WHERE把右表没匹配上的(比如departments.name是空的)给筛掉了。
得改成ON里面加条件:
sql SELECT FROM employees LEFT JOIN departments ON employees.department_id = departments.id AND departments.name = 'Engineering';
这样,没部门的员工也会显示,只是departments.name是NULL。
这个教训挺深,外连接的右表条件放ON里,别放WHERE。

哦对了,还有个坑,就是ON和WHERE用哪个性能更好。
比如我有个大表,几百万条数据,我要筛选某个日期的订单。
我试了两种写法:
sql -
方式一 SELECT FROM orders JOIN customers ON orders.customer_id = customers.id WHERE orders.date = '2 02 3 -01 -01 ';
-
方式二 SELECT FROM orders JOIN customers ON orders.customer_id = customers.id AND orders.date = '2 02 3 -01 -01 ';
一开始我以为第一种更好,因为先筛选订单表,数据量小了再连。
结果我朋友用EXPLAIN查了一下,数据库优化器觉得两种差不多,得看具体表和索引。
他说他那个数据库(PostgreSQL)好像更喜欢ON里面带条件,因为ON是在连接前处理,能早点缩小数据集。
不过这个得具体看情况,不能一概而论。

总之啊,ON是连接条件,WHERE是过滤条件。
外连接(LEFT, RIGHT, FULL OUTER JOIN)的时候,右表的条件放ON里比较安全,别放WHERE,不然容易数据不全。
多试试EXPLAIN,看看数据库怎么执行的。

我这都是踩坑总结的,不一定全对,但绝对实用。
你还有啥不明白的,我再给你掰扯掰扯。

SQL中ON与WHERE的区别

ON和WHERE在JOIN里的作用确实不一样,尤其是在LEFT JOIN的时候。
我之前搞混过,差点把一个项目里的数据搞错。

比如说,假设你在2 02 2 年3 月处理一个订单系统数据库,有orders和customers两个表。
你想用LEFT JOIN查所有订单,不管客户有没有信息。

sql SELECT o.order_id, c.customer_name FROM orders o LEFT JOIN customers c ON o.customer_id = c.id;
在这个例子中,ON是定义o表和c表的关系,即通过customer_id关联。
不管c表有没有对应的customer_name,o表的所有order_id都会出现在结果里。
如果某个订单在c表中找不到客户信息,customer_name就是NULL。

但如果你把过滤条件也写进ON: sql SELECT o.order_id, c.customer_name FROM orders o LEFT JOIN customers c ON o.customer_id = c.id AND c.country = 'USA';
这时候,ON会先筛选出美国客户,然后 LEFT JOIN。
结果就是只有那些客户ID对应美国客户的订单会显示,其他订单(包括客户不是美国的)都不会出现。
这其实就变成了INNER JOIN的效果,跟LEFT JOIN的初衷就不一样了。

真正要用WHERE过滤的时候,应该像这样: sql SELECT o.order_id, c.customer_name FROM orders o LEFT JOIN customers c ON o.customer_id = c.id WHERE o.order_date > '2 02 2 -01 -01 ';
这里ON只管关联,WHERE才管过滤。
因为ON已经通过customer_id关联了表,WHERE可以根据订单日期过滤整个结果集,但不会影响左表(orders)的记录。

我之前在一个项目里差点犯这种错误,就是在一个LEFT JOIN里把某个条件写错了ON,导致返回的数据完全不对。
后来反应过来,必须把过滤条件放WHERE里才对。

还有个特别要注意的点,就是像时间范围的过滤。
比如查当前有效的薪资记录: sql SELECT e.employee_id, s.salary FROM employees e LEFT JOIN salaries s ON e.id = s.emp_id AND s.to_date = '9 9 9 9 -01 -01 ' WHERE s.to_date = '9 9 9 9 -01 -01 ';
这里把s.to_date = '9 9 9 9 -01 -01 '写两个地方,第一次在ON里,第二次在WHERE里。
其实这样是不对的。
放在ON里: sql LEFT JOIN salaries s ON e.id = s.emp_id AND s.to_date = '9 9 9 9 -01 -01 '
这样只会关联那些to_date是'9 9 9 9 -01 -01 '的薪资记录,其他薪资记录(包括空记录)都不会关联进来。
而实际上你可能希望所有员工都有薪资记录,只是空记录而已。
所以这种情况下,条件应该只放在WHERE里。

总之ON定义关联,WHERE过滤结果。
这个区别在写复杂的JOIN查询时特别重要。
特别是在处理LEFT JOIN的时候,一定要小心把过滤条件放对地方。