SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database)

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 原文:SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database)一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 实现代码(SQL Codes)方法一:游...
原文: SQL Server 游标运用:查看所有数据库所有表大小信息(Sizes of All Tables in All Database)

一.本文所涉及的内容(Contents)

  1. 本文所涉及的内容(Contents)
  2. 背景(Contexts)
  3. 实现代码(SQL Codes)
    1. 方法一:游标 + 系统存储过程sp_MSForEachDB
    2. 方法二:封装sp_MSforeachtable + sys.databases
    3. 方法三:系统存储过程sp_MSForEachDB + sp_MSforeachtable
    4. 方法四:扩展sp_MSforeachdb + sp_MSforeachtable
  4. 参考文献(References)

二.背景(Contexts)

  之前写了篇关于:SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database)的文章,它罗列出某个数据所有表的信息,这些信息包括:表的记录数、数据记录占用空间、索引占用空间、没使用的空间等(如Figure1所示),现在我来讲述如何获取整个数据库实例中所有数据库所有表的信息(如Figure2所示)。

clip_image001

(Figure1:某数据库所有表信息)

clip_image003

(Figure2:所有数据库所有表信息)

三.实现代码(SQL Codes)

下面内容讲述了在实现Figure2过程中遇到的一些问题,如果你对这些问题不感兴趣可以直接看最后实现的SQL脚本。下面讲述了4种实现方法:

  1. 游标 + 系统存储过程sp_MSForEachDB,实现脚本为Script3;

  2. 封装sp_MSforeachtable + sys.databases,实现脚本为Script4和Script5;

  3. 系统存储过程sp_MSForEachDB + sp_MSforeachtable,实现脚本为Script6;

  4. 扩展sp_MSforeachdb + sp_MSforeachtable,实现脚本为Script7;

 

(一) 我们在SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database)的SQL脚本中进行改进,结合sp_MSForEachDB系统存储过程进行实现:

  1) 既然有了获取某个数据库所有表信息的脚本,那就可以在外层再套使用sp_MSForEachDB系统存储过程,下面的Script1脚本可以获取到所有数据库的所有表的信息,效果如Figure3所示:

--Script1:
--查看所有数据库所有表信息
EXEC sp_MSForEachDB 'USE [?];

DECLARE @tablespaceinfo TABLE (
    nameinfo VARCHAR(50),
    rowsinfo INT,
    reserved VARCHAR(20),
    datainfo VARCHAR(20),
    index_size VARCHAR(20),
    unused VARCHAR(20)
)

DECLARE @tablename VARCHAR(255);

DECLARE Info_cursor CURSOR FOR
    SELECT ''[''+[name]+'']'' FROM sys.tables WHERE TYPE=''U'';

OPEN Info_cursor
FETCH NEXT FROM Info_cursor INTO @tablename

WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO @tablespaceinfo EXEC sp_spaceused @tablename
    FETCH NEXT FROM Info_cursor
    INTO @tablename
END

CLOSE Info_cursor
DEALLOCATE Info_cursor

SELECT * FROM @tablespaceinfo
    ORDER BY Cast(Replace(reserved,''KB'','''') AS INT) DESC'

clip_image004

(Figure3:所有数据库所有表)

  2) 上图Figure3有两个缺点,第一是返回的数据太分散,没有统一表进行管理,第二是需要过滤master、model、msdb和tempdb等系统数据库,因为我们完全不关心系统数据库,下面的SQL脚本展示在使用sp_msforeachdb的时候如何排除某个数据库,效果如Figure4所示:

--sp_msforeachdb排除某个数据库
EXEC sp_msforeachdb 'IF ''?'' <> ''tempdb'' print ''?'''

clip_image005

(Figure4:sp_msforeachdb排除某个数据库)

  3) 下面的SQL脚本展示在使用sp_msforeachdb的时候如何排除多个数据库,效果如Figure5所示:

--sp_msforeachdb排除多个数据库
EXEC sp_msforeachdb 'IF ''?'' not in(''tempdb'',''master'',''model'',''msdb'') print ''?'''

clip_image006

(Figure5:sp_msforeachdb排除多个数据库)

  4) 把上面的SQL脚本运用到之前获取某个数据库表信息的SQL脚本中,但是执行的过程中出现了Figure6的错误信息:

