为什么 SQL Server 表在添加 55MB 数据后需要多占用 1GB?

Pat*_*eVB 5 sql-server

我发现一个客户数据库的大小在昨天和今天之间激增。经过调查,我在表模式中发现了一个设计问题,但在进行了一些数学运算后,我知道已将 55MB 的数据添加到表中,但表大小现在为 1GB,而表的原始大小为 70MB。

有关其他信息,通过添加 2000 多行且varchar(max)字段填充数据来添加 55MB数据。

这是之前/之后的分析:

昨天 昨天

今天 今天

你能向我解释一下为什么一个表需要 70MB 现在在添加 55MB 数据后需要 1GB 吗?

上没有任何索引 varchar(max)

可用空间非常低,与自增长无关。并且自动增长不是用百分比配置的,而是用固定的增长大小配置的。

这是表的创建脚本:

USE [DuoClient20140003]
GO

/****** Object:  Table [dbo].[tContactHistory]    Script Date: 23/05/2016 10:53:19 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[tContactHistory](
    [KeyCH] [int] IDENTITY(1,1) NOT NULL,
    [DateCH] [datetime] NULL,
    [KeyType] [int] NULL,
    [KeyF] [int] NULL,
    [KeyI] [int] NULL,
    [KeyCA] [int] NULL,
    [Content] [varchar](max) NULL,
    [KeyCom] [int] NULL,
    [KeyMH] [bigint] NULL,
    [KeyH] [bigint] NULL,
 CONSTRAINT [tContactHistory_PK] PRIMARY KEY CLUSTERED 
(
    [KeyCH] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tAddrCarnet_tContactHistory_FK1] FOREIGN KEY([KeyCA])
REFERENCES [dbo].[tAddrCarnet] ([KeyCA])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tAddrCarnet_tContactHistory_FK1]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tComments_tContactHistory_FK1] FOREIGN KEY([KeyCom])
REFERENCES [dbo].[tComments] ([KeyCom])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tComments_tContactHistory_FK1]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tFoyers_tContactHistory_FK1] FOREIGN KEY([KeyF])
REFERENCES [dbo].[tFoyers] ([KeyF])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tFoyers_tContactHistory_FK1]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tIndivs_tContactHistory_FK1] FOREIGN KEY([KeyI])
REFERENCES [dbo].[tIndivs] ([KeyI])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tIndivs_tContactHistory_FK1]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tMailHistory_tContactHistory] FOREIGN KEY([KeyH])
REFERENCES [dbo].[tMailHistory] ([KeyH])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tMailHistory_tContactHistory]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tModifsHistory_tContactHistory_FK1] FOREIGN KEY([KeyMH])
REFERENCES [dbo].[tModifsHistory] ([KeyMH])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tModifsHistory_tContactHistory_FK1]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tTabShort_tContactHistory_FK1] FOREIGN KEY([KeyType])
REFERENCES [dbo].[tTabShort] ([KeyT])
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tTabShort_tContactHistory_FK1]
GO

ALTER TABLE [dbo].[tContactHistory]  WITH NOCHECK ADD  CONSTRAINT [tContactHistoryKeyI_Chk] CHECK  (([KeyI]>=(1)))
GO

ALTER TABLE [dbo].[tContactHistory] CHECK CONSTRAINT [tContactHistoryKeyI_Chk]
GO
Run Code Online (Sandbox Code Playgroud)

Kah*_*ahn 6

系统过程sp_spaceused告诉您数据是如何分布的,并且通常足够准确。运行以下简单的 TSQL:

DECLARE @R TABLE (TabName sysname, [Row Count] bigint, Reserved varchar(128), Data varchar(128), [Idx Size] varchar(128), Unused varchar(128))

INSERT INTO @R
exec sp_spaceused 'tContactHistory'

SELECT TabName
    , [Row Count]
    , cast(round(cast(replace(Reserved, ' KB','') as dec(32,2))/1024,2) as dec(32,2)) [Reserved MB]
    , cast(round(cast(replace(Data, ' KB','') as dec(32,2))/1024,2) as dec(32,2)) [Data MB]
    , cast(round(cast(replace([Idx Size], ' KB','') as dec(32,2))/1024,2) as dec(32,2)) [Idx Size MB]
    , cast(round(cast(replace(Unused, ' KB','') as dec(32,2))/1024,2) as dec(32,2)) [Unused MB]
FROM @R
Run Code Online (Sandbox Code Playgroud)

告诉我们你发现了什么。如果填充因子、数据类型或只是简单的过分热心索引存在问题,这至少应该提供足够的线索,告诉我们下一步该往哪里走。

更新:结果是:

截屏

那么你的结果很奇怪。这意味着实际使用了数据。我还看到有人在你如何验证数据大小之前已经问过你,你回答说你是通过从字段大小计算它来完成的。你确定你这样做正确吗?是否有可能引入了您看不到的空字符串或特殊字符,但仍然占用空间?或者您正在处理而CHAR不是VARCHAR具有相同最终结果的字段?

DATALENGTH例如,请参阅函数以检查可变长度列的长度。

-- For example:
SELECT DATALENGTH(myVarCharColumnName) DLen 
FROM tContactHistory 
ORDER BY DLen desc
-- Note by checking the column type and storage size from MSDN, 
-- you can now use this information to calculate the storage size.
Run Code Online (Sandbox Code Playgroud)

此外,您是否通过例如将部分数据复制到新的虚拟表来测试这个假设,看看会发生什么?喜欢

SELECT TOP 1000 *
INTO myStorageSizeTest
FROM tContactHistory
WHERE ... -- Insert whatever condition here to only select the new rows where this space problem appeared
Run Code Online (Sandbox Code Playgroud)

等等。不要害怕测试和发挥创意。