桌子
如果您想使用数组作为数据库,显然最好的表格建模方法是使用 2D 数组:
$counties = array();
$countiesKey = 0;
// add a row
$counties[++$countiesKey] = array(
"name" => "Armagh",
"img" => "css/images/map.jpg",
"largeimg" => "css/images/banmap.jpg"
);
// and another...
$counties[++$countiesKey] = array(
"name" => "Antrim",
"img" => "css/images/map.jpg",
"largeimg" => "css/images/banmap.jpg"
);
这大致相当于下面的表定义(为简单起见,我们将使用 MySQL 进行比较并假设所有字符串字段为VARCHAR(1024)
):
CREATE TABLE counties (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(1024),
img VARCHAR(1024),
largeimg VARCHAR(1024)
);
唯一索引
所以我们使用数组索引作为我们的主键。但是为了基于除主键之外的任何“列”来搜索数据库,它需要一个O(n)
操作:我们需要迭代整个表并检查每一行的相关值。这就是索引发挥作用的地方。如果我们也想在我们的县名上添加索引怎么办?好吧,我们可以使用一个单独的关联数组:
$countiesNameIndex = array();
$countiesNameIndex['Armagh'] = 1;
$countiesNameIndex['Antrim'] = 2;
关联数组被实现为哈希表,因此通过键访问元素大致是O(1)
. 这使我们能够在按县名搜索时大大加快对行的访问:
$search = 'Antrim';
$result = array();
if (isset($countiesNameIndex[$search])) {
$result[$countiesNameIndex[$search]] = $counties[$countiesNameIndex[$search]];
}
return $result;
该索引可以在添加和删除行时动态维护:
// Insert a row
$row = array( /* row data */ );
if (isset($countiesNameIndex[$row['name']])) {
// insert fails, duplicate value in column with unique index
}
$counties[++$countiesKey] = $row;
$countiesNameIndex[$row['name']] = $countiesKey;
// Delete a row
$idOfRowToDelete = 2;
if (isset($counties[$idOfRowToDelete])) {
unset(
$countiesNameIndex[$counties[$idOfRowToDelete]['name']],
$counties[$idOfRowToDelete]
);
}
随着数据集变大,这种索引方法将大大加快数据访问速度。
非聚集索引
让我们快速看看我们将如何实现一个不携带有关它引用的行的顺序信息的非唯一索引 - 实现非常相似。这会比我们的唯一索引慢一点,但比迭代整个数据集要快得多:
$countiesImgIndex = array();
// Insert a row
// INSERT INTO counties ( ... ) VALUES ( ... )
$row = array( /* row data */ );
if (!isset($countiesImgIndex[$row['img']])) {
$countiesImgIndex[$row['img']] = array();
}
$counties[++$countiesKey] = $row;
$countiesImgIndex[$row['img']][] = $countiesKey;
// Search using the index
// SELECT * FROM counties WHERE img = 'css/images/map.jpg'
$search = 'css/images/map.jpg';
$result = array();
if (isset($countiesImgIndex[$search])) {
foreach ($countiesImgIndex[$search] as $rowId) {
$result[$rowId] = $counties[$rowId];
}
}
return $result;
// Delete a row
// DELETE FROM counties WHERE id = 2
$idOfRowToDelete = 2;
if (isset($counties[$idOfRowToDelete])) {
$key = array_search($idOfRowToDelete, $countiesImgIndex[$counties[$idOfRowToDelete]['img']]);
if ($key !== false) {
array_splice($countiesImgIndex[$counties[$idOfRowToDelete]['img']], $key, 1);
}
unset($counties[$idOfRowToDelete]);
}
使用多个索引
我们甚至可以使用这些索引来执行更复杂的操作——考虑如何实现 SQL 查询
SELECT *
FROM counties
WHERE name = 'Antrim'
AND img = 'css/images/map.jpg'
首先我们看一下最具体的索引(唯一索引):
$result = array();
$nameSearch = 'Antrim';
$imgSearch = 'css/images/map.jpg';
if (!isset($countiesNameIndex[$nameSearch])) {
return $result;
}
接下来我们检查该行是否与其他条件匹配:
if ($counties[$countiesNameIndex[$nameSearch]]['img'] === $imgSearch) {
$result[$countiesNameIndex[$nameSearch]]
= $counties[$countiesNameIndex[$nameSearch]];
}
return $result;
您可以看到,在这种情况下,我们只需要使用 1 个索引,因为正在查询的列之一具有唯一索引。这意味着我们可以直接转到唯一重要的行并检查它是否符合条件。现在让我们假设我们在另一个非唯一列 - 上有一个索引largeImg
。这个操作有点复杂,但我们可以通过使用来走捷径array_intersect()
:
$result = array();
$imgSearch = 'css/images/map.jpg';
$largeImgSearch = 'css/images/banmap.jpg';
if (!isset($countiesImgIndex[$imgSearch], $countiesLargeImgIndex[$largeImgSearch])) {
return $result;
}
return array_intersect(
$counties[$countiesImgIndex[$imgSearch]],
$counties[$countiesLargeImgIndex[$largeImgSearch]]
);
外键和连接表
但是当我们开始想加入另一个表时呢?再说一次,这很像我们在 SQL 中做的。假设我们有以下 SQL 表定义:
CREATE TABLE walks (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(1024),
location VARCHAR(1024),
county INT
);
显然我们从另一个数组开始,并插入一些行:
$walks = array();
$walksKey = 0;
$walks[++$walksKey] = array(
"name" => "Portadown Walk",
"county" => 1,
"location" => "Portadown",
);
$walks[++$walksKey] = array(
"name" => "Antrim Walk",
"county" => 2,
"location" => "Causeway"
);
很明显那里发生了什么,该county
列引用了$counties
表中行的 ID。顺便说一下,我们使用计数器来跟踪 ID 而不是使用$arr[] =
赋值语法的原因有两个:首先,它确保当从表中删除行时 ID 始终保持不变,其次它更容易(计算成本更低) 以提取最后插入的行的 ID - 这在使用外键创建复杂的表结构时会有所帮助,正如您在此处看到的那样。
现在让我们看一下将这些数据关联起来。假设我们运行了这个 SQL 查询:
SELECT c.*, w.*
FROM walks w
JOIN counties c ON w.county = c.id
LIMIT 0, 10
这可以按如下方式实现:
$result = array();
$i = 0;
foreach ($walks as $walkId => $walksRow) {
$result[$walkId] = array_merge($counties[$walksRow['county']], $walksRow);
if (++$i == 10) {
break;
}
}
return $result;
现在您可能已经发现了一个问题:两个表都包含一个名为 name 的列。上面的代码将从表中返回每一行的name
值walks
。您可以轻松调整此行为,但具体如何实施将取决于您想要的结果。
排序结果集
PHP 提供了一个可以为您完成大部分工作的函数 - array_multisort()
. 需要注意的最重要的一点是,应在提取结果行后应用顺序,以尽量减少所需操作的数量。
SELECT c.*, w.*
FROM walks w
JOIN counties c ON w.county = c.id
ORDER BY w.location ASC
// Collect the result set in $result as above
$location = array();
foreach ($result as $row) {
$location[] = $row['name'];
}
array_multisort($location, SORT_ASC, $result);
return $result;
希望以上示例应该开始演示一些可用于使用 PHP 数组在 RDBMS 上实现某些功能的逻辑。可以进行某些相当简单的优化,即使随着数据集的增长,这些操作也会相对便宜。