--Script2:
--查看所有数据库所有表信息
IF NOT EXISTS (SELECT * FROM [tempdb].sys.objects WHERE object_id = OBJECT_ID(N'[tempdb].[dbo].[tablespaceinfo]') AND type in (N'U'))
BEGIN
CREATE TABLE [tempdb].[dbo].[tablespaceinfo](
    [nameinfo] [varchar](255) NULL,
    [rowsinfo] [int] NULL,
    [reserved] [varchar](20) NULL,
    [datainfo] [varchar](20) NULL,
    [index_size] [varchar](20) NULL,
    [unused] [varchar](20) NULL
) ON [PRIMARY]
END
ELSE
    TRUNCATE TABLE tempdb.dbo.tablespaceinfo
EXEC sp_MSForEachDB 'USE [?];

--IF ''?'' not in(''tempdb'',''master'',''model'',''msdb'') 
IF ''?'' in(''AdventureWorksLT2008R2'') 
BEGIN
print ''?''

DECLARE @tablename VARCHAR(255);

DECLARE Info_cursor CURSOR FOR
    SELECT ''[''+[name]+'']'' FROM ?.sys.tables WHERE TYPE=''U'';

OPEN Info_cursor
FETCH NEXT FROM Info_cursor INTO @tablename

WHILE @@FETCH_STATUS = 0
BEGIN
    INSERT INTO tempdb.dbo.tablespaceinfo EXEC ?.dbo.sp_spaceused @tablename
    FETCH NEXT FROM Info_cursor
    INTO @tablename
END

CLOSE Info_cursor
DEALLOCATE Info_cursor
END
'

--返回表
SELECT * FROM tempdb.dbo.tablespaceinfo
    --ORDER BY Cast(Replace(reserved,'KB','') AS INT) DESC
    ORDER BY nameinfo

clip_image008

(Figure6:错误信息)

  5) 经过一番查找,最后发现是因为AdventureWorksLT2008R2数据库的安全中的架构是SalesLT,不是默认的dbo,所以报了Figure6的错误信息,但是如果使用sp_MSforeachtable,那就不用理会框架的问题。

clip_image009

(Figure7:SalesLT架构名)

只要我们在表名称前面加入正确的架构名,那就可以正确执行了,如Figure8所示:

--使用正确的架构名
AdventureWorksLT2008R2.dbo.sp_spaceused 'SalesLT.Address'

clip_image010

(Figure8:正确的架构名)

  6) 经过上面经验的总结,关于所有数据库所有表的信息的SQL脚本就水到渠成了,下面就是全部的SQL脚本,注意过滤的方式可以写成:IF ''?'' like(''A%'') ,执行的效果如Figure2所示:

--Script3:
-- =============================================
-- Author:        <听风吹雨>
-- Create date:    <2013.05.03>
-- Description:    <查看所有数据库所有表信息>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
--定义临时表
IF NOT EXISTS (SELECT * FROM [tempdb].sys.objects WHERE object_id = OBJECT_ID(N'[tempdb].[dbo].[tablespaceinfo]') AND type in (N'U'))
BEGIN
CREATE TABLE [tempdb].[dbo].[tablespaceinfo](
    [db_name] [sysname] NULL,
    [table_name] [sysname] NULL,
    [rows] [bigint] NULL,
    [reserved] [varchar](100) NULL,
    [data] [varchar](100) NULL,
    [index_size] [varchar](100) NULL,
    [unused] [varchar](100) NULL
) ON [PRIMARY]
END
ELSE
    TRUNCATE TABLE tempdb.dbo.tablespaceinfo

DECLARE @SQL NVARCHAR(MAX)
SET @SQL = COALESCE(@SQL,'') + '
USE [?];
--屏蔽掉系统数据库
IF ''?'' not in(''tempdb'',''master'',''model'',''msdb'')
--IF ''?'' like(''A%'')
BEGIN
PRINT ''?''

DECLARE @schemas_name VARCHAR(255);
DECLARE @table_name VARCHAR(255);

DECLARE Info_cursor CURSOR FOR
    --获取schemas_name和table_name
    SELECT b.name AS schemas_name,''[''+a.[name]+'']'' AS table_name FROM ?.sys.tables AS a
        LEFT JOIN ?.sys.schemas AS b
        ON a.schema_id = b.schema_id
        WHERE TYPE=''U''

