4

我在做什么

我正在创建一个 SQL 表,它将为复杂类型的对象提供后端存储机制。我正在尝试确定如何以最佳性能实现这一目标。我需要能够查询复杂类型的每个单独的简单类型值(例如,地址复杂类型中城市的字符串值)。

我原本以为我可以将复杂类型值作为 XML 存储在一条记录中,但现在我担心这种设计的搜索性能。 我需要能够在不改变数据库访问层的任何内容的情况下即时创建变量模式


我现在在哪里

现在我正在考虑创建以下表格。

 TABLE:  Schemas
   COLUMN NAME       DATA TYPE
   SchemaId          uniqueidentifier
   Xsd               xml                 //contains the schema for the document of the given complex type
   DeserializeType   varchar(200)        //The Full Type name of the C# class to which the document deserializes.

 TABLE:  Documents
   COLUMN NAME       DATA TYPE      
   DocumentId        uniqueidentifier
   SchemaId          uniqueidentifier

 TABLE:  Values                            //The DocumentId+ValueXPath function as a PK
   COLUMN NAME       DATA TYPE      
   DocumentId        uniqueidentifier
   ValueXPath        varchar(250)
   Value             text

从这些表中,在执行查询时,我会在值表上执行一系列自联接。当我想通过 DocumentId 获取整个对象时,我将有一个通用脚本来创建一个模拟复杂类型的非规范化数据表的视图。


我想知道的

我相信有更好的方法来完成我想要做的事情,但我对不同 SQL 技术的相对性能优势有点太无知了。具体来说,我不知道以下性能成本:

1 - comparing the value of a text field versus of a varchar field.
2 - different kind of joins versus nested queries
3 - getting a view versus an xml document from the sql db
4 - doing some other things that I don't even know I don't know would be affecting my query but, I am experienced enough to know exist

我将不胜感激有关 sql 中这些性能问题的任何信息或资源,以及有关如何以更有效的方式解决此一般问题的建议。


例如,

这是我目前计划做的一个例子。

我有一个 C# 类地址,看起来像

public class Address{
     string Line1 {get;set;}
     string Line2 {get;set;}
     string City {get;set;}
     string State {get;set;}
     string Zip {get;set;
}

一个实例是由new Address{Line1="17 Mulberry Street", Line2="Apt C", City="New York", State="NY", Zip="10001"}

它的 XML 值看起来像。

<Address>
   <Line1>17 Mulberry Street</Line1>
   <Line2>Apt C</Line2>
   <City>New York</City>
   <State>NY</State>
   <Zip>10001</Zip>
</Address>

使用上面的 db-schema,我将在 Schemas 表中有一条记录,其中包含地址 xml 模式的 XSD 定义。该实例将具有一个唯一标识符(Documents 表的 PK),该标识符分配给 Schemas 表中 Address 记录的 SchemaId。然后在 Values 表中将有五个记录来表示此地址。

它们看起来像:

DocumentId                              ValueXPath        Value
82415E8A-8D95-4bb3-9E5C-AA4365850C70    /Address/Line1    17 Mulberry Street
82415E8A-8D95-4bb3-9E5C-AA4365850C70    /Address/Line2    Apt C
82415E8A-8D95-4bb3-9E5C-AA4365850C70    /Address/City     New York
82415E8A-8D95-4bb3-9E5C-AA4365850C70    /Address/State    NY
82415E8A-8D95-4bb3-9E5C-AA4365850C70    /Address/Zip      10001

刚刚添加了一个赏金...

我的目标是获得我需要的资源,以便为我的应用程序提供一个完全可搜索的数据访问层,并具有从应用程序层生成的数据模式,不需要直接的数据库配置(即创建一个新的 SQL 表)为了向域模型添加一个新的聚合根。

我对使用除 SQL 之外的 .NET 兼容技术的可能性持开放态度,但我要求任何此类建议都得到充分证实才能考虑。

4

5 回答 5

3

在架构级别寻找解决方案怎么样?在我发现CQRS之前,我也对复杂的图表和性能感到头疼。

【开启布道模式】

  • 您可以使用基于文档或关系的存储。甚至两者都有!(事件溯源)
  • 很好的关注点分离:读取模型与写入模型
  • 有你的蛋糕,也吃吧!

好的,有一个初步的学习/技术曲线需要克服;)

[结束布道者模式]

正如您所说:“我需要能够在不更改数据库访问层的任何内容的情况下即时创建可变模式。 ”主要好处是您的读取模型可以非常快,因为它是为读取而设计的。如果您将事件溯源添加到组合中,您可以将您的读取模型删除并重建为您想要的任何模式......甚至“在线”。

有一些不错的开源框架,例如 nServiceBus,可以节省大量时间和技术挑战。一切都取决于你想把这些概念带到多远,你愿意/可以花时间在什么上面。如果您遵循 Greg Young 的方法,您甚至可以从基础开始。请参阅以下链接中的信息。

于 2011-03-11T10:43:30.423 回答
2

不知何故,你想要的在 SQL 中听起来像是一件痛苦的事情。基本上,您应该将文本字段的内部视为不透明,就像查询 SQL 数据库时一样。没有为有效查询创建文本字段。

如果您只想序列化的对象存储在文本字段中,那很好。但不要尝试构建在文本字段内部查找对象的查询。

您的想法听起来像是您想要执行一些连接、XML 解析和 XPath 应用程序来获得一个值。我觉得这不是最有效的事情。

所以,我的建议:

附录,根据您上面的解释

简单地说,不要过度使用这个东西:

  • 如果您只想持久化 C#/.NET 对象,只需使用框架中已经内置的 XML 序列化,一个表即可。
  • 如果您出于某种原因需要存储复杂的 XML,请使用专用的 XML 存储
  • 如果您有一个固定的数据库架构,但它对于高效查询来说太复杂了,请使用内存中的文档存储,您可以在其中保留数据的非规范化版本以加快查询速度(或者只是简化您的数据库架构)
  • 如果你真的不需要一个固定的模式,只使用一个文档存储,而根本忘记有任何“模式定义”

至于您的解决方案,是的,它可以以某种方式工作。如果设置正确,则可以使用纯 SQL 模式。但是对于应用 XPath,您可能会在每次访问记录时解析整个 XML 文档,这在开始时效率不高。

如果您想查看文档数据库,可以使用CouchDBMongoDB的 .NET 驱动程序。eXist XML 数据库提供了许多 Web 协议,您可以使用 VisualStudio 的傻瓜式界面轻松创建客户端类。或者只是用谷歌搜索已经做过的人。

于 2011-02-23T14:34:30.950 回答
1

我需要能够在不改变数据库访问层的任何内容的情况下动态创建变量模式。

您正在 RDBMS 中重新实现 RDBMS。数据库已经可以做到这一点——这就是 DDL 语句喜欢create tablecreate schema适用的......

我建议您研究“模式”和 SQL 安全性。正确的安全设置没有理由不允许您的用户创建自己的表来存储文档属性,甚至自动生成它们。

编辑: 稍微长一点的答案,如果您没有立即满足全部要求,我会将数据存储为 XML 数据类型,并使用 XPath 查询对其进行查询。这对于偶尔查询少量行(当然少于几千行)是可以的。

此外,您的 RDBMS 可能支持基于 XML 的索引,这可能是解决问题的另一种方法。CREATE XML INDEX例如在 SqlServer 2008 中。

但是对于频繁查询,您可以使用触发器或物化视图以表格格式创建相关数据的副本,因此可以通过查询细分表来加速更密集的报告。

我不知道您的要求,但如果您负责自己创建报告/查询,这可能是一种使用方法。如果您需要让用户创建他们自己的报告,那是一座需要攀登的大山。

我想我的意思是“你确定你需要这样做,而 XML 不能只是完成这项工作”。

于 2011-03-10T18:15:06.007 回答
0

有趣的问题。

我想你可能在这里问错了问题。一般来说,只要你的文本字段上有一个 FULLTEXT 索引,查询就会很快。例如,如果您必须使用通配符,则比 varchar 快得多。

但是,如果我是您,我会专注于您将要运行的实际查询。你需要布尔运算符吗?通配符?数值比较?这就是我认为你会遇到真正的性能担忧的地方。

我想你会需要这样的查询:

  • “查找纽约州、新泽西州和宾夕法尼亚州的所有地址”
  • “查找 Mulberry 街 1 号和 100 号门牌之间的所有地址”
  • “找到所有缺少邮政编码的地址,并且城市是纽约”

在较高级别上,您建议的解决方案是将 XML 存储在某处,然后将该 XML 反规范化为名称/值对以进行查询。

名称/值对有着悠久而引以为豪的历史,但在复杂的查询情况下变得笨拙,因为您没有使用关系数据库模型的内置优化和概念。

我建议的一些改进是查看域模型,至少看看您是否可以将单独的数据类型分解到“值”列中;您最终可能会得到“textValue”、“moneyValue”、“integerValue”和“dateValue”。在您给出的示例中,您可能会将“地址 1”分解为“门牌号”(作为整数)和“街道名称”。

说了这么多——我认为除了完全改变以文档为中心的数据库之外,没有更好的解决方案。

于 2011-03-09T11:36:32.667 回答
0

部分取决于您的数据库引擎。您使用的是 SQL Server,不是吗?

回答你的话题:

1 - 比较文本字段与 varchar 字段的值:如果您要比较两个 db 字段,则 varchar 字段更智能。Nvarchar(max) 以 2*l+2 字节的 unicode 存储数据,其中“l”是长度。对于性能问题,您需要考虑表有多大,以便选择索引(或不索引)表字段的最佳方式。请参阅主题

2 - 有时嵌套查询很容易创建和执行,也可以作为减少查询时间的一种方式。但是,根据复杂性,最好使用不同类型的连接。最好的办法是双管齐下。每个查询执行两次或更多次,因为数据库引擎在第一次执行时“编译”一个查询,然后后续会更快。测量不同参数的时间并选择最佳选项。

“有时您可以重写子查询以使用 JOIN 并获得更好的性能。创建 JOIN 的优点是您可以按照与查询定义的顺序不同的顺序评估表。使用子查询的优点是它经常不必须扫描子查询中的所有行以评估子查询表达式。例如,EXISTS 子查询可以在看到第一个符合条件的行时返回 TRUE。-链接

3- 这个问题没有太多信息,但是如果您将直接从表中获取 xml 文档,那么插入视图将是一个好主意。同样,这将取决于视图和文档。

4- 其他问题是关于您的表的预期总记录;列的索引,您需要考虑排序、连接、过滤、PK 和 FK。每种情况都可能需要不同的方法。我的建议是花一些时间阅读有关您的数据库引擎和查询功能以及与您的系统相关的信息。

我希望我有所帮助。

于 2011-02-23T15:18:32.800 回答