Sql server 分层数据结构设计(嵌套集)

Sql server 分层数据结构设计(嵌套集),sql-server,database,database-design,Sql Server,Database,Database Design,我正在设计一个分层数据库结构,它对包含产品的目录进行建模(类似于)。数据库平台是SQL Server 2005,目录非常大(750000个产品,超过4个级别的8500个目录节),但相对静态(每天重新加载一次),因此我们只关心读取性能 目录层次结构的一般结构为:- 一级组 二级组 三级组 第4级部分(产品链接到此处) 我们使用嵌套集模式来存储层次结构级别,并将该级别上存在的产品存储在单独的链接表中。因此,简化的数据库结构将是 CREATE TABLE CatalogueSectio

我正在设计一个分层数据库结构,它对包含产品的目录进行建模(类似于)。数据库平台是SQL Server 2005,目录非常大(750000个产品,超过4个级别的8500个目录节),但相对静态(每天重新加载一次),因此我们只关心读取性能

目录层次结构的一般结构为:-

  • 一级组
    • 二级组
      • 三级组
        • 第4级部分(产品链接到此处)
我们使用嵌套集模式来存储层次结构级别,并将该级别上存在的产品存储在单独的链接表中。因此,简化的数据库结构将是

CREATE TABLE CatalogueSection
(
    SectionID INTEGER,
    ParentID INTEGER,
    LeftExtent INTEGER,
    RightExtent INTEGER
)

CREATE TABLE CatalogueProduct
(
    ProductID INTEGER,
    SectionID INTEGER
)
我们确实有一个额外的复杂性,因为我们有大约1000个独立的客户群体,他们可能会或可能不会看到目录中的所有产品。因此,我们需要为每个客户群维护一份单独的目录层次结构“副本”,这样当他们浏览目录时,他们只能看到自己的产品,也看不到任何空白部分

为了便于实现这一点,我们维护了一个表,其中列出了从下一节“汇总”的层次结构的每一级的产品数量。因此,即使产品只直接链接到层次结构的最低级别,它们也会一直计算到树的最上面。此表的结构如下所示

CREATE TABLE CatalogueSectionCount
(
    SectionID INTEGER,
    CustomerGroupID INTEGER,
    SubSectionCount INTEGER,
    ProductCount INTEGER
)
那么,问题就来了 在层次结构的顶层,性能非常差。显示所选目录部分(以及所有子部分)中“前10名”产品的一般查询需要1分钟才能完成。在层次结构的较低部分,速度更快,但仍然不够好

我已经将索引(包括适用的覆盖索引)放在所有键表上,通过查询分析器、索引优化向导等运行它,但仍然无法让它执行得足够快

我想知道这个设计是否有根本性的缺陷,还是因为我们有这么大的数据集?我们有一个合理的开发服务器(3.8GHZ Xeon,4GB RAM),但它不工作:)

谢谢你的帮助


James

您可能可以通过角色和treeId解决客户群问题,但您必须向我们提供查询。

是否可以在每天加载后计算ProductCount和SubSectionCount?

如果数据一天只改变一次,那么计算这些数字当然是值得的,即使需要一些非规范化。

使用一个闭包表。如果基本结构是具有字段ID和ParentID的父子结构,则闭包表的结构是ID和DescendandId。换句话说,闭包表是一个祖先-后代表,其中每个可能的祖先都与所有后代相关联。如果需要,可以包括LevelsBetween字段。闭包表实现通常包括自引用记录,即ID 1是子代ID 1的祖先,其级别介于零和零之间

例如: 父/子
ParentID-ID
1-2
1-3
3-4
3-5
4-6

祖先/后代
ID-下降和反水平之间
1-1-0
1-2-1
1-3-1
1-4-2
1-6-3
2-2-0
3-3-0
3-4-1
3-5-1
3-6-2
4-4-0
4-6-1
5-5-0

该表旨在消除递归联接。当您每天加载一次数据时,您会将递归连接的加载推入ETL循环。这就把它从查询中移开了

此外,它还允许可变级别的层次结构。你不会被困在4点

最后,它允许您在非叶节点中插入产品。许多目录在更高层次上创建“杂项”存储桶,以创建要将产品附加到的叶节点。您不需要这样做,因为中间节点包含在闭包中

就索引而言,我会在ID/degendanti上进行聚集索引


现在介绍查询性能。这会去掉一大块,但不是全部。你提到了“前十名”。这意味着对一组你没有提到的事实进行排序。我们需要细节来帮助调整这些。另外,它只获取叶级部分,而不获取产品。至少,您应该在CataloteProduct上有一个按SectionID/ProductID订购的索引。根据您提供的基数,我将强制将节到产品的联接作为循环联接。目录节上的报告将转到闭包表以获取子体(使用聚集索引查找)。然后,子体列表将用于使用循环索引查找索引从CatalogeProduct获取产品。然后,使用这些产品,您将获得进行排名所需的事实。

也许向我们展示慢SQL会有所帮助?我们可能会发现一些会造成瓶颈的东西。是的,我们已经每天预先计算了这些。问题不在于产品的计数,而在于所选部分中显示的实际产品列表速度较慢。您是否在重新加载数据后更新统计信息?如果您的索引很好(调整为只读),那么可能是您返回的数据太多了吗?这是我下一步可能关注的领域。TBH,在没有看到模式和/或存储过程的情况下,要提供更多帮助将是非常困难的。非常好,这正是我所需要的,并且确实提高了性能。谢谢