OPEN Info_cursor
FETCH NEXT FROM Info_cursor INTO @schemas_name,@table_name

WHILE @@FETCH_STATUS = 0
BEGIN
    --把表信息插入到临时表
    SET @table_name = ''[''+@schemas_name+'']''+''.''+@table_name
    INSERT INTO tempdb.dbo.tablespaceinfo([table_name],[rows],[reserved],[data],[index_size],[unused])
        EXEC ?.dbo.sp_spaceused @table_name
    --更新数据库名称
    UPDATE tempdb.dbo.tablespaceinfo SET [db_name] = ''?'' WHERE [db_name] IS NULL
    
    FETCH NEXT FROM Info_cursor INTO @schemas_name,@table_name
END

CLOSE Info_cursor
DEALLOCATE Info_cursor
END
'
--循环所有数据库
PRINT @SQL
EXEC sp_MSForEachDB @SQL

--返回临时表数据
SELECT * FROM tempdb.dbo.tablespaceinfo
    ORDER BY [db_name],Cast(Replace(reserved,'KB','') AS INT) DESC
--ORDER BY [db_name],[table_name]

DROP TABLE [tempdb].[dbo].[tablespaceinfo]

(二) 还有没其他方式可以实现Figure2的效果呢?你可以考虑使用sp_MSforeachtable的方式实现,先使用存储过程sp_spaceused_db简单封装sp_MSforeachtable,它简单实现获取某个数据库的所有表,再使用拼凑生成批量的INSERT和UPDATE语句生成表信息数据,Script9与Script10需要分开执行,执行的效果如Figure2所示:

--Script4:
USE [tempdb]
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_spaceused_db]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[sp_spaceused_db]
GO

-- =============================================
-- Author:        <听风吹雨>
-- Create date: <2013.05.06>
-- Description:    <封装sp_MSforeachtable>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
CREATE PROCEDURE [dbo].[sp_spaceused_db]
    @db_name nvarchar(776) = null
AS
BEGIN
    DECLARE @SQL NVARCHAR(MAX)
    SELECT @SQL = COALESCE(@SQL,'') + '
        EXEC ['+@db_name+'].dbo.sp_MSforeachtable @command1="sp_spaceused ''?''"'
    
    PRINT(@SQL)
    EXECUTE(@SQL)
END
--Script5:
-- =============================================
-- Author:        <听风吹雨>
-- Create date: <2013.05.06>
-- Description:    <查看所有数据库所有表信息>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
CREATE TABLE [tempdb].[dbo].[tablespaceinfo](
    [db_name] [sysname] NULL,
    [table_name] [sysname] NULL,
    [rows] [bigint] NULL,
    [reserved] [varchar](100) NULL,
    [data] [varchar](100) NULL,
    [index_size] [varchar](100) NULL,
    [unused] [varchar](100) NULL
)

DECLARE @SQL NVARCHAR(MAX)
SELECT @SQL = COALESCE(@SQL,'') + '
INSERT INTO [tempdb].[dbo].[tablespaceinfo]([table_name],[rows],[reserved],[data],[index_size],[unused])
    EXEC [tempdb].dbo.sp_spaceused_db ' + QUOTENAME(name,'''') + '
UPDATE [tempdb].[dbo].[tablespaceinfo] SET [db_name] = ' + QUOTENAME(name,'''') + '
    WHERE [db_name] IS NULL'
FROM sys.databases WHERE database_id >4

PRINT(@SQL)
EXECUTE(@SQL)

SELECT * FROM [tempdb].[dbo].[tablespaceinfo]
    ORDER BY [db_name],Cast(Replace(reserved,'KB','') AS INT) DESC

DROP TABLE [tempdb].[dbo].[tablespaceinfo]

(三) 如果你想使用sp_MSForEachDB与sp_MSforeachtable(sp_MSforeach_worker、sp_MStablespace)结合的方式实现Figure2效果,刚开始测试的时候发现这两个存储过程在解释“?”的时候会出现歧义,SQL无法理解它是指数据库还是表,难道微软会做那么愚蠢的事情?后来查看了这两个存储过程的SQL脚本,发现是有办法解决上面问题的。

