我想要一个适应性强的数据库方案。但仍然在我的应用程序中使用一个简单的表数据网关,我只传递$ data []数组进行存储
基本列在初始表方案中确定。然而,稍后会出现几个元区域(大约10-20)。我希望在那里有一些灵活性,而不是每次都手动调整数据库,或者 - 因为新字段而改变应用程序逻辑。
所以现在有两种选择似乎可行,但并不过分。但我不确定可伸缩性或数据库的缺点。
(1)自动ALTER TABLE。每当要保存$ data数组时,都会将这些键与当前数据库列进行比较。在将$ data插入表中之前定义新列。实际上在测试代码中似乎很简单:
function save($data, $table="forum") {
// columns
if ($new_fields = array_diff(array_keys($data), known_fields($table))) {
extend_schema($table, $new_fields, $data);
}
// save
$columns = implode("`, `", array_keys($data));
$qm = str_repeat(",?", count(array_keys($data)) - 1);
echo ("INSERT INTO `$table` (`$columns`) VALUES (?$qm);");
function known_fields($table) {
return unserialize(@file_get_contents("db:$table")) ?: array("id");
function extend_schema($table, $new_fields, $data) {
foreach ($new_fields as $field) {
echo("ALTER TABLE `$table` ADD COLUMN `$field` VARCHAR;");
由于它主要是元信息字段,因此将它们添加为VARCHAR就足够了。无论如何,没有人会问他们。所以数据库实际上只是用作存储。
但是,虽然我可能想要随时添加许多新的$ data字段,但它们并不总是被填充。
(2)将序列化()字段转换为BLOB。任何新的/无关的元字段都可能对数据库不透明。简单地从真实数据库列中整理出虚拟字段非常简单。元字段可以序列化()到blob /文本字段然后:
function ext_save($data, $table="forum") {
$db_fields = array("id", "content", "flags", "ext");
// disjoin
foreach (array_diff(array_keys($data),$db_fields) as $key) {
$data["ext"][$key] = $data[$key];
unset($data[$key]);
}
$data["ext"] = serialize($data["ext"]);
在读取查询上反序列化和解压缩此“ext”列是一个小问题。优点是数据库中不会有任何稀疏填充的列,所以我猜它比AUTO ALTER TABLE方法更紧凑,更快。
当然,此方法可防止在WHERE或GROUP BY子句中使用其中一个新字段。但我认为没有任何可能的元字段的(USER_AGENT,author_ip,author_img,票,命中,LAST_MODIFIED,..)将/应该永远在那里反正使用。
所以我现在更喜欢'ext'blob方法,即使它是单程票
这些列通常如何调用? (寻找例子/ doc)
您是否会在(非常理论上)数据库内查询中使用XML序列化?
适应表方案似乎是一个“更干净”的界面,即使大多数列可能仍然是空的。这对速度有何影响? MySQL / innodb胃里有多少个稀疏的VARCHAR字段? 但最重要的是:是否有任何标准实施方案?带有自动 ALTER TABLE技巧的伪ORM?存储一个简单的列列表似乎可行,但像pdo :: getColumnMeta这样的东西会更健壮。
答案 0 :(得分:4)
在你提出的两个想法中,我会选择第二个想法。第一个让我想哭,不要随便。
如果您确定不需要根据元字段进行查询,那么序列化是存储它们的完美有效方式。
还有一个更好的解决方案,您似乎没有确定 - 即使用数据透视表。拥有原始表,然后是第二个表格,其中包含以下模式:
metaid metaname metavalue
1 colour red
2 texture rough
然后第三个'pivot'表将链接两个
tbl1_id metaid
1 1
2 2
这样,没有稀疏填充的列,您可以保留基于元数据进行查询的功能。