Tải bản đầy đủ (.pdf) (40 trang)

Tài liệu SQL Server MVP Deep Dives- P12 pptx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (805.5 KB, 40 trang )

396
C
HAPTER
29
My favorite DMVs, and why
SELECT
'These procedures have not been executed in the past '
+ RTRIM(uptime) + ' minutes (the last time SQL started)',
sqlserver_start_time
FROM
AB_Utility.dbo.AB_Uptime();
SELECT
[name] = AB_Utility.dbo.AB_GetThreePartName
(p.[object_id], DB_ID()),
p.create_date,
p.modify_date
FROM
sys.procedures AS p
LEFT OUTER JOIN
sys.dm_exec_procedure_stats AS ps
ON
p.[object_id] = ps.[object_id]
WHERE
ps.[object_id] IS NULL
ORDER BY
p.[Name];
END
GO
EXEC dbo.sp_MS_marksystemobject N'dbo.sp_AB_GetUnusedProcedures';
GO
USE [your_database];


GO
EXEC dbo.sp_AB_GetUnusedProcedures;
GO
WARNING
Although creating objects in the master database has been a relatively
safe and well-known method for years, please proceed with the under-
standing that you may need to change it later. It is not documented,
not supported, and likely to cease working in some future version of
SQL
Server. The main problem with using undocumented methods is
that Microsoft does not need to warn you before they change or
remove the functionality; therefore, your next upgrade might end up
being a lot more work than you thought.
Finding inefficient and unused indexes
The following query will help you identify indexes in your database that are not used
at all, or are used more during maintenance operations than for improving query per-
formance. As with the query to find unused procedures, what you do with this infor-
mation will rely heavily on how long
SQL
Server has been up and running. If you
restarted
SQL
Server this morning, then these statistics may not yet represent an ade-
quate sample of your workload. And as with the unused procedure code, you will need
to create this object in each relevant database, because it returns metadata from the
local catalog view
sys.indexes
. (If you want to use the system object technique, the
changes to the code are similarly simple.) This code is shown in listing 7.
Licensed to Kerri Ross <>

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
397
Some interesting applications of my favorite DMVs

USE [your_database];
GO
IF OBJECT_ID('dbo.AB_MeasureIndexUsefulness', N'P') IS NOT NULL
DROP PROCEDURE dbo.AB_MeasureIndexUsefulness;
GO
CREATE PROCEDURE dbo.AB_MeasureIndexUsefulness
AS
BEGIN
SET NOCOUNT ON;
SELECT 'These indexes have collected statistics for the past '
+ RTRIM(uptime) + ' minutes (the last time SQL started)',
sqlserver_start_time
FROM
AB_Utility.dbo.AB_Uptime();

WITH calced AS
(
SELECT
[object_id],
index_id,
reads = user_seeks + user_scans + user_lookups,
writes = user_updates,
perc = CONVERT(DECIMAL(10,2), user_updates * 100.0 /
(user_seeks + user_scans + user_lookups + user_updates))
FROM
sys.dm_db_index_usage_stats

WHERE
database_id = DB_ID()
)
SELECT
[status] = CASE
WHEN reads = 0 AND writes = 0 THEN
'Consider dropping : not used at all'
WHEN reads = 0 AND writes > 0 THEN
'Consider dropping : only writes'
WHEN writes > reads THEN
'Consider dropping : more writes (' +
RTRIM(perc) + '% of activity)'
WHEN reads = writes THEN
'Reads and writes equal'
END,
[table] = AB_Utility.dbo.AB_GetTwoPartName(
c.[object_id], DB_ID()),
[index] = i.Name,
c.reads,
c.writes
FROM
calced AS c
INNER JOIN
sys.indexes AS i
ON
c.[object_id] = i.[object_id]
Listing 7 Measuring the usefulness of indexes
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
398

