SQL Server 2008 - 过多的非规范化和过度索引:矩阵有啥用?

Posted

技术标签:

【中文标题】SQL Server 2008 - 过多的非规范化和过度索引:矩阵有啥用?【英文标题】:SQL Server 2008 - Too much denormalization and over Indexing: What use is there for the Matrix?SQL Server 2008 - 过多的非规范化和过度索引:矩阵有什么用? 【发布时间】:2011-12-08 03:47:51 【问题描述】:

我有一个崭露头角的开发人员,他对他称之为“矩阵”的东西非常热情

我正在寻找同行的见解

简而言之,这就是我们所拥有的: - 1 个高度非规范化的表,大约 120 列 - 数据点范围包括帐户、客户、家庭、关系、产品、员工等…… - 每列一个索引:大约 120 个非聚集索引 - 今天索引使用的数据库中大约 90% 的空间是该表上的索引 - 今天大约有 150 万行有很多空值 - 表加载了以动态 SQL 为核心的存储过程 - 所有字段名称都是通用的,不描述数据 - 数据字典类型表与动态 SQL 一起使用,将任何数据点加载到任何字段 - 字段映射不是静态的:今天列 dim_0001 是客户名称,但明天可能是其他名称 - 没有主键 - 没有外键 - 没有真正的约束(例如所有字段都可以为空)

表格的参数: - 使编写查询更简单,因为它消除了编写一些连接的需要

预期用途: - 最终用户层,将成为 Business Objects 中构建的 Universe 的核心组件 - 后期 ETL 流程开发

我的建议要么终止当前的流程(测试环境中的早期开发),要么将其移至测试的下一步。

根据我所做的研究、我的教育和经验,我不支持它,并且希望在依赖这些表的一个或两个进程迁移到另一个解决方案后立即删除这些表。

下面的脚本供您参考(我仅限于一个索引示例)。

您可以提供的任何见解(即使只是一个词的意见)都是有价值的

-- The Matrix

