2

我目前正在为一个网站的想法弄乱一些东西 - 我非常想让我的用户创建保存数据的“表”,然后允许他们查询这些数据(以一种比写起来更令人讨厌的方式SQL 查询,希望比使用 excel 更容易)。

到目前为止,我的想法是在我的数据库中使用几个表来表示这一点 - 一个表表示一个表,一个表表示表的列,一个表表示表中的每一行,最后一个表表示值. 类似于(伪 SQL)的东西:

CREATE TABLE 'Tables' (
   Id INT NOT NULL PRIMARY KEY,
   NAME VARCHAR(255)
)

CREATE TABLE 'TableColumns' (
   Id INT NOT NULL PRIMARY KEY,
   TableId INT NOT NULL FOREIGN KEY ON 'Tables',
   NAME VARCHAR(255)
)

CREATE TABLE 'TableRows' (
   Id INT NOT NULL PRIMARY KEY,
   TableId INT NOT NULL FOREIGN KEY ON 'Tables',
   RowNumber INT NOT NULL
)

CREATE TABLE 'TableValues' (
   RowId INT NOT NULL PRIMARY KEY,
   ColumnId INT NOT NULL PRIMARY KEY,
   Value VARCHAR(255)
)

(请注意,TableValues 表在这里有 2 个主键字段,它应该代表一个“复合”主键,不要太在意我的语法不是合法的 SQL,它只是应该显示这个想法)。

我对此进行了一些测试,并能够成功地进行简单的查询(简单的过滤、排序等)。我这样做的方法是首先查询 TableRows 表 - 为了进行过滤,我然后过滤掉了列与条件不匹配的行,并且为了排序,我根据列的内容对 RowIds 进行了排序(由指定的排序指定)。生成按所需顺序排列的行 ID 列表,从这里开始只是选择需要的内容。

所有这一切都很好,但从这里开始我有点卡住了。我希望能够以某种方式表示不同的数据类型(这确实是我的主要问题),然后再研究如何进行连接。

在考虑所有这些时,我开始怀疑是否有更好的方法来做到这一点。请注意,这里的性能当然是一个因素,但我不打算支持具有数十万行的虚拟表,每个虚拟表可能大约 1000 行 - 当然整个系统需要能够处理许多这些。

我知道我总是可以在我的数据库中实际创建表,并在 C# 中动态创建查询来完成此操作,同样使用 SQL 查询进行查询 - 但是我从来都不喜欢让用户对我的数据库“构建”查询像这样 - 在我看来,这似乎会导致出现许多错误的路径 - 在最坏的情况下最终允许用户以一种或另一种方式杀死数据库。

此外,我的问题变成了如何以从 C# 角度来看有意义的方式来处理这个问题。到目前为止,我认为我倾向于使用 LINQ,然后创建自己的扩展方法来应用所需的功能——即扩展 IQueryable 的 ExtensionMethods。

所以我真正想要的是一些关于如何做到这一点的想法,关于如何调整性能的想法,关于如何处理表中单独数据类型的想法(当然将类型存储在 tablecolumn 中,但是如何实际存储该值,以便我可以按它过滤、排序等?- 不只是在我的 tablevalues 表上添加“TextValue”、“MoneyValue”等列)。最后但并非最不重要的一点是,希望在这里进行一些很好的讨论——我至少认为这是一个有趣的话题。

4

9 回答 9

9

出于某种原因,每个人都会在某个时候遇到这个想法。

这似乎是对的,它应该工作。

它会。有点。

关于 TheDailyWTF 的评论是有道理的。在 DBMS 之上重新实现 DBMS 确实不是一个好主意。像这样的元数据会给你

  • 表现不佳的系统
  • 维护的噩梦

如果您真的需要这种灵活性(是吗?),您最好花时间实现允许您将元数据存储在某些表中并为数据库中的实际表生成模式的层。

我知道这种系统有几个例子:

我相信还有其他人。

这种设计的好处是您的数据库最终实际上是有意义的,并且它使用 RDBMS 来发挥其优势。此外,由于一旦创建表后这种配置不应该一直发生,它允许用户在需要时微调数据库(主要在索引方面)。

对于您提出的那种系统,我真的强烈认为唯一正确的答案是don't

于 2009-05-28T16:48:23.637 回答
6

这是一个有趣的想法,但是随着时间的推移,以这种方式使用 SQL 可能会非常痛苦。
如果我理解正确,您希望用户能够定义数据结构,然后将数据保存到这些结构中。您还希望能够查询它。我认为可能还有其他几种方法可以解决这个问题;

  • 使用 XML 怎么样?允许每个用户为每个“表”存储一个 XML 文件,并且只维护它的模式。每个“行”都是一个带有子元素的 XML 元素。您可以选择将 XML 粘贴在 SQL 中,或者仅通过其他方式存储它。这不适用于大型数据集,但对于数千条记录来说,速度非常快;我在 C# 中使用 20+MB XML 文件进行了一些测试,并且能够在不到 1 秒的时间内创建、读取和解析它们。使用 LINQ to XML,您甚至可以构建相当复杂的查询和连接。我不会将 XML 用于大型企业系统,但您会惊讶于它在具有大量内存和快速处理器的现代机器上能走多远——而且它具有无限的灵活性。
  • 你能改用面向对象的数据库(马蒂斯等)吗?我自己没有这些经验,但我认为你可以很容易地做一些类似于 XML 方法但性能更好的事情。
  • Amazon Simple DB:如果我没记错的话,这本质上是一个您可以使用的基于名称/值对的数据库。您的应用程序是否可以在后台使用它来避免您不得不处理所有管道?如果您必须为 SQL Server 付费,那么 Amazon DB 可能更便宜,并且有能力进行大规模扩展,但没有关系查询之类的东西。