C
HAPTER
29
My favorite DMVs, and why
AND c.index_id = i.index_id
WHERE
c.writes >= c.reads;
END
GO
Note that because the read and write metrics are per operation, not per row, a
DML
operation that affects 100 rows will only count as one
user update
in this view.
Finding inefficient queries
The table-valued function in listing 8 will return the top n queries, ordered in
descending order by longest average CPU time, longest average elapsed time, highest
average reads, highest logical reads, highest writes, or highest number of executions.
Because this one query does not rely on database-specific catalog views, it can be cre-
ated in the utility database and called from anywhere (passing database name, num-
ber of rows, and ordering preference). You can also add a
WHERE
clause to restrict the
result

set
to objects matching a certain naming pattern or queries that executed at
least n times.
USE AB_Utility;
GO

IF OBJECT_ID(N'dbo.AB_GetInefficientQueries', N'IF') IS NOT NULL
DROP FUNCTION dbo.AB_GetInefficientQueries;
GO
CREATE FUNCTION dbo.AB_GetInefficientQueries
(
@database_name SYSNAME,
@number_of_rows INT,
@order_by VARCHAR(15)
)
RETURNS TABLE
AS
RETURN
(
SELECT TOP (@number_of_rows) * FROM
(
SELECT
exec_object = AB_Utility.dbo.AB_GetTwoPartName(
est.objectid, est.[dbid]),
exec_statement = AB_Utility.dbo.AB_ParseSQLText(est.[text],
qs.statement_start_offset, qs.statement_end_offset ),
u.sqlserver_start_time,
uptime_minutes = u.uptime,
execution_count,
first_execution_time = qs.creation_time,
qs.last_execution_time,
avg_cpu_time_milliseconds
= qs.total_worker_time / (1000 * qs.execution_count),
avg_logical_reads
= qs.total_logical_reads / qs.execution_count,
Listing 8 Finding inefficient queries

Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
399
Some interesting applications of my favorite DMVs
avg_physical_reads
= qs.total_physical_reads / qs.execution_count,
avg_writes
= qs.total_logical_writes / qs.execution_count,
avg_elapsed_time_milliseconds
= qs.total_elapsed_time / (1000 * qs.execution_count)
FROM
sys.dm_exec_query_stats AS qs
CROSS APPLY
sys.dm_exec_sql_text(qs.[sql_handle]) AS est
CROSS JOIN
AB_Utility.dbo.AB_Uptime() AS u
WHERE
est.[dbid] = DB_ID(@database_name)
) x
ORDER BY CASE @order_by
WHEN 'cpu time' THEN avg_cpu_time_milliseconds
WHEN 'logical reads' THEN avg_logical_reads
WHEN 'physical reads' THEN avg_physical_reads
WHEN 'writes' THEN avg_writes
WHEN 'elapsed time' THEN avg_elapsed_time_milliseconds
WHEN 'executions' THEN execution_count
END DESC,
exec_object
);
GO

USE [tempdb];
GO
SELECT *
FROM AB_Utility.dbo.AB_GetInefficientQueries
(
'msdb',
50,
'cpu time'
)
-- WHERE exec_object NOT LIKE '%sp_get_composite_job_info%'
-- WHERE execution_count >= 50;
Finding missing indexes
Starting with
SQL
Server 2005, the database engine started keeping track of indexes
that the optimizer would have taken advantage of, if they existed. The missing index
DMV
s should be used only as a guide, and not as the final authority on how you should
change your index structures. (As with other
DMV
s, the data does not persist between
restarts. Also, be careful about relying on data for tables with indexes that have
changed recently, as this can also clear out missing index information.) The function
in listing 9 will return a slightly more useful output structure to help you determine
which tables and indexes you should further investigate for fine tuning. This includes
information about how long
SQL
Server has been up, when the last user seek or scan
was for that specific query (because it may represent an ad hoc query outside of your
normal workload), and the

CREATE

INDEX

