文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

在没有数据仓库或单独的分析数据库的组织中,报告的唯一来源和最新的数据可能是在现场生产数据库中。 在查询生产数据库时,优化是关键。一个低效的查询可能会对生产数据库产生大量的资源消耗,如果查询有错误会引发性能下降 或服务不可用。因此优化查询使得对数据库性能影响最小化是非常重要的。

第 1 段(可获 1.04 积分)

1. 首先分析业务需求

在前面的一篇文章中, 我们讨论了针对BI的业务分析最佳实践( best practices to define business requirements for BI)。这些做法可以应用在优化SQL查询中,如:

  • 识别利益相关方. 确保所有涉及方都参与了讨论开发查询语句。在查询生产数据库时,要确保DBA团队在场。
  • 专注于业务目标. 确保查询有一个明确的和唯一的目的。在生产数据库中实验或重复报告是一个不必要的风险。
  • 制定良好需求讨论框架。确定报告的功能和范围,确定其预期的受众。 这将有助于关注表上查询细节的正确性。
  • 通过有效的提问来沟通需求 这些问题通常遵循5 WS– Who? What? Where? When? Why?
  • 写非常具体的要求,并利益相关者确认 生产数据库的性能是非常重要的,所以不能有不清楚或不明确的需求。确保需求是尽可能的具体,确保在运行查询语句前需求是经过确认的。
第 2 段(可获 2.25 积分)

2. 定义select的字段,来代替select * 操作

当运行SELECT语句时,很多数据库开发人员会使用SELECT * (选择全部列)的缩写来选择表中所有需要的数据,但是,如果一张表中有很多字段,同时表数据量较大有许多条数据,由于查询了许多非必要数据,将会浪费数据库资源.

SELECT语句中定义查询字段,数据库将只查询符合业务需要的数据,让我们看看下面的例子:业务只需要客户的邮寄地址.

低效率:

SELECT *
FROM Customers
第 3 段(可获 1.11 积分)

这个查询将获得顾客表中除了邮寄地址以外的许多数据,比如电话号码,活跃日期(上次登录日期),销售备注和顾客服务说明.

高效率查询:

SELECT FirstName, LastName, Address, City, State, Zip
FROM Customers

这个查询只会选出邮件地址需要的信息.

为了维护所有表和字段名称的索引,对系统表中运行一条查询语句即可.系统表比如 INFORMATION_SCHEMA 或者 ALL_TAB_COLUMNS (对于 MS SQL Server数据库 – 请查阅以下:链接)

注:MYSQL中运行SELECT * FROM INFORMATION_SCHEMA.COLUMNS 即可查询出数据库中所有的表和视图以及其结构;

3. Select中列出更多的列去避免使用SELECT DISTINCT语句

SELECT DISTINCT 是一个查询中较为方便的移除重复列的方法,但是SELECT DISTINCT 底层采用了对所有指明唯一的列进行分组以实现得到不重复的数据结果,为了完成这一目标,需要非常大量的运算.此外,数据分组的结果可能并不精确.为了避免使用SELECT DISTINCT,使用选择更多的列的方式来创造每条数据唯一的结果集比较恰当.

第 4 段(可获 1.84 积分)

低效率和不准确的:

SELECT DISTINCT FirstName, LastName, State
FROM Customers

这个查询不能查询出在同一个州具有相同姓名的多个人。受欢迎的名字,如:David Smith 或者 Diane Johnson,将组合在一起,导致记录数不准确。 在更大的数据库,大量的类似 David Smith、Diane Johnson的记录会导致查询很慢。

高效和正确的:

SELECT FirstName, LastName, Address, City, State, Zip
FROM Customers

通过添加更多的字段,而不使用SELECT DISTINCT 来返回非重复记录。数据库不必对任何字段进行分组,返回的记录数也是正确的。 

第 5 段(可获 1.16 积分)

4. 使用INNER JOIN 而不是WHERE来创建连接

一些SQL开发人员更喜欢使用WHERE来做join,比如:

SELECT Customers.CustomerID, Customers.Name, Sales.LastSaleDate
FROM Customers, Sales
WHERE Customers.CustomerID = Sales.CustomerID

这个类型join实际上创建时笛卡尔连接,也被称为笛卡尔积或 CROSS JOIN。 在笛卡尔连接中,所有可能的组合都会被创建出来。在上面的例子中,如果有1000顾客和1000条销售记录,这个查询会先产生1000000个结果,然后通过正确的 CustomerID过滤出1000条记录。 这是一种低效利用数据库资源,数据库多做100倍的工作。 在大型数据库中,笛卡尔连接是一个大问题,对两个大表的笛卡尔积会创建数10亿或万亿的记录。

第 6 段(可获 1.59 积分)

为了避免创建笛卡尔积,应该使用INNER JOIN

SELECT Customers.CustomerID, Customers.Name, Sales.LastSaleDate
FROM Customers
   INNER JOIN Sales
   ON Customers.CustomerID = Sales.CustomerID

这样数据库就只产生等于CustomerID 的1000条目标结果。

有些数据库系统会识别出 WHERE连接并自动转换为 INNER JOIN。在这些数据库系统中,WHERE 连接与INNER JOIN 就没有性能差异。但是, INNER JOIN 是所有数据库都能识别的,因此DBA会建议在你的环境中使用它。

第 7 段(可获 1.03 积分)

