需求:
前段時間遇到一個需求,批次同步資料。
給過來的資料如果本地已經有的就更新,沒有的就建立。
分析
因為資料有差異,不能進行覆蓋因此mysql的 REPLACE INTO 是不考慮了,
這時候,我需要把傳過來的資料進行分離,分離成兩個組,一個更新組,一個插入組。
首先來構建一下假資料
//源資料
$origin =[];
//當前本地資料
$current=[];
//生成10000個資料
for($i=1;$i<10000;$i++) {
$origin[] = ['id' => $i, 'title' => 'title'.$i, 'content' => 'name'.$i];
}
//假設本地的資料已經取好了,我用title來作為唯一值來處理吧,容易認一點
for($i=5000;$i<8000;$i++) {
$current[] = 'title'.$i;
}
第一種使用foreach遍歷
$t1 = microtime(true);
$insertArr = [];
$updateArr = [];
foreach ($origin as $k=>$value) {
if (!in_array($value['title'], $current)) {
$insertArr[] = $value;
}else {
$updateArr[] = $value;
}
}
$t2 = microtime(true);
dd($t2 - $t1);
程式碼執行時間:0.10883212089539
第二種使用array_map或array_walk進行遍歷
$t1 = microtime(true);
array_walk($origin, function($value) use ($current, &$insertArr, &$updateArr) {
if (!in_array($value['title'], $current)) {
$insertArr[] = $value;
} else {
$updateArr[] = $value;
}
});
或者
array_map(function($value) use ($current, &$insertArr, &$updateArr) {
if (!in_array($value['title'], $current)) {
$insertArr[] = $value;
} else {
$updateArr[] = $value;
}
}, $origin);
$t2 = microtime(true);
dd($t2 - $t1);
兩個執行時間差不多都是這個:0.11882400512695
第三種:array_diff_key交集比較
$t1 = microtime(true);
//把title取出來做為鍵
//$origin_new = array_combine(array_column($origin, 'title'), $origin);
$origin_new = array_column($origin, null, 'title');
//把值作為鍵
$current_new = array_flip($current);
//需要新增插入的資料 (用鍵來做差集比較得出新的源)
$insertArr = array_diff_key($origin_new, $current_new);
//需要進行更新的資料
$updateArr = array_diff_key($origin_new, $new_origin);
$t2 = microtime(true);
dd($t2 - $t1);
程式碼執行時間:0.0013279914855957
以上程式碼執行後分離的陣列都得到以下相同的資料
//列印陣列
dd($insertArr, $updateArr);
分離出的需要插入的陣列
array:7000 [
"title1" => array:3 [▼
"id" => 1
"title" => "title1"
"content" => "name1"
]
"title2" => array:3 [▶]
"title3" => array:3 [▶]
...
"title4999" => array:3 [ …3]
"title8000" => array:3 [ …3]
"title8001" => array:3 [ …3]
...
"title9999" => array:3 [ …3]
"title10000" => array:3 [ …3]
]
分離出來的需要更新的資料
array:3000 [▼ "title5000" => array:3 [▼ "id" => 5000 "title" => "title5000" "content" => "name5000" ] "title5001" => array:3 [▶] "title5002" => array:3 [▶] ... "title7998" => array:3 [ …3] "title7999" => array:3 [ …3] ]
總結
foreach、 array_map、 array_walk 這些需要遍歷的都比不過透過 array_diff_key 陣列鍵去對比取交集的方法。
選擇 array_diff_key 效率是最快的
以下進行封裝:
/**
* 對比法分割陣列
*
* @param $key 透過指定鍵(key)去對比
* @param $origin 原陣列
* @param $split 分割陣列 (源陣列中的某個鍵的值)
* @return array ['split'=>被分割出來的陣列, 'remainders'=>剩餘的資料];
*/
function splitArr($key, $origin, $split)
{
//把title取出來做為鍵
//$origin_new = array_combine(array_column($origin, $key), $origin);
$origin_new = array_column($origin, null, $key);
//把值作為鍵
$split_flip = array_flip($split);
//剩餘的源資料 (用鍵來做差集比較得剩餘的源資料)
$remainders = array_diff_key($origin_new, $split_flip);
//被分割出來的資料
$new_split = array_diff_key($origin_new, $remainders);
return ['split'=>$new_split, 'remainders'=>$remainders];
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結