当前位置: 首页 > 面试题库 >

允许在HAVING子句中使用别名的性能影响

燕文昌
2023-03-14
问题内容

今天早些时候我在这个问题上愚弄了自己。问题是使用SQL
Server,正确的答案涉及添加HAVING子句。我最初犯的错误是认为该SELECT语句中可以使用该语句中的别名HAVING,这在SQL
Server中是不允许的。我犯此错误是因为我假设SQL Server与MySQL具有相同的规则,该规则允许在HAVING子句中使用别名。

这让我感到好奇,然后我在Stack Overflow和其他地方四处闲逛,找到了大量材料来解释为什么在两个各自的RDBMS上强制执行这些 规则
。但是,我没有找到关于在子句中允许/禁止别名的 性能 含义的解释HAVING

举一个具体的例子,我将重复上述问题中出现的查询:

SELECT students.camID, campus.camName, COUNT(students.stuID) as studentCount
FROM students
JOIN campus
    ON campus.camID = students.camID
GROUP BY students.camID, campus.camName
HAVING COUNT(students.stuID) > 3
ORDER BY studentCount

HAVING子句中使用别名而不是重新指定COUNT?的性能含义是什么?这个问题可以在MySQL中直接回答,希望有人可以深入了解如果在HAVING子句中支持别名,那么在SQL中会发生什么。

在极少数情况下,可以同时使用MySQL和SQL Server标记SQL问题,因此请在阳光下享受这一刻。


问题答案:

仅仅专注于特定查询,并在下面加载示例数据。这确实解决了其他一些查询,例如count(distinct ...)其他人提到的查询。

alias in the HAVING出现要么略微优于或相当多的优于其替代(取决于查询)。

这使用一个预先存在的表,其中大约有500万行是通过我的回答快速创建的,耗时3至5分钟。

结果结构:

