的表格
如果你想使用一個數組作爲一個數據庫,相當明顯的最佳方式來模擬一個表使用一個二維數組:
$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)
操作其他數據庫:我們需要遍歷整個表,檢查每一行的相關值。這是索引起作用的地方。如果我們想要在我們的縣名上添加索引呢?那麼,我們可以使用一個單獨的關聯數組:
關聯數組被實現爲一個散列表,所以訪問由鍵的元素是大致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'
首先我們來看看最具體的指數(唯一的我ndex):
$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
列引用ID在$counties
表中的行中。順便說一下,我們使用計數器來跟蹤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;
現在你可能已經發現這個問題:這兩個表包含一個名爲名稱列。上面的代碼將從每個行的walks
表中返回name
的值。你可以很容易地調整這種行爲,但究竟如何實施將取決於你想要的結果。
排序的結果集
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的特徵的邏輯。即使數據集增長,也可以進行某些相當簡單的優化,使這些操作相對便宜。
如果它真的回答了您的問題,請考慮接受答案 – michi 2013-04-14 12:37:06