DDL
if you wanted to follow through with the
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
400
C
HAPTER
29
My favorite DMVs, and why
suggestion. To use the function, you pass in the database name and the number of
rows you want to return.
USE AB_Utility;
GO
IF OBJECT_ID(N'dbo.AB_GetMissingIndexes', N'IF') IS NOT NULL
DROP FUNCTION dbo.AB_GetMissingIndexes
GO
CREATE FUNCTION dbo.AB_GetMissingIndexes
(
@database_name SYSNAME,
@number_of_rows INT
)
RETURNS TABLE
AS
RETURN
(
SELECT TOP (@number_of_rows)

*,
-- must give credit to Tibor Karazsi here:
[statement] = 'CREATE INDEX [<<index name>>]'
+ ' ON ' + [table] + ' ('
+ COALESCE(eq + COALESCE(', ' + iq, ''), iq)
+ ')' + COALESCE(' INCLUDE(' + ic + ');', ';')
FROM
(
SELECT
[table] = AB_Utility.dbo.AB_GetTwoPartName(
d.[object_id], d.database_id),
eq = d.equality_columns,
iq = d.inequality_columns,
ic = d.included_columns,
relative_benefit = (s.user_seeks + s.user_scans)
* (s.avg_total_user_cost * s.avg_user_impact),
s.user_seeks,
s.user_scans,
s.last_user_seek,
s.last_user_scan
FROM
sys.dm_db_missing_index_details AS d
INNER JOIN
sys.dm_db_missing_index_groups AS g
ON d.index_handle = g.index_handle
INNER JOIN
sys.dm_db_missing_index_group_stats AS s
ON g.index_group_handle = s.group_handle
WHERE
d.database_id = DB_ID(@database_name)

) x
CROSS JOIN AB_Utility.dbo.AB_Uptime()
ORDER BY relative_benefit DESC
);
Listing 9 Finding missing indexes
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
401
DMV categories in SQL Server
GO
SELECT *
FROM AB_Utility.dbo.AB_GetMissingIndexes
(
'Org00010001',
50
);
DMV categories in SQL Server
Table 1 lists the
DMV
categories in both
SQL
Server 2005 and
SQL
Server 2008. Table 2
lists the new
DMV
categories in
SQL
Server 2008.


