【问题标题】:Database within a database (table design) [duplicate]数据库中的数据库(表设计)[重复]
【发布时间】:2012-08-21 07:39:27
【问题描述】:

可能重复:
Database design to create tables on the fly

我需要在数据库中创建一个数据库。这个问题与this 有关。我将尝试详细解释我正在尝试做的事情并提供代码示例。基本上,我希望能够在数据库中创建动态表。例如,我将有一个网页,允许用户使用列和数据创建自己的表。这是我想出的数据库设计:

aColumn
aDataType
aRow
aTable
zBit
zDateTime
zMoney
zNumber
zText

z 开头的表是特定数据进入的数据,例如整数、日期时间值等。aColumn 是属于特定表的列。 aRow 标识 aTable 中的特定行。这是数据库设计:

aTable: Id, name
aColumn: Id, Name, aTable, aDataType
aDataType: Id, Name
aRow: Id, aTable
zBit: Id, aRow, aColumn, Data(Bit)
zDateTime: Id, aRow, aColumn, Data (DateTime)
zMoney: Id, aRow, aColumn, Data (Money)
zNumber: Id, aRow, aColumn, Data (INT)
zText: Id, aRow, aColumn, Data (nvarchar(MAX))

这是我用来启动和运行它的一些示例数据:

表格

Id          Name
1           Users

aColumns

Id          Name           aTable       aDataType
1           Name           1            2
2           UserId         1            1
3           Occupation     1            2

aDataType

Id          Name
1           Number
2           Text

一行

Id          aTable
1           1
2           1

一个数字

Id          aRow           aColumn      Data
1           1              1            1245
2           2              2            56

文本

Id          aRow           aColumn      Data
1           1              1            Sara
2           2              1            Jake

所有其他 z* 表都是空白的

这是创建动态表的查询:

select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zBit] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]
UNION ALL
select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zDateTime] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]
UNION ALL
select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zMoney] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]
UNION ALL
select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zMoney] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]
UNION ALL
select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zNumber] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]
UNION ALL
select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zText] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]

这是这个查询的一部分:

select t.[Id] as [Table], c.Name as [Column], dt.Name as [DataType], r.[Id] as [Row], cast(v.Data as nvarchar(MAX)) as Data from [pod].[dbo].[aTable] t
INNER JOIN [pod].[dbo].[aColumn] c on t.Id = c.[aTable]
INNER JOIN [pod].[dbo].[aDataType] dt on c.[aDataType] = dt.Id
INNER JOIN [pod].[dbo].[aRow] r on t.[Id] = r.[aTable]
INNER JOIN [pod].[dbo].[zText] v on c.[Id] = v.aColumn and r.[Id] = v.[aRow]

正如您在此处看到的,一条数据(z* 表)由一行和一列标识。当我运行这个查询时,我得到这个:

结果

Table       Column         DataType     Row           Data
1           UserId         Number       1             1245          
1           UserId         Number       2             56
1           Name           Text         1             Sara
1           Name           Text         2             Jake

这是我想要的结果: (如果列未知,我不确定如何将这些行转换为列)

Row         UserId       Name
1           1245         Sara
2           56           Jake

大问题 这张表应该有 3 列还记得吗?

aColumns

Id          Name           aTable       aDataType
1           Name           1            2
2           UserId         1            1
3           Occupation     1            2

所以我的最终预期结果是:

Row         UserId       Name         Occupation
1           1245         Sara         NULL
2           56           Jake         NULL

在结果中,我还需要对列进行排序。这甚至可能吗。哪些数据库支持这种功能。我对任何可以做到这一点的数据库持开放态度。

