t–sql pl–sql
Depending on the performance problem cause, fixing poor SQL query design can be quick or time consuming. It’s recommended to follow some general recommendations and best practices while writing queries, so you don’t have to rewrite and optimize them later.
根据性能问题的原因,修复不良SQL查询设计可能很快或很耗时。 建议您在编写查询时遵循一些常规建议和最佳做法,因此以后不必重写和优化它们。
影响查询性能的因素 (Factors that affect query performance)
Query performance also depends on data volume and transaction concurrency. Executing the same query on a table with millions of records requires more time that performing the same operation on the same table with only thousands of records.
查询性能还取决于数据量和事务并发性。 在具有数百万条记录的表上执行相同的查询所花的时间要比在仅具有数千条记录的同一表上执行相同操作所花的时间更多。
A lot of concurrent transactions can degrade SQL Server performance. The shorter the queue of transactions that wait to be processed, the better performance.
许多并发事务会降低SQL Server性能。 等待处理的事务队列越短,性能越好。
Executing a SQL query to retrieve records from multiple joined tables with small sets of data in a sandbox is quick, but running the same query in production with millions of rows in each joined table and multiple users accessing the same tables and data can add significant pressure. That’s why sometimes developers are not fully aware of query performance.
在沙箱中执行SQL查询以从具有多个数据集的多个联接表中检索记录很快,但是在生产中运行相同的查询时每个联接表中都有数百万行,并且多个用户访问相同的表和数据可能会增加很大的压力。 这就是为什么有时开发人员不完全了解查询性能的原因。
To be able to see how a SQL query performs in real production environment, it’s necessary to provide the same conditions as in the production environment. Otherwise, the potential underperforming can be masked.
为了了解SQL查询在实际生产环境中的执行情况,有必要提供与生产环境中相同的条件。 否则,可能会掩盖表现不佳的情况。
We’ll use STATISTICS TIME to show the number of milliseconds required to parse, compile, and execute a SELECT statement.
我们将使用STATISTICS TIME来显示解析,编译和执行SELECT语句所需的毫秒数。
We’ll execute a simple SELECT statement:
我们将执行一个简单的SELECT语句:
SET STATISTICS TIME ON
SELECT [AddressID] [int], [AddressLine1], [AddressLine2], [City]
FROM [Person].[Address]
SET STATISTICS TIME OFF
When executed on a million row table, it takes approximately 13 seconds:
在百万行表上执行时,大约需要13秒:
SQL Server Execution Times:
CPU time = 1031 ms, elapsed time = 13263 ms.
SQL Server执行时间:
CPU时间= 1031毫秒,经过的时间= 13263毫秒。
When executed on a two million row table, the time practically is doubled:
在两百万行的表上执行时,时间实际上增加了一倍:
(2000000 row(s) affected)
(受影响的2000000行)
SQL Server Execution Times:
CPU time = 2140 ms, elapsed time = 26961 ms.
SQL Server执行时间:
CPU时间= 2140毫秒,经过的时间= 26961毫秒。
STATISTICT TIME shows two execution times. The first one is the CPU time, it presents the number of milliseconds the processor has been processing the data. The second time named ‘elapsed time’ is the time needed to show the results in the grid. When working with a large set of records, showing the records in the grid lasts much longer than retrieving them from the table.
STATISTICT TIME显示两个执行时间。 第一个是CPU时间,它表示处理器已处理数据的毫秒数。 第二次称为“经过时间”是在网格中显示结果所需的时间。 当使用大量记录时,在网格中显示记录的时间要比从表中检索记录的时间长得多。
检索不必要的数据 (Don’t retrieve more data than necessary)
When retrieving data from SQL Server tables, don’t retrieve more than you need. Obtaining excessive data is resource expensive and time consuming.
从SQL Server表中检索数据时,不要检索超出您需要的内容。 获得过多的数据是资源昂贵和费时的。
We’ll use the Person.Address table in the AdventureWorks database:
我们将使用AdventureWorks数据库中的Person.Address表:
CREATE TABLE [Person].[Address](
[AddressID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[AddressLine1] [nvarchar](60) NOT NULL,
[AddressLine2] [nvarchar](60) NULL,
[City] [nvarchar](30) NOT NULL,
[StateProvinceID] [int] NOT NULL,
[PostalCode] [nvarchar](15) NOT NULL,
[SpatialLocation] [geography] NULL,
[rowguid] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[ModifiedDate] [datetime] NOT NULL,
CONSTRAINT [PK_Address_AddressID] PRIMARY KEY CLUSTERED
(
[AddressID] ASC
) ON [PRIMARY])
We’ll add random records to the table, so that it contains 1 million records:
我们将随机记录添加到表中,使其包含一百万条记录:
DECLARE @i int
SET @i = 0
WHILE @i < 1000000
BEGIN
INSERT INTO Person.Address( AddressLine1,
AddressLine2,
City,
StateProvinceID,
PostalCode,
rowguid,
ModifiedDate )
VALUES(CONVERT(nvarchar(60), NEWID()) ,
CONVERT(nvarchar(60), NEWID()) ,
rand () *5 ,
rand (),
rand () *7,
NEWID(),
DATEADD (day, (ABS(CHECKSUM(NEWID())) % 1625), GETDATE())
SET @i = @i + 1
END
If you need for example IDs of the addresses modified before 2015, retrieve only that information. Don’t retrieve their addresses, cities, postal codes, or dates they were modified.
例如,如果您需要在2015年之前修改的地址的ID,则仅检索该信息。 不要获取其地址,城市,邮政编码或修改日期。
We’ll start with the worst case – selecting all columns and all rows:
我们从最坏的情况开始-选择所有列和所有行:
SET STATISTICS TIME ON
SELECT *
FROM Person.Address
SET STATISTICS TIME OFF
The statistics shows:
统计信息显示:
(1000000 row(s) affected)
(1000000行受影响)
SQL Server Execution Times:
CPU time = 2219 ms, elapsed time = 20917 ms.
SQL Server执行时间:
CPU时间= 2219毫秒,经过时间= 20917毫秒。
Now, we’ll retrieve only two columns – the address ID and modified date where the year condition should be applied:
现在,我们将仅检索两列–应应用年份条件的地址ID和修改日期:
SET STATISTICS TIME ON
SELECT AddressID, ModifiedDate
FROM Person.Address
SET STATISTICS TIME OFF
The processor time is almost four times smaller:
处理器时间几乎缩短了四倍:
(1000000 row(s) affected)
(1000000行受影响)
SQL Server Execution Times:
CPU time = 578 ms, elapsed time = 10219 ms.
SQL Server执行时间:
CPU时间= 578毫秒,经过的时间= 10219毫秒。
Finally, we’ll retrieve only the records needed – IDs of the addresses modified before 2015:
最后,我们将仅检索所需的记录-2015年之前修改的地址的ID:
SET STATISTICS TIME ON
SELECT AddressID
FROM Person.Address
WHERE YEAR (ModifiedDate) < 2015
SET STATISTICS TIME OFF
(158956 row(s) affected)
(受影响的158956行)
SQL Server Execution Times:
CPU time = 515 ms, elapsed time = 3286 ms.
SQL Server执行时间:
CPU时间= 515毫秒,经过时间= 3286毫秒。
The number of the returned rows is approximately 6 times smaller, the number of the columns retrieved also smaller than in the previous example, yet the processor time is only 10% shorter. That’s due to the YEAR built-in function used on a non-index column.
返回的行数大约比以前的示例小6倍,检索到的列数也比以前的示例小,但是处理器时间仅短10%。 这是由于在非索引列上使用了YEAR内置函数。
If possible, avoid using functions (both built-in such as YEAR, LEFT, etc. and user-defined) in the WHERE clause:
如果可能,请避免在WHERE子句中使用函数(包括YEAR,LEFT等内置函数和用户定义函数):
SET STATISTICS TIME ON
SELECT AddressID
FROM person.Address1
WHERE ModifiedDate < '2015/01/01'
SET STATISTICS TIME OFF
(158956 row(s) affected)
(受影响的158956行)
SQL Server Execution Times:
CPU time = 298 ms, elapsed time = 2930 ms.
SQL Server执行时间:
CPU时间= 298毫秒,经过的时间= 2930毫秒。
As shown, the narrower the results set is, the less time is needed to retrieve it. More resources are available for other operations and there will be fewer chances for bottlenecks.
如图所示,结果集越窄,检索它所需的时间越少。 有更多资源可用于其他操作,并且出现瓶颈的机会也更少。
However, pay attention to the column you use in the function used in the predicate (as the parameter in the WHERE condition). In the example above, we used a non-index column. If the clustered index column is used, the time needed will be longer:
但是,请注意在谓词中使用的函数中使用的列(作为WHERE条件中的参数)。 在上面的示例中,我们使用了非索引列。 如果使用聚集索引列,则所需时间将更长:
SET STATISTICS TIME ON
SELECT AddressID
FROM Person.Address1
WHERE LEFT (AddressID,6) < 158956
SET STATISTICS TIME OFF
(158956 row(s) affected)
(受影响的158956行)
SQL Server Execution Times:
CPU time = 845 ms, elapsed time = 3788 ms.
SQL Server执行时间:
CPU时间= 845毫秒,经过的时间= 3788毫秒。
As shown, for the same number of records, the processor requires almost 300% more time
如图所示,对于相同数量的记录,处理器需要将近300%的时间
WHERE condition, 158,956 records retrieved | |||||
SELECT * | SELECT <column list> | ModifiedDate < ‘2015/01/01’ | YEAR (ModifiedDate) < ‘2015’ | LEFT (AddressID,2) < 12000 | |
Processor time in ms | 2,219 | 578 | 298 | 515 | 845 |
在哪里条件下,检索到158,956条记录 | |||||
选择 * | 选择 <列列表> | 修改日期 <'2015/01/01' | 年份(修改日期) <'2015' | 左(AddressID,2) <12000 | |
处理器 时间(毫秒) | 2,219 | 578 | 298 | 515 | 845 |
In this article, we’ve presented the factors that affect SQL query performance and gave some general guidelines for testing. We showed how much processor time is used for retrieving different amount of records. In the part 2 of this article, we’ll show how using indexed views instead of adding indexes to tables affects SQL performance.
在本文中,我们介绍了影响SQL查询性能的因素,并提供了一些常规测试准则。 我们显示了多少处理器时间用于检索不同数量的记录。 在本文的第2部分中,我们将展示使用索引视图而不是向表中添加索引如何影响SQL性能。
翻译自: https://www.sqlshack.com/poor-sql-query-design-sql-query-performance-killer-basics/
t–sql pl–sql