CREATE TABLE [z005497].[tblMatrix](
    [as_of_dt] [datetime] NOT NULL,
    [dim_0001] [varchar](100) NULL,
    [dim_0002] [varchar](103) NULL,
    [dim_0003] [varchar](100) NULL,
    [dim_0004] [varchar](100) NULL,
    [dim_0005] [varchar](100) NULL,
    [dim_0006] [varchar](100) NULL,
    [dim_0007] [varchar](100) NULL,
    [dim_0008] [varchar](100) NULL,
    [dim_0009] [varchar](100) NULL,
    [dim_0010] [varchar](100) NULL,
    [dim_0011] [varchar](100) NULL,
    [dim_0012] [varchar](100) NULL,
    [dim_0013] [varchar](100) NULL,
    [dim_0014] [varchar](100) NULL,
    [dim_0015] [varchar](100) NULL,
    [dim_0016] [varchar](100) NULL,
    [dim_0017] [varchar](103) NULL,
    [dim_0018] [varchar](103) NULL,
    [dim_0019] [varchar](103) NULL,
    [dim_0020] [varchar](103) NULL,
    [dim_0021] [varchar](103) NULL,
    [dim_0022] [varchar](103) NULL,
    [dim_0023] [varchar](103) NULL,
    [dim_0024] [varchar](103) NULL,
    [dim_0025] [varchar](103) NULL,
    [dim_0026] [varchar](11) NULL,
    [dim_0027] [varchar](11) NULL,
    [dim_0028] [varchar](11) NULL,
    [dim_0029] [varchar](11) NULL,
    [dim_0030] [varchar](11) NULL,
    [dim_0031] [varchar](11) NULL,
    [dim_0032] [varchar](11) NULL,
    [dim_0033] [varchar](11) NULL,
    [dim_0034] [varchar](11) NULL,
    [dim_0035] [varchar](11) NULL,
    [dim_0036] [varchar](11) NULL,
    [dim_0037] [varchar](11) NULL,
    [dim_0038] [varchar](11) NULL,
    [dim_0039] [varchar](11) NULL,
    [dim_0040] [varchar](11) NULL,
    [dim_0041] [varchar](11) NULL,
    [dim_0042] [varchar](11) NULL,
    [dim_0043] [varchar](11) NULL,
    [dim_0044] [varchar](11) NULL,
    [dim_0045] [varchar](11) NULL,
    [dim_0046] [varchar](11) NULL,
    [dim_0047] [varchar](11) NULL,
    [dim_0048] [varchar](11) NULL,
    [dim_0049] [varchar](11) NULL,
    [dim_0050] [varchar](11) NULL,
    [dim_0051] [varchar](11) NULL,
    [dim_0052] [varchar](11) NULL,
    [dim_0053] [varchar](11) NULL,
    [dim_0054] [varchar](5) NULL,
    [dim_0055] [varchar](5) NULL,
    [dim_0056] [varchar](5) NULL,
    [dim_0057] [varchar](5) NULL,
    [dim_0058] [varchar](5) NULL,
    [dim_0059] [varchar](5) NULL,
    [dim_0060] [varchar](5) NULL,
    [dim_0061] [varchar](5) NULL,
    [dim_0062] [varchar](5) NULL,
    [dim_0063] [varchar](5) NULL,
    [dim_0064] [varchar](5) NULL,
    [dim_0065] [varchar](5) NULL,
    [dim_0066] [varchar](5) NULL,
    [dim_0067] [varchar](5) NULL,
    [dim_0068] [varchar](5) NULL,
    [dim_0069] [varchar](5) NULL,
    [dim_0070] [varchar](5) NULL,
    [dim_0071] [varchar](5) NULL,
    [dim_0072] [varchar](5) NULL,
    [dim_0073] [varchar](5) NULL,
    [dim_0074] [varchar](5) NULL,
    [dim_0075] [varchar](5) NULL,
    [dim_0076] [varchar](5) NULL,
    [dim_0077] [varchar](5) NULL,
    [dim_0078] [varchar](5) NULL,
    [dim_0079] [varchar](5) NULL,
    [dim_0080] [varchar](5) NULL,
    [dim_0081] [varchar](5) NULL,
    [dim_0082] [varchar](5) NULL,
    [dim_0083] [varchar](5) NULL,
    [dim_0084] [int] NULL,
    [dim_0085] [int] NULL,
    [dim_0086] [int] NULL,
    [dim_0087] [int] NULL,
    [dim_0088] [int] NULL,
    [dim_0089] [int] NULL,
    [dim_0090] [int] NULL,
    [dim_0091] [int] NULL,
    [dim_0092] [int] NULL,
    [dim_0093] [int] NULL,
    [dim_0094] [varchar](12) NULL,
    [dim_0095] [varchar](12) NULL,
    [dim_0096] [varchar](12) NULL,
    [dim_0097] [varchar](120) NULL,
    [dim_0098] [varchar](120) NULL,
    [dim_0099] [varchar](120) NULL,
    [dim_0100] [numeric](20, 0) NULL,
    [dim_0101] [varchar](20) NULL,
    [dim_0102] [varchar](20) NULL,
    [dim_0103] [varchar](20) NULL,
    [dim_0104] [varchar](20) NULL,
    [dim_0105] [varchar](20) NULL,
    [dim_0106] [varchar](20) NULL,
    [dim_0107] [varchar](20) NULL,
    [dim_0108] [varchar](20) NULL,
    [dim_0109] [varchar](20) NULL,
    [dim_0110] [varchar](20) NULL,
    [dim_0111] [varchar](20) NULL,
    [dim_0112] [varchar](20) NULL,
    [dim_0113] [varchar](20) NULL,
    [dim_0114] [varchar](20) NULL,
    [dim_0115] [varchar](20) NULL,
    [dim_0116] [varchar](20) NULL,
    [dim_0117] [varchar](20) NULL,
    [dim_0118] [varchar](20) NULL,
    [dim_0119] [varchar](20) NULL,
    [dim_0120] [varchar](20) NULL,
    [lastLoad] [datetime] NULL
) ON [PRIMARY]



