【问题标题】:The most efficient way to get all data from SQL Server table with varchar(max) column使用 varchar(max) 列从 SQL Server 表中获取所有数据的最有效方法
【发布时间】:2009-10-01 19:18:57
【问题描述】:

此问题适用于 SQL Server 2005。

我有一个有 2 列的表格。

Table_A
    Id Guid (PrimaryKey)
    TextContent varchar(max)

该表包含大约 7000 条记录,文本内容范围从 0 到 150K+。

当我做一个选择语句时 SELECT Id, TextContent FROM Table_A,耗时10分钟左右。

有没有更好的方法从表中取出所有数据?

在主要执行期间,我只加载某些记录。示例:SELECT Id, TextContent FROM TableA WHERE ID IN (@id0,@id1, @id2, @id3....@id20)。这个查询并不慢,但也不是那么快。我想看看是否可以通过在运行时之前拉出 TextContent 来优化流程。此过程在一两分钟内运行是可以的,但 10 分钟是不可接受的。

【问题讨论】:

  • 你真的需要一次搞定吗?
  • 如果您告诉我们您打算如何处理这些数据,我们可以提供获取数据的最佳方法。
  • 我希望你不要试图在应用程序中过滤它!如果是,请在 SQL 中添加 WHERE 和过滤器
  • 这个桌子设计有很多问题,很难知道从哪里开始。你能改变桌子的设计吗?这里有很多人可以帮助您设计更适合您需求的东西。
  • 我只是稍微澄清一下这个问题。

标签: sql-server


【解决方案1】:

GUID 是主键,默认情况下它也是您的集群键,这无疑会导致大量碎片 - 但考虑到列的性质,varchar(max) 将在LOB 存储,而不是存储在页面上,除非它适合同时保持在 8060 限制内。

因此,如果您也将 GUID 设为集群,则将 GUID 设为主要不会对碎片有所帮助 - 您可以使用 DMV sys.dm_db_index_physical_stats 检查碎片级别

我认为碎片并不是真正的问题,除非每行的平均数据量很高,例如经常在 8k 以上。

如果是的话,……碎片开始疼了。最坏的情况是每页 1 行,7k I/O,这并不理想,但在每个 LOB 存储平均 100k 的情况下,您可能会看到更多的 87k I/O,以及写入数据的顺序等会导致什么应该是对表(和磁盘)的顺序扫描,当磁盘磁头在带有行 + LOB 指针的页面和 LOB 页面之间来回长行程时,变成了大规模的随机 I/O 巨星。 除此之外,GUID 也有可能是集群键,因此它甚至无法扫描数据页而无需大量磁盘磁头移动。

我还必须同意 Erich 的观点,即您尝试通过网络传输的数据量会导致链接不足时产生相当大的延迟,您应该寻求通过分页或适当的查询在服务器级别正确过滤数据.

我知道您希望预先缓存数据,这有时可以工作 - 但它是在如此大的实体上执行的,它往往表明其他问题是错误的,而您正在修复错误的问题。

一个。

【讨论】:

    【解决方案2】:

    这是从表中取出数据的正确方法,除非您只需要 1 行。如果您只需要 1 行,只需使用正确的查询即可。

    您使用的是哪种网络连接?这么说吧,你有 7000 条记录。每个平均包含 100k 数据(为方便起见,如果它多于或少于此,那很好,我的观点仍然成立)。总查询将返回 700 MB 的数据!即使通过极快的连接,下载时间也很容易达到 10 分钟。

    即使在完美的 100 兆连接下,传输也需要将近一分钟!此外,您必须从物理磁盘中取出数据,这还需要一段时间。

    我建议进行某种分页,以便将数据分成更小的部分。

    【讨论】:

    • 分页是不错的选择。但是,我想看看是否可以一口气完成,而无需花时间实现某种分页。
    • 看起来没有什么好的方法可以一次从 SQL 表中取出所有数据。目前最好的方法是分块获取数据。
    【解决方案3】:

    我对此表示怀疑。如果你想“从表中取出所有数据”,那么你必须读取存储在表中的每个字节,这可能需要大量的物理磁盘 I/O。

    也许您想要的只是从表中检索一些数据?

    【讨论】:

      【解决方案4】:

      您的 Id 列是一个 GUID。你用的是默认的吗?是 NewID() 吗?我假设它聚集在 PK 上。

      如果您使用 NewSequentialID() 作为默认值,您将获得更少的页面拆分,因此您的数据将分布在更少的物理页面上。

      有了这么多的数据,这是我能看到的唯一有助于提高性能的东西。

      【讨论】:

        【解决方案5】:

        正如许多其他人提到的,您正在获取大量数据。首先确定你是否真的需要所有的行。

        如果这样做,请不要一次获取所有内容 - 请改用 LIMIT。这实际上会降低速度,但如果出现任何故障,您只需再次加载一小段时间,无需再等待 10 分钟。

        SELECT Id, TextContent FROM Table_A LIMIT 0, 30
        

        此查询将获取表的前 30 个条目。与

        SELECT Id, TextContent FROM Table_A LIMIT 30, 30
        

        你会得到下一块。

        也许您可以向我们提供更多信息,例如您想如何处理这些数据以及您使用哪种编程语言?

        【讨论】:

        • LIMIT 关键字在 SQL Server 2005 中不可用。
        • D'oh.. 不知道。尽管如此,应该可以用一个 int 作为主键来实现类似的东西,然后只获取一个范围。
        【解决方案6】:

        是的

        1) 永远不要使用 SELECT *,总是列出你的列,无论是 1、2 还是 100 2)尝试查看索引

        150k 个字符?在那个领域?你指的是这个吗?

        【讨论】:

        • 通常情况下,我会同意从不使用 Select * 的说法,但在这种情况下,他想获取所有列...如果他想获取所有列,是否有性能优势?跨度>
        • 此外,在这种情况下,索引也无济于事,因为他没有进行任何连接,也没有 where 子句。
        • 大卫现在他想要所有的专栏,但总是为未来做计划。有人要求放置某种不应该显示给最终用户的位字段的那一刻,它突然出现在那里。永远不要选择 *,良好的编程习惯。不要懒惰列出任何选择中的所有列。我不明白为什么有人还想要那么多数据……你能想象分页那么多数据吗?有多少人在 google 上搜索并查看 google 一词中的第 1000 个 o 以获得结果?
        • 我只是将 SELECT * 放在问题中,因为我懒得输入 SELECT Id、TextContent。在生产代码中,我不使用 SELECT *。因为我确实相信 SELECT * 应该在生产代码中被禁止。
        • @David Stratton:是的,这对性能有好处——尽管在这种特殊情况下,这绝对是无关紧要的。好处是,如果您指定 SELECT *,那么 SQL Server 必须首先在系统目录中查找您的表的列列表,这显然需要一些时间 - 特别是如果您有很多列。在这里,只有一个查询和两列,它是边缘的 - 但它仍然存在。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-07-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多