5. 使用WHERE而不是使用 HAVING 来筛选

类似于上面提到的概念, 高效查询的目的就是只从数据库得到需要的记录。 每一个SQL 指令,HAVING 语句是放在WHERE 语句后面来运算的。 如果想基于条件来过滤记录,无疑WHERE 会更佳高效。

例如, 我们假定在2016年有200销售记录,我们想查询出2016年的客户销售数。

SELECT Customers.CustomerID, Customers.Name, Count(Sales.SalesID)
FROM Customers
   INNER JOIN Sales
   ON Customers.CustomerID = Sales.CustomerID
GROUP BY Customers.CustomerID, Customers.Name
HAVING Sales.LastSaleDate BETWEEN #1/1/2016# AND #12/31/2016#
第 8 段(可获 1.11 积分)

这个查询会从销售表中拉取1000条记录,然后过滤得到2016年的200条记录,最后才是对这个数据集计数。

相比而言, WHERE 就直接限制了拉取的数据记录数:

SELECT Customers.CustomerID, Customers.Name, Count(Sales.SalesID)
FROM Customers
  INNER JOIN Sales
  ON Customers.CustomerID = Sales.CustomerID
WHERE Sales.LastSaleDate BETWEEN #1/1/2016# AND #12/31/2016#
GROUP BY Customers.CustomerID, Customers.Name

本查询只拉取2016年的200条记录,然后对这个数据集计数。这样就不必使用 HAVING 。

第 9 段(可获 0.91 积分)

HAVING 只能应用在聚合字段上。在上面的查询中,我们可以添加一个显示消费记录大于5的客户的条件。

SELECT Customers.CustomerID, Customers.Name, Count(Sales.SalesID)
FROM Customers
   INNER JOIN Sales
   ON Customers.CustomerID = Sales.CustomerID
WHERE Sales.LastSaleDate BETWEEN #1/1/2016# AND #12/31/2016#
GROUP BY Customers.CustomerID, Customers.Name
HAVING Count(Sales.SalesID) > 5

6. 仅在模糊查询的尾部使用通配符

当搜索文本数据时,如 城市或姓名,通配符可以扩大搜索范围。但是,模糊搜索却是一个低效的搜索。

第 10 段(可获 0.81 积分)

在使用通配符的查询中,特别是匹配到结尾处的通配符,数据库会在选中的字段中匹配所有的记录。

考虑如下查询,查询城市名以 ‘Char’开头的:

SELECT City FROM Customers
WHERE City LIKE ‘%Char%’

这个查询会包含我们所期望的这些数据: Charleston, Charlotte 和Charlton。 但是,它也会返回这些不需要的数据:Cape Charles, Crab Orchard和Richardson.

更高效的查询是:

SELECT City FROM Customers
WHERE City LIKE ‘Char%’

这个查询就仅返回所期望的结果:Charleston, Charlotte 和Charlton。

第 11 段(可获 1.05 积分)

7. 使用LIMIT来限制查询结果集

在第一次运行查询前,通过使用 LIMIT来确保结果是可获取的和有意义的。 (某些数据库中,是使用TOP) LIMIT 语句会仅返回指定数量的数据。在编辑查或细化查询语句时使用 LIMIT可以防止对生成数据库产生大数据查询。

在上面的2016年销售查询中,可以如下语句来限制返回10条记录:

SELECT Customers.CustomerID, Customers.Name, Count(Sales.SalesID)
FROM Customers
  INNER JOIN Sales
  ON Customers.CustomerID = Sales.CustomerID
WHERE Sales.LastSaleDate BETWEEN #1/1/2016# AND #12/31/2016#
GROUP BY Customers.CustomerID, Customers.Name
LIMIT 10
第 12 段(可获 1.1 积分)

通过这样的简单查询,我们可以看到是否有可用的数据集。

8. 在非高峰时段运行分析统计查询

为了最小化查询对生成数据库的影响,应该告诉DBA在非高峰时段调度执行查询。查询应该在并发用户数最少的时段运行,比如:典型的时间段是半夜时段(凌晨3-5点)。

符合以下情况越多,就越应该选择在晚上运行查询:

  • 查询一个大表 (超过100万记录).
  • 笛卡尔连接 或 交叉连接。
  • 循环语句.
  • SELECT DISTINCT 语句.
  • 嵌套的子查询.
  • 在大段文本字段上模糊查询
  • 跨schema 查询.
第 13 段(可获 1.54 积分)

文章评论

广州访客
太过于片面。
就例如6. 仅在模糊查询的尾部使用通配符
你知道这样为什么会快吗,仅仅是因为查出来的东西少了?其实是因为这样写可以走索引
访客
4. 使用INNER JOIN 而不是WHERE来创建连接!!!!!这个where哪里是笛卡尔积???
访客
感谢分享。人家是翻译的,楼上的几位脾气太大了。
深圳访客
有些是需求上的不同吧。。本来只要id一般也没多少人会去select *。我模糊搜索假如是在中间的也要呢?
武汉访客
这类文章只是聊聊天,实际生产环境肯定是需求第一、稳定第一、时效第一,产品迭代和需求变更难免出现一些与效率背道而驰的东西。这篇文章应该有些年头了,无需那么认真,现在硬件提升和数据精细化,没几千万行的表真算不上大表。
郑州访客
对我非常好,谢谢!