-- Index example

CREATE NONCLUSTERED INDEX [idx_dim_0001 (not unique)] ON [z005497].[tblMatrix] 
(
    [dim_0001] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]


-- The configuration table from which developers would find out what is in the Matrix

CREATE TABLE [z005497].[tblMatrixCfg](
    [dimId] [int] IDENTITY(100000,1) NOT NULL,
    [colName] [varchar](25) NOT NULL,
    [dataType] [varchar](25) NOT NULL,
    [dimName] [varchar](25) NOT NULL,
    [dimDesc] [varchar](500) NOT NULL,
    [dimpath] [varchar](5000) NOT NULL,
    [loadDate] [datetime] NOT NULL,
    [modUser] [varchar](100) NOT NULL,
    [modDate] [datetime] NOT NULL,
 CONSTRAINT [PK_tblMatrixCfg_1] PRIMARY KEY CLUSTERED 
(
    [dimId] ASC,
    [colName] ASC,
    [dimName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

【问题讨论】:

Not exactly your matrix model but has some similarities and should be a cautionary tale! 感谢 Martin...与我们在此构建中所经历的事情有很多相似之处。这句话总结了它......“Vision 系统的最佳质量和灵活性对于 Upstart 作为一家公司来说是最不重要的,尽管它对 Upstart 的 IT 部门很有吸引力。它足够可靠,但它没有足够的速度来实现它的目标。业务需求。这是一个典型的例子,说明当 IT“部门”的优先级与“业务”的优先级不匹配或未适当对齐时会发生什么。 【参考方案1】:

如果可以的话,杀了它。

此外,该开发人员需要更多经验。他/她应该在另一家公司得到它。

它基本上违反了很多我不知道从哪里开始的东西。

即使你最终与一个高度规范化的模型作斗争,它盲目地遵循某人的最佳实践,它也无法与这种设计将造成的灾难相提并论。

【讨论】:

感谢 Cade,感谢您的洞察力...我是 *** 新手...现在查看您的问题,看看我是否可以增加任何价值 通用字段名称作为一项功能出售,但似乎有点矫枉过正,因为一旦进程依赖于某个字段,它无论如何都无法动态更改(不更改进程) @santiago_jon 我什至不想讨论这个开发人员会为基于这种数据库架构的应用程序的生命周期推荐哪些实践——你会看到这样的东西偶尔会被人津津乐道,而且总是以泪水告终。像这样的东西和 EAV 模型(它们确实在特殊子系统中占有一席之地)往往会被想要编写“最后一个数据库”的开发人员所吸引 - en.wikipedia.org/wiki/The_Last_One_(software) 一定要尽快干掉这个。它的设计很糟糕,我可以说推动这个的dba没有太多经验。每列一个索引几乎没有用,并且会破坏任何写入性能。看起来 EAV 设计出了问题,我可以看到这迟早会成为一个问题。【参考方案2】:

仅举一个例子来说明 Cade 的“我不知道从哪里开始”的含义:

“今天列 dim_0001 是客户名称,但明天可能是其他名称”

这通常也意味着在用户接受系统中,dim_0001 可以是客户姓名(系统可能似乎工作并被接受),然后您进入生产阶段,dim_0001 成为总裁妻子的姓名或因此,需要花费数小时的会议时间来试图找出 (a) 问题出在哪里,以及 (b) 如何在尽可能短的时间内解决问题。

( (b) 通常相当于用诸如“如果 col_name = dim_0001 则不要将其视为矩阵所说的内容,而是将其视为此处硬编码的内容”之类的东西来修补代码。)

【讨论】:

谢谢欧文。在与开发人员交谈时,他提到通用命名列的想法部分是通过查看我们的几个配置表而产生的。例如:可以在处理结果汇总报告中提供业务用户友好的方法描述的表格。在其中一些表格中,我们有少量的“以防万一”列,当业务方面的重要人物跳起来说“嘿,我现在就这样”时,这些列已准备就绪。示例:“AltDesc1”(备用描述备用 1)和“AltDesc2”(备用描述 2)。 当被问到时,开发人员参考了这篇 Wikipedia 文章(当然有 Weasel Words 标签):en.wikipedia.org/wiki/Enterprise_bus_matrix 那篇文章在我和我的眼里听起来很大程度上是理论上的,他最终得到了矩阵并不是作者真正描述的那样。 @santiago_jon 在数据仓库中,它是开发逻辑模型的工具。在任何情况下,数据仓库通常不用于事务数据。以维度形式对某些事物进行建模可能有一些好处,但它解决了非常具体的问题,通常用于临时查询和分析。该模型不是维度模型,也不对应于典型的归一化模型——唯一的先例是同样构思错误的想法。【参考方案3】:

“矩阵有什么用?”

好吧,我当然不明白。

我以前从未见过这样的事情,我不明白它是如何被使用的,或者索引是如何加速任何事情的,或者如何在不使用至少自连接的情况下查询这个表。

如果你愿意,可以叫我新手,但这对我来说是第一次。我认为,如果这是做事的方式,数据库供应商不应该付出太多努力来允许我们开发人员定义具有不同数据类型的列和关系的表。

【讨论】:

谢谢米凯尔。在这一点上,我处于损害控制模式,希望能从他的工作中拯救我的东西:到目前为止,他已经投入了大约 40 个小时的公司时间。这让我想知道他可以用这 40 个小时写多少个连接。在我们的商店大概三个月的价值。这对他来说是这个任务结束时的圣杯:消除编写连接的需要。我仍然看不到矩阵将如何做到这一点:当您的 WHERE 子句需要基于矩阵中未找到的字段的标准时会发生什么?无论如何,您不只需要通过联接引入这些表吗?好像是这样。 @santiago_jon 算自己幸运,他只有 40 个小时。 同意。我们关心这个人,并希望他成功。我计划今天与他会面并查看同行评审结果。感觉我需要稍微缩短约束力,并坚持让他继续专注于解决当前的业务需求。祝我好运!【参考方案4】:

这是试图将面向对象范式填充到关系系统中的结果。 Document databases 允许这种编程:

面向文档的数据库中的文档是相似的,在某些方面 关系数据库中记录或行的方式,但它们较少 死板的。他们不需要遵守标准模式,也不会 它们具有所有相同的部分、插槽、部件、键等。为了 下面是一个文档示例:

FirstName="Bob", Address="5 Oak St.", Hobby="sailing".

另一个文档可能是:

FirstName="Jonathan", Address="15 Wanamassa Point Road", Children=[Name:"Michael",Age:10, Name:"Jennifer", Age:8,
Name:"Samantha", Age:5, Name:"Elena", Age:2].

这两个文档有一些相似的信息和一些不同的信息。 与每条记录具有相同集合的关系数据库不同 的字段和未使用的字段可能保持为空,没有空的 在这种情况下,任一文档(记录)中的“字段”。该系统允许 要添加的新信息,不需要明确说明 如果遗漏了其他信息。

试图在关系数据库中使用这种范例是一个“方钉圆孔”的问题。文档数据库可能非常适合高度事务性系统,但将事务性数据加载到数据仓库中的各种事实表中会更好地进行分析。

【讨论】:

以上是关于SQL Server 2008 - 过多的非规范化和过度索引:矩阵有啥用?的主要内容,如果未能解决你的问题,请参考以下文章

使用 SQL Server 进行实时聚合和非规范化的架构推荐

在 SQL Server 中调整大型查询

sql server2008里面的image类型,怎么向里面放图片啊,还有java里面如何把图片读出来

sqlserver (是标识) 不能修改

怎么修改SQL SERVER2008的select产生的临时结果存放的位置?每次查询行数较多的结果都会把C盘占满。

SQL Server 使用没有主键的聚集索引创建表