CREATE TABLE `ratings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `thing` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8;

但是使用INNODB代替。由于范围保留插入而创建预期的INNODB间隙异常。只是说而已,没有区别。470万行。

修改表以接近Tim的假定架构。

rename table ratings to students; -- not exactly instanteous (a COPY)
alter table students add column camId int; -- get it near Tim's schema
-- don't add the `camId` index yet

以下将花费一些时间。一次又一次地大块运行它,否则您的连接可能会超时。超时是由于update语句中有500万行没有LIMIT子句。注意,我们 确实
有一个LIMIT子句。

因此,我们将进行半百万次的行迭代。将列设置为1到20之间的随机数

update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise)

继续运行以上内容,直到no camId为null。

我跑了10次(整个过程需要7到10分钟)

select camId,count(*) from students
group by camId order by 1 ;

1   235641
2   236060
3   236249
4   235736
5   236333
6   235540
7   235870
8   236815
9   235950
10  235594
11  236504
12  236483
13  235656
14  236264
15  236050
16  236176
17  236097
18  235239
19  235556
20  234779

select count(*) from students;
-- 4.7 Million rows

创建一个有用的索引(当然是在插入之后)。

create index `ix_stu_cam` on students(camId); -- takes 45 seconds

ANALYZE TABLE students; -- update the stats: http://dev.mysql.com/doc/refman/5.7/en/analyze-table.html
-- the above is fine, takes 1 second

创建校园表。

create table campus
(   camID int auto_increment primary key,
    camName varchar(100) not null
);
insert campus(camName) values
('one'),('2'),('3'),('4'),('5'),
('6'),('7'),('8'),('9'),('ten'),
('etc'),('etc'),('etc'),('etc'),('etc'),
('etc'),('etc'),('etc'),('etc'),('twenty');
-- ok 20 of them

运行两个查询:

SELECT students.camID, campus.camName, COUNT(students.id) as studentCount 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING COUNT(students.id) > 3 
ORDER BY studentCount; 
-- run it many many times, back to back, 5.50 seconds, 20 rows of output

SELECT students.camID, campus.camName, COUNT(students.id) as studentCount 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING studentCount > 3 
ORDER BY studentCount; 
-- run it many many times, back to back, 5.50 seconds, 20 rows of output

所以时代是一样的。每跑十遍。

两者的EXPLAIN输出相同

+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
| id | select_type | table    | type | possible_keys | key        | key_len | ref                  | rows   | Extra                           |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
|  1 | SIMPLE      | campus   | ALL  | PRIMARY       | NULL       | NULL    | NULL                 |     20 | Using temporary; Using filesort |
|  1 | SIMPLE      | students | ref  | ix_stu_cam    | ix_stu_cam | 5       | bigtest.campus.camID | 123766 | Using index                     |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+

使用AVG()函数,通过以下两个查询having(具有相同的EXPLAIN输出)中的别名,我的性能提高了约12%。

SELECT students.camID, campus.camName, avg(students.id) as studentAvg 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING avg(students.id) > 2200000 
ORDER BY students.camID; 
-- avg time 7.5

explain

SELECT students.camID, campus.camName, avg(students.id) as studentAvg 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID, campus.camName 
HAVING studentAvg > 2200000
ORDER BY students.camID;
-- avg time 6.5

最后是DISTINCT

SELECT students.camID, count(distinct students.id) as studentDistinct 
FROM students 
JOIN campus 
    ON campus.camID = students.camID 
GROUP BY students.camID 
HAVING count(distinct students.id) > 1000000 
ORDER BY students.camID; -- 10.6   10.84   12.1   11.49   10.1   9.97   10.27   11.53   9.84 9.98
-- 9.9

 SELECT students.camID, count(distinct students.id) as studentDistinct 
 FROM students 
 JOIN campus 
    ON campus.camID = students.camID 
 GROUP BY students.camID 
 HAVING studentDistinct > 1000000 
 ORDER BY students.camID; -- 6.81    6.55   6.75   6.31   7.11 6.36   6.55
-- 6.45

具有相同输出的别名持续运行
速度提高35%EXPLAIN。见下文。因此,相同的Explain输出已显示两次,但并不能得出相同的性能,但这只是一般的提示。

+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| id | select_type | table    | type  | possible_keys | key        | key_len | ref                  | rows   | Extra                                        |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
|  1 | SIMPLE      | campus   | index | PRIMARY       | PRIMARY    | 4       | NULL                 |     20 | Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | students | ref   | ix_stu_cam    | ix_stu_cam | 5       | bigtest.campus.camID | 123766 | Using index                                  |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+

目前,Optimizer似乎更喜欢别名,尤其是对于DISTINCT.



 类似资料:
  • 问题内容: 我正在执行以下查询,并为所有列使用别名。我用命名了别名。因为这是必需的。现在,我想直接在where子句中引用别名,我的操作如下所示: 但是,当我执行此操作时,出现以下错误: 我知道SQL首先执行where子句,因此这就是错误的原因。但是我该如何解决呢?有什么建议吗? 问题答案: 您已经知道不能在子句中使用别名,但这仅适用于相同级别的SQL。您可以将查询包装在外部查询中: 唯一的选择是重

  • 我为Select创建了动态DB2存储过程。我将使用这个泛型查询通过传递参数来执行多个select语句。 下面是我的查询的样子。 当我运行这个的时候

  • 主要内容:语法,实例HAVING 子句允许指定条件来过滤将出现在最终结果中的分组结果。 WHERE 子句在所选列上设置条件,而 HAVING 子句则在由 GROUP BY 子句创建的分组上设置条件。 语法 下面是 HAVING 子句在 SELECT 查询中的位置: 在一个查询中,HAVING 子句必须放在 GROUP BY 子句之后,必须放在 ORDER BY 子句之前。下面是包含 HAVING 子句的 SELECT

  • 主要内容:Oracle HAVING子句简介,Oracle HAVING子句的例子在本教程中将学习如何使用Oracle 子句过滤由子句返回分组。 Oracle HAVING子句简介 子句是SELECT语句的可选子句。它用于过滤由GROUP BY子句返回的行分组。 这就是为什么子句通常与子句一起使用的原因。 以下说明了Oracle子句的语法: 在这个语句中,子句紧跟在子句之后。如果使用不带子句的子句,则子句将像WHERE子句那样工作。 请注意,子句过滤分组的行,而子句过滤行。这是

  • HAVING 子句 在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与聚合函数一起使用。 HAVING 子句可以让我们筛选分组后的各组数据。 SQL HAVING 语法SELECT column_name, aggregate_function(column_name) FROM table_name WHERE column_name operator value GROUP

  • 问题内容: 我正在尝试搜索文本和备注的多列,以查找某些我不想看到的特定短语和黑名单短语。 假设下表 前任。我想找到所有提到(在任何领域中)“苹果”但黑名单中的“苹果酱”的故事。 如何在where子句中使用别名?我找不到有关此主题的任何文档: 1)这种方法可行吗? 2)替代方法是否意味着我将在每次行迭代中执行多个字符串连接? 问题答案: 我不能在where子句中使用别名。 这种方法可行吗? 当然,将