于 2009-05-28T16:48:53.480 回答
3

您在原始问题中显示的设计是实体-属性-值设计的变体。

一些人表达的不耐烦可能是因为几乎每个数据库开发人员都“发现”了这种设计,并试图将它用于你正在使用它的目的——一个支持可扩展数据库的灵活系统,而不会带来不便使用CREATE TABLEALTER TABLE

但是EAV有很多缺点。这只是一个:您如何使任何给定的列成为强制性的(相当于NOT NULL约束)?

关系数据库假定您预先知道表,并且您可以定义它们。RDBMS 不是用于完全动态关系或具有完全可变属性集的关系的正确工具。为此还有许多其他技术,例如 XML、RDFCouchDB

另请参阅我对“下一代数据库”的回答。

于 2009-05-28T17:31:41.647 回答
2

冒着大量反对票的风险,为什么不让他们安装 MS Access?

于 2009-05-28T16:27:31.683 回答
1

我在 Microsoft 的 Outlook Business Contact Manager 加载项中遇到了类似的方法。他们处理字段类型的方式是有一个表来定义每个字段的类型,然后他们将实际字段值存储在一个只包含 Varbinary 列的表中。与 varbinary 的转换由字段类型表控制。

于 2009-05-28T16:40:19.457 回答
1

一旦以非常通用且因此极其相似的方式存储数据,我就为系统做了一些事情。

系统写的少,读的多,而且为了取出数据而进行的复杂连接使系统变得很慢。

现在我知道这违背了所有数据库实践,但是,我对每个“表”的数据进行了非规范化处理,并在数据库中物理创建了一个名为 Table_1、Table_2 的表。

我基于 Tables 表上的触发器创建和删除了 Table_1 和 Table_2,我使用 TableColumns 表上的触发器向这些表添加和删除了列,使用 TableRows 表上的触发器插入和删除了行,并使用 TableValues 表上的触发器更新了值.

是的,正如预期的那样,我们的写入性能大大降低了,但我们的读取性能(这在当时非常重要)得到了极大的提升,因为我们可以直接从这些“非规范化”表中读取。

因此,基本上您实际上可以在示例中创建和删除表,并相应地添加和删除列。您可以创建您需要的实际数据类型的列,然后您可以在其中存储您想要的任何内容。

可能会使您编写的查询更容易,并且您只为您的用户查看一个表。

于 2009-05-28T16:50:23.197 回答
1

我不确定为什么所有的仇恨和没有人真正试图回答你的问题。即使它最终是一个“你将如何实施 Google Docs”的问题,它仍然是一个公平的问题。

假设你已经考虑过你是否真的想这样做(我假设所有的仇恨都是关于的),这至少是一个更好的主意:

DATA_TABLE {
    TABLEID INT,
    INT1 INT,
    INT2 INT,
    VARCHAR1 VARCHAR(255),
    ... etc
}

然后将元数据存储在某处,记录特定 TABLEID 的名称、使用的字段及其面向用户的名称等。连接很容易支持,因为它们只是数据表上的自连接。是否将所述元数据存储在数据库或其他位置取决于您。

这种方法可以很好地工作(我可以证明许多成功的 .com 网站都使用了这种模式,我参与过和/或认识的人都是),但为了获得最佳性能,我建议使用 CHAR 而不是 VARCHAR ,对齐你的元组,使它们适合页面,等等。

我认为这与 dsteele 的回答相当(非常)相似

于 2009-05-28T16:47:42.220 回答
0

SharePoint 在 SQL Server 之上执行此操作以及更多操作。我将研究 SharePoint 如何使用 SQL Server。

于 2009-05-28T18:00:48.907 回答
0

您肯定希望简化这一点 - 允许您的用户创建表,可能在 TEMPDB 中。只需将模式后缀添加到“CREATE TABLE”d 表中,您就可以避免让它们对 REAL 系统表进行扩展。使您也可以轻松过滤它们。问题是大多数数据库不允许非 dba 创建表,因此需要一些预先设置。您甚至可以专门为此在您的数据库中创建一个模式,并在您的数据库支持的情况下限制大小和表大小限制。

不要重新发明轮子。

这样做,您将自动获得连接、联合等以及与 Crystal Reports 和其他不需要深入了解您的特定模式的工具的接口。

于 2009-05-28T17:41:41.457 回答