【问题讨论】:

  • 我需要在一个数据库中创建一个数据库为什么?简单地添加和删除列和/或表有什么问题。
  • en.wikipedia.org/wiki/Inner-platform_effectthedailywtf.com/Articles/The_Inner-Platform_Effect.aspx。您为什么要重新发明*,而不是让您的应用程序动态地创建真正的行和列?
  • @ChrisShain - 动态创建新行/列的问题是如何编写查询,因为每个查询都需要查找存在哪些列,然后动态地允许人们选择要检索的内容.基本上,您正在为流程中的 EAV 模型创建框架。
  • 对,但你也需要用你的方法做到这一点。 MySQL、Oracle 和 SQL Server 都有用于检索现有数据库结构的内置方法。恕我直言,EAV 的更大优势在于能够高效地存储稀疏数据,许多数据库引擎已经做到了这一点(请参阅 SQL Server 的面向列的存储)
  • 欢迎来到数据库世界。我怀疑每个数据库开发人员都会经历这个阶段,直到他们了解“EAV”这个术语并开始阅读它。然而,如果你只是在玩,玩得开心,并以艰难的方式自己学习——这通常是最好的学习方式。祝你好运:)

标签: mysql sql sql-server oracle postgresql


【解决方案1】:

然后,您可能需要查看 Entity Attribute Value model (EAV) 设计的数据。

基本上,您可以拥有一个带有表名的表,以及表上的一些其他元数据。

然后,您可以为这些行中的每一行创建一个表以包含列数据,例如数据类型和名称。

然后,您有一个表格,您可以将每列的值放在一个长表格中。

这允许您动态创建表,或动态添加/删除行。

有关关系和 EAV 的比较,您可以查看以下问题:

Entity Attribute Value Database vs. strict Relational Model Ecommerce

但是,如果您希望获得此数据的关系视图,则需要创建触发器以帮助保持视图保持最新状态,这可能需要大量工作才能使其正常工作。如果您不需要关系视图,那么您应该没问题。

另一种方法是使用 NoSQL 数据库 (http://en.wikipedia.org/wiki/NoSQL),因为不必设置架构,因此您只需存储该行所需的列。

在这一点上,我会采用 NoSQL 方式,因为有许多数据库可以工作,而您需要做的重新发明是最小的。

【讨论】:

    【解决方案2】:

    对于问题的最后一部分,您要问的是如何针对 EAV 架构进行 cross-tabulation 查询。一些数据库通过对 SQL 标准的扩展来支持这一点,而另一些则根本不支持它。为了可移植性,您必须在您的应用程序中执行此操作。 PostgreSQL 为此提供了crosstab function in the tablefunc extension

    如果你走上EAV 的道路,你迟早会后悔的。它在某些有限的情况下很有用,但它不适合关系模型,并会导致很多痛苦和问题,其中最重要的是性能很差。

    请考虑:

    • 如果可能,请重新设计,这样您就不需要动态架构。在您的情况下可能不可能,因为您的明确要求是基于 Web 的数据库应用程序的用户可编辑架构,但在大多数情况下,这是正确的选择。

    • 使用ALTER TABLECREATE TABLE 等动态创建/删除模式。有些数据库在这方面比其他数据库要好得多。 PostgreSQL 的事务 DDL 可以提供很多帮助。需要谨慎以避免这成为性能和维护方面的噩梦,但如果您尝试使用动态结构对关系数据库进行建模,这可能是最明智的选择。

    • 针对类似 EAV 的查询进行了优化的键/值存储;见Key/Value stores。请注意,其中许多系统不提供完整的 ACID 语义,并且可能具有有限的查询语言,因此您可以在应用程序中完成更多工作。

    • 在数据库中存储 XML 或 JSON。您可以使用关系数据库来做到这一点,但使用文档数据库可能会更好。适用与 K/V 商店相同的警告。如果您在应用程序中执行所有查询逻辑并且您的数据量不是太大,则此方法可以正常工作。

    • 使用 PostgreSQL 的 hstore 等特定于数据库的功能在需要时支持任意键/值存储,并在不需要 k/v 的情况下使用标准关系设计。如果您希望将关系作为输出,它仍然是一个主要的 PITA,它涉及低效的交叉表查询和连接。

    Chris 提出了一个很好的观点:你的整个设计非常可疑。请参阅:The inner platform effectTDWTF's take on it。说真的,不要去那里。

    【讨论】: