iCo*_*fee 10 sql sql-server join normalization sql-server-2008
假设我有一个主表,它有100列引用(作为外键)到100个表(包含主键).
整套信息需要加入这100张桌子.加入如此多的表肯定是一个性能问题.希望我们可以期望任何用户都希望在查询中请求包含来自不超过5-7个表(在这100个表中)中的值的一堆数据,这些查询将条件(在查询的WHERE部分中)放在大约3-4张桌子(其中100张).不同的查询有用于生产表的不同组合的"选择"查询的一部分,并把条件"WHERE".但是,同样,每个SELECT都需要5-7个表,每个WHERE都需要3-4个表(当然,用于生成SELECT的表列表可能与用于在WHERE中放置条件的表列表重叠).
我可以编写一个VIEW,底层代码加入所有这100个表.然后我可以将上面提到的SQL查询写到这个VIEW.但在这种情况下,对我来说如何指示SQL Server(尽管代码中的明确指令连接所有这100个表)只有大约11个表应该连接(11个表足以连接以生成SELECT)是一个很大的问题.结果并考虑到WHERE条件).
另一种方法可以是创建转换以下"假"代码的"特征"
SELECT field1, field2, field3 FROM TheFakeTable WHERE field1=12 and field4=5
Run Code Online (Sandbox Code Playgroud)
进入以下"真实"代码:
SELECT T1.field1, T2.field2, T3.field3 FROM TheRealMainTable
join T1 on ....
join T2 on ....
join T3 on ....
join T4 on ....
WHERE T1.field1=12 and T4.field4=5
Run Code Online (Sandbox Code Playgroud)
从语法的角度来看,即使允许这种"TheFakeTable-mechanism"与真实表格和结构的任何混合组合也不是问题.这里真正的问题是如何在技术上实现这个"特征".我可以创建一个函数,它将"假"代码作为输入并生成"真实"代码.但是,因为它需要使用动态SQL工具evrywhere其中这个"TheFakeTable机制"似乎也不是很方便.幻想土地解决方案是在我的Management Studio中扩展SQL语言的语法,以允许编写这样的假代码,然后在发送到服务器之前自动将此代码转换为真实代码.
我的问题是:
感谢大家的每一条评论!
PS具有100个表格的结构来自我在这里提出的以下问题: 规范化一个非常大的表格
小智 16
SQL Server优化器确实包含删除冗余连接的逻辑,但是存在限制,并且连接必须是多余的.总而言之,连接可以有四种效果:
NULL
s(用于RIGHT
或FULL JOIN
)要成功删除冗余连接,查询(或视图)必须考虑所有四种可能性.如果这样做,正确的话,效果可能会令人惊讶.例如:
USE AdventureWorks2012;
GO
CREATE VIEW dbo.ComplexView
AS
SELECT
pc.ProductCategoryID, pc.Name AS CatName,
ps.ProductSubcategoryID, ps.Name AS SubCatName,
p.ProductID, p.Name AS ProductName,
p.Color, p.ListPrice, p.ReorderPoint,
pm.Name AS ModelName, pm.ModifiedDate
FROM Production.ProductCategory AS pc
FULL JOIN Production.ProductSubcategory AS ps ON
ps.ProductCategoryID = pc.ProductCategoryID
FULL JOIN Production.Product AS p ON
p.ProductSubcategoryID = ps.ProductSubcategoryID
FULL JOIN Production.ProductModel AS pm ON
pm.ProductModelID = p.ProductModelID
Run Code Online (Sandbox Code Playgroud)
优化器可以成功简化以下查询:
SELECT
c.ProductID,
c.ProductName
FROM dbo.ComplexView AS c
WHERE
c.ProductName LIKE N'G%';
Run Code Online (Sandbox Code Playgroud)
至:
Rob Farley在最初的MVP Deep Dives一书中深入探讨了这些想法,并且有一篇关于他在SQLBits 上发表主题的录音.
主要限制是外键关系必须基于单个键以促进简化过程,并且针对此类视图的查询的编译时间可能变得非常长,特别是当连接数增加时.编写一个100表视图可以让所有语义完全正确,这可能是一个很大的挑战.我倾向于找到一种替代解决方案,可能使用动态SQL.
也就是说,非规范化表的特殊性质可能意味着视图组装起来非常简单,只需要强制执行FOREIGN KEYs
非强制NULL
引用列,并且需要适当的UNIQUE
约束来使此解决方案按照您的希望工作,而不需要100个物理连接运算符的开销在计划中.
使用十个表而不是一百个:
-- Referenced tables
CREATE TABLE dbo.Ref01 (col01 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref02 (col02 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref03 (col03 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref04 (col04 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref05 (col05 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref06 (col06 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref07 (col07 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref08 (col08 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref09 (col09 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref10 (col10 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
Run Code Online (Sandbox Code Playgroud)
父表定义(带页面压缩):
CREATE TABLE dbo.Normalized
(
pk integer IDENTITY NOT NULL,
col01 tinyint NOT NULL REFERENCES dbo.Ref01,
col02 tinyint NOT NULL REFERENCES dbo.Ref02,
col03 tinyint NOT NULL REFERENCES dbo.Ref03,
col04 tinyint NOT NULL REFERENCES dbo.Ref04,
col05 tinyint NOT NULL REFERENCES dbo.Ref05,
col06 tinyint NOT NULL REFERENCES dbo.Ref06,
col07 tinyint NOT NULL REFERENCES dbo.Ref07,
col08 tinyint NOT NULL REFERENCES dbo.Ref08,
col09 tinyint NOT NULL REFERENCES dbo.Ref09,
col10 tinyint NOT NULL REFERENCES dbo.Ref10,
CONSTRAINT PK_Normalized
PRIMARY KEY CLUSTERED (pk)
WITH (DATA_COMPRESSION = PAGE)
);
Run Code Online (Sandbox Code Playgroud)
风景:
CREATE VIEW dbo.Denormalized
WITH SCHEMABINDING AS
SELECT
item01 = r01.item,
item02 = r02.item,
item03 = r03.item,
item04 = r04.item,
item05 = r05.item,
item06 = r06.item,
item07 = r07.item,
item08 = r08.item,
item09 = r09.item,
item10 = r10.item
FROM dbo.Normalized AS n
JOIN dbo.Ref01 AS r01 ON r01.col01 = n.col01
JOIN dbo.Ref02 AS r02 ON r02.col02 = n.col02
JOIN dbo.Ref03 AS r03 ON r03.col03 = n.col03
JOIN dbo.Ref04 AS r04 ON r04.col04 = n.col04
JOIN dbo.Ref05 AS r05 ON r05.col05 = n.col05
JOIN dbo.Ref06 AS r06 ON r06.col06 = n.col06
JOIN dbo.Ref07 AS r07 ON r07.col07 = n.col07
JOIN dbo.Ref08 AS r08 ON r08.col08 = n.col08
JOIN dbo.Ref09 AS r09 ON r09.col09 = n.col09
JOIN dbo.Ref10 AS r10 ON r10.col10 = n.col10;
Run Code Online (Sandbox Code Playgroud)
破解统计信息以使优化器认为表非常大:
UPDATE STATISTICS dbo.Normalized WITH ROWCOUNT = 100000000, PAGECOUNT = 5000000;
Run Code Online (Sandbox Code Playgroud)
示例用户查询:
SELECT
d.item06,
d.item07
FROM dbo.Denormalized AS d
WHERE
d.item08 = 'Banana'
AND d.item01 = 'Green';
Run Code Online (Sandbox Code Playgroud)
给我们这个执行计划:
Normalized表的扫描看起来很糟糕,但是两个Bloom过滤器位图都是在存储引擎的扫描过程中应用的(因此无法匹配的行甚至不会显示到查询处理器).这可能足以在您的情况下提供可接受的性能,当然比使用溢出列扫描原始表更好.
如果您能够在某个阶段升级到SQL Server 2012 Enterprise,则还有另一种选择:在规范化表上创建列存储索引:
CREATE NONCLUSTERED COLUMNSTORE INDEX cs
ON dbo.Normalized (col01,col02,col03,col04,col05,col06,col07,col08,col09,col10);
Run Code Online (Sandbox Code Playgroud)
执行计划是:
这可能看起来更糟糕,但列存储提供了出色的压缩,整个执行计划在批处理模式下运行,并为所有贡献列提供过滤器.如果服务器有足够的线程和内存可用,那么这种替代方案可能真的会飞.
最后,考虑到表的数量以及获得糟糕的执行计划或需要过多编译时间的可能性,我不确定这种规范化是否正确.我可能会首先纠正非规范化表的模式(正确的数据类型等),可能应用数据压缩...通常的事情.
如果数据真正属于星型模式,则可能需要更多的设计工作,而不仅仅是将重复数据元素拆分为单独的表.
归档时间: |
|
查看次数: |
7525 次 |
最近记录: |