--Script6:
-- =============================================
-- Author:        <听风吹雨>
-- Create date: <2013.05.08>
-- Description:    <查看所有数据库所有表信息>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
IF NOT EXISTS (SELECT * FROM [tempdb].sys.objects WHERE object_id = OBJECT_ID(N'[tempdb].[dbo].[tablespaceinfo]') AND type in (N'U'))
BEGIN
CREATE TABLE [tempdb].[dbo].[tablespaceinfo](
    [db_name] [sysname] NULL,
    [table_name] [sysname] NULL,
    [rows] [bigint] NULL,
    [reserved] [varchar](100) NULL,
    [data] [varchar](100) NULL,
    [index_size] [varchar](100) NULL,
    [unused] [varchar](100) NULL
) ON [PRIMARY]
END
ELSE
    TRUNCATE TABLE tempdb.dbo.tablespaceinfo

DECLARE @SQL NVARCHAR(MAX)
SELECT @SQL = COALESCE(@SQL,'') + '
USE [?];
--屏蔽掉系统数据库
IF ''?'' not in(''tempdb'',''master'',''model'',''msdb'') 
BEGIN
    --插入表信息
    INSERT INTO tempdb.dbo.tablespaceinfo([table_name],[rows],[reserved],[data],[index_size],[unused])
        EXEC [?].sys.sp_MSforeachtable @command1="sp_spaceused N''$''",@replacechar=N''$''
    --更新数据库名称
    UPDATE tempdb.dbo.tablespaceinfo SET [db_name] = ''?'' WHERE [db_name] IS NULL

END'
PRINT (@SQL)

--所有数据库
EXEC sp_MSforeachdb @command1="print '?'",@command2=@SQL, @replacechar=N'?'

--返回临时表数据
SELECT * FROM tempdb.dbo.tablespaceinfo
    ORDER BY [db_name],Cast(Replace(reserved,'KB','') AS INT) DESC

DROP TABLE [tempdb].[dbo].[tablespaceinfo]

(四) 上面的Script6脚本过滤数据的方式比较麻烦,所以我对sp_MSforeachdb系统存储过程进行了一些修改,生成一个新的存储过程sp_MSforeachdb_Filter,详情请查看:SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database),在创建了存储过程sp_MSforeachdb_Filter的情况下执行下面的SQL脚本,你可以随意修改@whereand参数来满足你的过滤条件,非常方便。

--Script7:
-- =============================================
-- Author:        <听风吹雨>
-- Create date: <2013.05.08>
-- Description:    <查看所有数据库所有表信息>
-- Blog:        <http://www.cnblogs.com/gaizai/>
-- =============================================
IF NOT EXISTS (SELECT * FROM [tempdb].sys.objects WHERE object_id = OBJECT_ID(N'[tempdb].[dbo].[tablespaceinfo]') AND type in (N'U'))
BEGIN
CREATE TABLE [tempdb].[dbo].[tablespaceinfo](
    [db_name] [sysname] NULL,
    [table_name] [sysname] NULL,
    [rows] [bigint] NULL,
    [reserved] [varchar](100) NULL,
    [data] [varchar](100) NULL,
    [index_size] [varchar](100) NULL,
    [unused] [varchar](100) NULL
) ON [PRIMARY]
END
ELSE
    TRUNCATE TABLE tempdb.dbo.tablespaceinfo

DECLARE @SQL NVARCHAR(MAX)
SELECT @SQL = COALESCE(@SQL,'') + '
    --插入表信息
    INSERT INTO tempdb.dbo.tablespaceinfo([table_name],[rows],[reserved],[data],[index_size],[unused])
        EXEC [?].sys.sp_MSforeachtable @command1="sp_spaceused N''$''",@replacechar=N''$''
    --更新数据库名称
    UPDATE tempdb.dbo.tablespaceinfo SET [db_name] = ''?'' WHERE [db_name] IS NULL'
PRINT (@SQL)

----过滤数据库
--EXEC [sp_MSforeachdb_Filter] @command1="print '?'",@command2=@SQL, @replacechar=N'?',
--@whereand=" and [name] not in('tempdb','master','model','msdb') "

--过滤数据库
EXEC [sp_MSforeachdb_Filter] @command1="print '?'",@command2=@SQL, @replacechar=N'?',
@whereand=" and [dbid] > 4 "

--返回临时表数据
SELECT * FROM tempdb.dbo.tablespaceinfo
    ORDER BY [db_name],Cast(Replace(reserved,'KB','') AS INT) DESC

