即使未引用,表中的字段数是否会影响性能?

时间:2015-01-06 03:19:17

标签: sql-server sql-server-2008

我正在阅读并将CSV文件解析为SQL Server 2008数据库。此过程对所有文件使用通用CSV解析器。

CSV解析器将解析后的字段放入通用字段导入表(F001 VARCHAR(MAX)NULL,F002 VARCHAR(MAX)NULL,Fnnn ...),然后另一个进程使用知道的SQL代码移动到实际表中哪个解析字段(Fnnn)转到目标表中的哪个字段。因此,只要在表中,就会引用正在复制的字段。有些文件可能会变得非常大(一百万行)。

问题是:表中的字段数是否会显着影响性能或内存使用量?即使大多数字段未被引用。对字段导入表执行的唯一操作是INSERT,然后是SELECT以将数据移动到另一个表中,字段数据上没有任何JOIN或WHERE。

目前,我有三个字段导入表,一个有20个字段,一个有50个字段,一个有100个字段(这是我到目前为止遇到的最大字段数)。目前有逻辑可以使用最小的文件。

我想让这个过程更通用,并且有一个包含1000个字段的表(我知道1024列的限制)。是的,一些待处理的计划文件(来自第三方)将在900-1000字段范围内。

对于大多数文件,将少于50个字段。

此时,处理现有的三个字段导入表(以及更多字段的计划表(200,500,1000?))正在成为代码中的后勤噩梦,处理单个表将解决许多问题,只要我不这样做;放弃很多表现。

3 个答案:

答案 0 :(得分:2)

首先,回答所述问题:

  

即使没有引用,表中字段的数量是否会影响性能?

  • 如果字段是固定长度的(* INT,* MONEY,DATE / TIME / DATETIME / etc,UNIQUEIDENTIFIER等)并且该字段未标记为SPARSE或压缩尚未启用(均在SQL Server 2008中启动),然后占用字段的完整大小(即使NULL),这确实会影响性能,即使字段不在SELECT列表中也是如此。

  • 如果字段是可变长度和NULL(或空),那么它们只占用页眉中的少量空间。

  • 关于空间,一般来说,这个表是堆(没有聚簇索引)还是聚簇?你如何清除每个新导入的表格?如果它是一个堆而你只是在做DELETE,那么它可能无法摆脱所有未使用的页面。在sp_spaceused执行tempdb时,即使有0行占用空间,也会知道是否存在问题。下面的建议2和3自然不会出现这样的问题。

现在,一些想法:

  1. 您是否考虑过使用SSIS动态处理此问题?

  2. 由于您似乎有单线程进程,为什么不在每次进程开始时创建一个全局临时表?或者,在INSERT INTO...EXEC中删除并重新创建一个真实的表格?无论哪种方式,如果您知道目标,您甚至可以使用目标字段名称和数据类型动态创建此导入表。即使CSV导入器不知道目的地,在过程开始时您可以调用知道目的地的过程,可以创建“临时”表,然后导入器仍然可以一般导入标准没有指定字段的表名,如果表中的字段是NULLable且至少与文件中的列一样多,则不会出错。

  3. 传入的CSV数据是否包含嵌入的返回,引号和/或分隔符?您是否操纵登台表和目标表之间的数据?可以使用正确的数据类型直接动态导入目标表,但不能进行传输操作。另一个选择是在SQLCLR中执行此操作。您可以编写存储过程来打开文件,并在执行File_SplitIntoFields时吐出拆分字段。或者,如果您不想自己编写,请查看SQL# SQLCLR库,特别是<F001>存储过程。此proc仅在Full / paid-for版本中可用,并且我是SQL#的创建者,但它似乎非常适合这种情况。

  4. 鉴于:

    • 所有字段都以文字形式导入
    • 目标字段名称和类型已知
    • 目标表之间的字段数不同

    如果只有一个XML字段并将每行导入为单个级别的文档,每个字段为<F002>ID LoadFileID ImportLine 1 1 <row><FirstName>Bob</FirstName><LastName>Villa</LastName></row> 2 1 <row><Number>555-555-5555</Number><Type>Cell</Type></row> 等,那该怎么办?通过这样做,您不必担心字段数或有任何未使用的字段。实际上,由于目标字段名称对于进程是已知的,因此您甚至可以使用这些名称来命名XML文档中每行的元素。所以行看起来像:

    {{1}}

    是的,数据本身将占用比当前VARCHAR(MAX)字段更多的空间,这是因为XML是双字节的,并且元素标签的固有体积开始。但是你不会陷入任何物理结构。只是查看数据将更容易识别问题,因为您将查看真实的字段名称而不是F001,F002等。

  5. 至少在加快读取文件,拆分字段和插入的过程方面,您应该使用表值参数(TVP)将数据流式传输到导入表中。我在这里有一些答案显示了该方法的各种实现,主要区别在于数据源(文件与已经在内存中的集合等):

答案 1 :(得分:1)

是肯定的。大型记录占用磁盘和内存中的更多空间,这意味着加载它们比小记录慢,而更少的记录可以放在内存中。这两种效果都会影响性能。

答案 2 :(得分:1)

正如评论中正确指出的那样,即使您的表格有1000列,但其中大部分都是NULL,但它不应该影响性能,因为NULLs不会浪费大量空间

您提到您可能拥有900-1000非NULL列的实际数据。如果您计划导入此类文件,则可能会遇到SQL Server的另一个限制。是的,表中的最大列数为1024,但限制为8060 bytes per row。如果你的列是varchar(max),那么每个这样的列将占用实际行中8060的24个字节,其余的数据将被推送到行外:

  

SQL Server支持行溢出存储,它可以实现可变长度   列被推离行。只有一个24字节的根存储在   可变长度列的主记录被推出行;因为   这个,有效行限制高于以前的版本   SQL Server。有关更多信息,请参阅“超出行的溢出数据”   SQL Server联机丛书中的8 KB“主题。

因此,在实践中,您可以拥有一个只有8060 / 24 = 335 nvarchar(max)非NULL列的表。 (严格来说,即使少一点,也有其他标题)。

所谓的wide tables最多可包含30,000列,但宽表行的最大大小为8,019字节。所以,在这种情况下,他们不会真正帮助你。