Table 1 DMV categories in SQL Server 2005 and 2008
DMV category DMVs URL for more information
Common Language
Runtime (CLR)
sys.dm_clr_* />library/ms179982.aspx
Database sys.dm_db_* />library/ms181626.aspx
Database mirroring sys.dm_db_mirroring_* />library/ms173571.aspx
Execution sys.dm_exec_* />library/ms188068.aspx
Full-text search sys.dm_fts_* />library/ms174971.aspx
Indexes sys.dm_db[_missing|_index_* />library/ms187974.aspx
Input/output (I/O) sys.dm_io_* />library/ms190314.aspx
Query notifications sys.dm_qn_* />library/ms187407.aspx
Replication sys.dm_repl_* />library/ms176053.aspx
Service broker sys.dm_broker_* />library/ms176110.aspx
SQL Server operating
system
sys.dm_os_* />library/ms176083.aspx
Transactions sys.dm_tran_* />library/ms178621.aspx
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
402
C
HAPTER
29
My favorite DMVs, and why
Summary
Gone are the days of running
DBCC
commands and system stored procedures over
and over again, and keeping links to Profiler and Performance Monitor on every

machine’s desktop, when trying to peek into the usage characteristics of our
SQL
Server instances. I hope I have provided a glimpse of how much power we have been
given through
DMV
s and
DMF
s, and that I have inspired you to use them more often
when observing usage or troubleshooting performance issues.
About the author
Aaron Bertrand is the Senior Data Architect at One to One
Interactive, a global marketing agency headquartered in Boston,
Massachusetts. At One to One, Aaron is responsible for database
design and application architecture. Due to his commitment to
the community, shown through blogging at http:
//www.sql-
blog.com, peer-to-peer support on forums and newsgroups, and
speaking at user group meetings and code camps, he has been
awarded as a Microsoft
MVP
since 1998. Aaron recently pub-
lished a technical white paper for Microsoft, detailing how to use
the new Resource Governor feature in
SQL
Server 2008.

Table 2 New DMV categories in SQL Server 2008
DMV URL
Change data capture sys.dm_cdc_* />en-us/library/bb522478.aspx
Extended events sys.dm_xe_* />en-us/library/bb677293.aspx

Object (dependency) sys.dm_sql_* />en-us/library/bb630390.aspx
Resource governor sys.dm_resource_governor_* />en-us/library/bb934218.aspx
Security sys.dm_[audit|cryptographic|etc]_* />en-us/library/bb677257.aspx
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
403
30 Reusing space in a table
Joe Webb
People who have switched to Microsoft
SQL
Server from Microsoft Access sometimes
ask, “If I delete a bunch of rows, do I need to compact my
SQL
Server database?”
It’s been many years since I’ve used Access, but I still remember the reason for
their concern. Access would continually add rows to the end of the table. If some,
or even all, of the rows were deleted from the table, Access wouldn’t reuse the
space. It kept adding rows to the end of the table and never backfilled the holes.
Compacting the Access database file would get rid of the holes.
Understanding how SQL Server
automatically reuses table space
I’m not an expert in Access, and I’m certainly not knocking it. I haven’t even used
Access since v2.0 back in the 1990s. This behavior may have changed since then, or
perhaps I misremember, but suffice it to say that with
SQL
Server this is not an
issue. But don’t take my word for it. Let’s consider an example to prove the point.
To set up the example, let’s create a table with three columns and then populate
it with test data. The
T-SQL

code for doing this is shown in listing 1.
USE tempdb ;
GO
--create a test table
CREATE TABLE dbo.Test
(
col1 INT
,col2 CHAR(25)
,col3 VARCHAR(4000)
) ;
--create some test data
DECLARE @cnt INT ;
SET @cnt = 0 ;
Listing 1 Creating and populating the dbo.Test table
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
404
C
HAPTER
30
Reusing space in a table
WHILE @cnt < 1000
BEGIN
SELECT
@cnt = @cnt + 1 ;
INSERT
dbo.Test ( col1,col2,col3 )
VALUES (
@cnt
,'test row # ' + CAST(@cnt AS VARCHAR(10)) + 'A'

,REPLICATE('ABCD', ROUND(RAND() * @cnt, 0))
) ;
END
The
CREATE

TABLE
statement creates a table called Test as part of the dbo schema in
the tempdb database. The table is composed of three columns. The first is an integer,
the second is a fixed length character string that contains 25 characters, and the
third and final column is a variable length character string that can contain up to
1900 characters.
To add test data, we will use a simple
INSERT
statement. The
INSERT
statement has
been placed inside a
WHILE
loop and is executed 1,000 times. Note that the last col-
umn in the table will vary in length from zero to a maximum of 4,000. Statistically, it
should average around 1,000 characters.
Let’s view the contents of the table to make sure we have what we think we have.
We can do this by running a
SELECT
statement to retrieve the data, as shown in
listing 2. The results of the query are shown in figure 1.
--view the table
SELECT
*

FROM
dbo.Test ;
To examine the amount of space the table consumes, we’ll use a Dynamic Manage-
ment View (
DMV
) called
sys.dm_db_index_physical_stats
.
DMV
s were first intro-
duced in
SQL
Server 2005 and have been continued in
SQL
Server 2008. (For more on
DMV
s, see chapter 29, “My favorite
DMV
s, and why,” by Aaron Bertrand.)
NOTE
The scripts that make use of
DMV
s will not work in
SQL
Server 2000.
The query in listing 3 returns a single row of data with four columns—the allocation
unit type, the page count, the average page space used as a percentage, and the total
Listing 2 Querying the dbo.Test table
Figure 1 Results from dbo.Test table
Licensed to Kerri Ross <>

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
405
Understanding how SQL Server automatically reuses table space
number of rows in the dbo.Test table. The alloc_unit_type_desc column describes the
allocation unit type: valid values are
IN_ROW_DATA
,
LOB_DATA
, or
ROW_OVERFLOW_DATA
(see Books Online for a detailed explanation of these terms). The page_count col-
umn indicates the total number of data pages used by the table for
IN_ROW_DATA
(that
is, the allocation unit that stores the table rows in our example). The avg_page_
space_used_in_percent column reports the average percentage of space used in all
data pages in the
IN_ROW_DATA
allocation type. The record_count intuitively contains
the number of rows contained in the table.
--check the size of the table
SELECT
alloc_unit_type_desc
,page_count
,avg_page_space_used_in_percent
,record_count
FROM
sys.dm_db_index_physical_stats(
DB_ID()
,OBJECT_ID(N'dbo.Test')

,NULL
,NULL
,'Detailed') ;
Figure 2 displays the results of the
DMV
query. As you can see on my test system, 158
data pages are used to store the 1,000 rows and each data page is, on average, 82.1 per-
cent full.
To continue with the example, let’s delete half of the rows in our dbo.Test table and
see what happens to the space used by the table. The
T-SQL
script in listing 4 uses the
modulo operator, represented by the percent sign in
T-SQL
, to delete each row where
the value in the first column, col1, is an odd number. So we are deleting every other
row in the table.
--delete the odd rows
DELETE FROM
Test
WHERE
col1 % 2 = 1
--view the table
SELECT
*
Listing 3 Examining the space used by the dbo.Test table
Listing 4 Deleting the odd-numbered rows
Figure 2 Using a DMV to
review space used
Licensed to Kerri Ross <>

Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
406
C
HAPTER
30
Reusing space in a table
FROM
dbo.Test ;
Figure 3 shows the results of the
SELECT
query. You can see that the table is left with
test

row

#2
,
test

row

#4
, and so on, for a total of 500 rows.
Now that half of the rows in the table have been deleted, let’s look at the space the
table consumes by running the
DMV
query from listing 3 again.
Figure 4 shows that the number of pages used to store the table’s data remains con-
stant but the percentage used of each page changes. It is cut in half, from 82.1 percent
to 42.1 percent. Also notice that the number of rows reported in the record_count

column has been cut in half, as expected.
So let’s add some new rows to the table to prove that
SQL
Server will automatically
reuse the newly freed space, thus filling in the holes, to go back to our Access compari-
son. Using the
T-SQL
script found in listing 5, we can quickly add 500 rows of data to
the dbo.Test table. This script is similar to the one first used to populate the table with
sample data. It inserts one row at a time in a
WHILE
loop. To help differentiate the new
rows from the existing rows, I change the insert statement so that the third column is
filled with
WXYZ
’s rather than
ABCD
’s as before, though it doesn’t matter for our proof.
--add some more test data
DECLARE @cnt INT ;
SET @cnt = 0 ;
WHILE @cnt < 500
BEGIN
SELECT
@cnt = @cnt + 1 ;
INSERT
dbo.Test ( col1,col2,col3 )
VALUES (
@cnt
,'test row # ' + CAST(@cnt AS VARCHAR(10))

Listing 5 Adding new rows to the dbo.Test table
Figure 3 Deleting half the rows
in the dbo.Test table
Figure 4 Examining the space
consumed by the dbo.Test table
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
407
Recognizing when SQL Server does not reclaim space
,REPLICATE('WXYZ', ROUND(RAND() * @cnt, 0))
) ;
END
Now let’s once again use the
sys.dm_db_index_physical_stats

DMV
to see the space
used by the table. Figure 5 displays the result of the query.
Notice that the table is still using 158 data pages, but the average percent used for
each page has increased to 62.9 percent from the prior value of 42.1 percent. The
results confirm our expectations—the holes were indeed reused by
SQL
Server. No
additional pages were allocated; the free space available was used.
At this point it is important to understand that, although
SQL
Server can and will
automatically reuse space that was previously used by one or more rows, it will do so
only under very specific circumstances. This behavior is most readily demonstrated
through the use of a heap (a table without a clustered index) in which there is no pre-

defined order of the rows. The behavior can also be observed in clustered tables when
the newly inserted rows have key values that allow them to be inserted into the holes
left by prior deletions. You shouldn’t expect all newly inserted rows to fit nicely in the
holes of a clustered table. The point is that
SQL
Server will reuse space as appropriate.
To clean up after this example, let’s execute one final statement to drop the
dbo.Test table. Listing 6 displays the
DROP
statement.
--clean up
DROP TABLE dbo.Test ;
Recognizing when SQL Server does not reclaim space
Under certain circumstances
SQL
Server does not automatically reclaim space that is
no longer being used. If a table definition is altered to drop one or more variable
length columns, the space consumed by those columns is not immediately made avail-
able for reuse by
SQL
Server.
To illustrate this behavior, let’s consider an example. Let’s create another test table
using the script in listing 7. The script creates the table and populates it with 1,000
rows of data.
USE tempdb ;
GO
--create the dbo.Test2 table
Listing 6 Dropping the dbo.Test table
Listing 7 Creating the dbo.Test2 table
Figure 5 Reviewing the space used

by the dbo.Test table after inserting
new rows
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
408
C
HAPTER
30
Reusing space in a table
CREATE TABLE dbo.Test2
(
col1 INT
,col2 CHAR(25)
,col3 VARCHAR(4000)
) ;
--create some test data
DECLARE @cnt INT ;
SET @cnt = 0 ;
WHILE @cnt < 1000
BEGIN
SELECT
@cnt = @cnt + 1 ;
INSERT
dbo.Test2 ( col1,col2,col3)
VALUES (
@cnt
,'test row # ' + CAST(@cnt AS VARCHAR(10))
,REPLICATE('A', 4000)
) ;
END

Figure 6 shows the results from the
SELECT
statement. This table has three columns of
data, an integer in the first column, a character string in the second column that can
contain up to 25 characters, and a variable length character string in the final column
that contains 4,000 characters.
Using the query shown in listing 3, we can see how much space our newly created
dbo.Test2 table is consuming. Figure 7 shows the results. The newly created table
takes up 500 data pages to store the 1,000 rows of data. Each data page is, on average,
99.9 percent full.
Now to set up our test scenario, let’s drop the third column, the one that consumes
the most space. Listing 8 contains the
ALTER

TABLE
script to drop col3; it then executes
the
DMV
query to reveal the space used by the table.
Figure 6 Viewing data in the
dbo.Test2 table
Figure 7 Space used by
the dbo.Test2 table
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
409
Recognizing when SQL Server does not reclaim space

--drop the last column
ALTER TABLE dbo.Test2 DROP COLUMN col3 ;

--check the space used again
SELECT
alloc_unit_type_desc
,page_count
,avg_page_space_used_in_percent
,record_count
FROM
sys.dm_db_index_physical_stats(
DB_ID()
,OBJECT_ID(N'dbo.Test2')
,NULL
,NULL
,'Detailed') ;
Looking at the results in figure 8, we can see that we get the same results as
before—500 data pages, storing 1,000 rows, each 99.9 percent full, and this after drop-
ping the column that consumed the most space.
Why is this? When a table is altered to drop a column,
SQL
Server does not remove the
column data from the data pages. Instead it updates the metadata in the system tables
so that when queried, it appears as if the column no longer exists. The data is still
present in the data pages, but it’s not returned as a part of a result set. Thus, the space
cannot be reused initially.
So let’s add some additional rows to the table and see what happens. The script
shown in listing 9 inserts 500 additional rows into the dbo.Test2 table. Notice that this
script inserts only two columns of data because we’ve dropped the third column, col3.
--insert additional rows
DECLARE @cnt INT ;
SET @cnt = 0 ;
WHILE @cnt < 500

BEGIN
SELECT
@cnt = @cnt + 1 ;
INSERT
dbo.Test2 ( col1,col2 )
VALUES (
@cnt
,'test row # ' + CAST(@cnt AS VARCHAR(10))
) ;
END
Listing 8 Dropping a
varchar
column in the dbo.Test table
Listing 9 Adding more rows to the dbo.Test2 table
Figure 8 Reviewing the space used
after dropping a column
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
410
C
HAPTER
30
Reusing space in a table
When we use the
sys.dm_db_index_physical_stats
DMV
to examine the space used
by the dbo.Test2 table, we find that the number of data pages increases slightly, indi-
cating that the space once consumed by the dropped column was not automatically
reused. Figure 9 shows the results.

If the space had been automatically reused, there should have been more than
enough space to contain the additional 500 rows without having to add more space.
But because the columns were only marked as no longer being part of the table in the
metadata, there is no space to reuse. So
SQL
Server added additional pages to make
space for the new rows.
Using DBCC CLEANTABLE
to reclaim unused table space
Although it is comforting to know that in certain cases
SQL
Server will automatically
reuse space once consumed by deleted rows, we’ve just seen that it will not automati-
cally reuse space once consumed by dropped columns.
Fortunately, this space is not lost forever; we can reclaim this space by issuing a
DBCC
command. The
DBCC

CLEANTABLE
command allows us to specify a database and
table, and it will free up any space once consumed by dropped variable length charac-
ter columns.
To reclaim the space in our dbo.Test2 table, run the
T-SQL
command found in
listing 10.
--reclaim the space from the table
DBCC CLEANTABLE('tempdb', 'dbo.Test2') ;
If it succeeds, you should receive a message similar to the following as an output mes-

sage in the query window.
DBCC execution completed. If DBCC printed error messages, contact your system
administrator.
DBCC

CLEANTABLE
reclaims space from variable length columns that no longer exist as
part of the table definition. In this context a variable length column can be one of the
following data types:
varchar
,
nvarchar
,
varchar(max)
,
nvarchar(max)
,
varbinary
,
varbinary(max)
,
text
,
ntext
,
image
,
sql_variant
, and
xml

.
Now that we’ve used the
DBCC
command to reclaim the space, let’s return to our
DMV
query and examine the space used by the table by running listing 3.
Listing 10 Reclaiming space using
DBCC

CLEANTABLE
Figure 9 Inserting rows after
dropping a column
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.
411
Summary
Figure 10 shows the results. Notice that the number of data pages consumed by the
table did not decrease; however, the average space used as a percentage decreased
dramatically to 1.4 percent from 99.7 percent.
To prove that the space is truly available for reuse, let’s add another 3,000 rows to the
table by altering and running listing 9.
Now checking the used space by again running the
DMV
query in listing 3, we see
that the number of pages used to make the table did not increase. Notice, though,
that the average page space used as a percentage did increase to 4.2 percent from 1.4
percent. Figure 11 shows the results.
Summary
When rows are deleted from a table without a clustered index (an object known as a
heap),

SQL
Server can readily reuse the space that was once consumed by deleted
rows. The same may hold true for clustered tables, as long as the newly inserted rows
have the required key values that would allow them be placed in a hole created by a
deleted row.
There are certain circumstances, in which deleted space is not immediately released
for reuse; in particular when variable length character columns are dropped from a
table. By employing the
DBCC

CLEANTABLE
command we can reclaim the space once
consumed by the dropped columns and make better use of the disk resources at hand.
About the author
Joe Webb, a Microsoft
SQL
Server
MVP
, serves as Chief Operat-
ing Manager for WebbTech Solutions, a Nashville-based
IT
con-
sulting company. He has over 15 years of industry experience
and has consulted extensively with companies in the areas of
business process analysis and improvements, database design
and architecture, software development, and technical training.
In addition to helping his consulting clients, Joe enjoys writ-
ing and speaking at technical conferences. He has delivered
Figure 10 The space consumed
by dbo.Test2 after running

DBCC

CLEANTABLE
Figure 11 Inserting rows into
reclaimed space
Licensed to Kerri Ross <>
Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark.

×