DROP TABLE [tempdb].[dbo].[tablespaceinfo]

四.参考文献(References)

与存储过程sp_MSforeachdb类似的存储过程sp_MSforeachdb

SQL Server数据库开发顶级技巧

sp_MSforeachtable使用方法

How to get information about all databases without a loop

关于quotename的用法

SQL Server 游标运用:查看一个数据库所有表大小信息(Sizes of All Tables in a Database)

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
相关文章
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL 数据库 SQL 语句调优方法详解(2-1)
本文深入介绍 MySQL 数据库 SQL 语句调优方法。涵盖分析查询执行计划,如使用 EXPLAIN 命令及理解关键指标;优化查询语句结构,包括避免子查询、减少函数使用、合理用索引列及避免 “OR”。还介绍了索引类型知识,如 B 树索引、哈希索引等。结合与 MySQL 数据库课程设计相关文章,强调 SQL 语句调优重要性。为提升数据库性能提供实用方法,适合数据库管理员和开发人员。
数据库编程:在PHP环境下使用SQL Server的方法。
看看你吧,就像一个调皮的小丑鱼在一片广阔的数据库海洋中游弋,一路上吞下大小数据如同海中的珍珠。不管有多少难关,只要记住这个流程,剩下的就只是探索未知的乐趣,沉浸在这个充满挑战的数据库海洋中。
54 16
如何优化SQL查询以提高数据库性能?
这篇文章以生动的比喻介绍了优化SQL查询的重要性及方法。它首先将未优化的SQL查询比作在自助餐厅贪多嚼不烂的行为,强调了只获取必要数据的必要性。接着,文章详细讲解了四种优化策略:**精简选择**(避免使用`SELECT *`)、**专业筛选**(利用`WHERE`缩小范围)、**高效联接**(索引和限制数据量)以及**使用索引**(加速搜索)。此外,还探讨了如何避免N+1查询问题、使用分页限制结果、理解执行计划以及定期维护数据库健康。通过这些技巧,可以显著提升数据库性能,让查询更高效流畅。
【SQL技术】不同数据库引擎 SQL 优化方案剖析
不同数据库系统(MySQL、PostgreSQL、Doris、Hive)的SQL优化策略。存储引擎特点、SQL执行流程及常见操作(如条件查询、排序、聚合函数)的优化方法。针对各数据库,索引使用、分区裁剪、谓词下推等技术,并提供了具体的SQL示例。通用的SQL调优技巧,如避免使用`COUNT(DISTINCT)`、减少小文件问题、慎重使用`SELECT *`等。通过合理选择和应用这些优化策略,可以显著提升数据库查询性能和系统稳定性。
115 9
【潜意识Java】MyBatis中的动态SQL灵活、高效的数据库查询以及深度总结
本文详细介绍了MyBatis中的动态SQL功能,涵盖其背景、应用场景及实现方式。
354 6
使用访问指导(SQL Access Advisor)优化数据库业务负载
本文介绍了Oracle的SQL访问指导(SQL Access Advisor)的应用场景及其使用方法。访问指导通过分析给定的工作负载,提供索引、物化视图和分区等方面的优化建议,帮助DBA提升数据库性能。具体步骤包括创建访问指导任务、创建工作负载、连接工作负载至访问指导、设置任务参数、运行访问指导、查看和应用优化建议。访问指导不仅针对单条SQL语句,还能综合考虑多条SQL语句的优化效果,为DBA提供全面的决策支持。
126 11
MySQL导入.sql文件后数据库乱码问题
本文分析了导入.sql文件后数据库备注出现乱码的原因,包括字符集不匹配、备注内容编码问题及MySQL版本或配置问题,并提供了详细的解决步骤,如检查和统一字符集设置、修改客户端连接方式、检查MySQL配置等,确保导入过程顺利。
SQL Servers审核提高数据库安全性
SQL Server审核是一种追踪和审查SQL Server上所有活动的机制,旨在检测潜在威胁和漏洞,监控服务器设置的更改。审核日志记录安全问题和数据泄露的详细信息,帮助管理员追踪数据库中的特定活动,确保数据安全和合规性。SQL Server审核分为服务器级和数据库级,涵盖登录、配置变更和数据操作等事件。审核工具如EventLog Analyzer提供实时监控和即时告警,帮助快速响应安全事件。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等