我正在使用第三方RESTful API数据更新MySQL数据库表。此更新每天触发几次,因此为了简化过程,我决定在插入新的“新”API数据之前简单地截断数据库表。但是,这导致了两个主要问题:
a)进程执行时表变为空(这是一个实时站点),
b)在执行插入操作时,MySQL服务器有时无法截断表。这导致记录重复,有时甚至翻两番。
我可以将截断和插入放入一个MySQL事务中,但这仍然无法解决问题(a)。
我决定以不同的方式解决问题:
首先,我将API数据和本地数据收集到两个结构相同的数组中,按键(ASC)和值(ASC)排序。这是一个例子:
$local = [
['categoryId' => '547d8fd5','programId' => '0714f2cb'],
['categoryId' => '547d8fd5','programId' => '0914f2cb'],
]
$remote = [
['categoryId' => '547d8fd5','programId' => '0714f2cb'],
['categoryId' => '547d8fd5','programId' => '0814f2cb'],
]
然后我将使用以下方法比较两个数组:
public static function arrayDiffAssocRecursive($array1, $array2)
{
foreach($array1 as $key => $value) {
if (is_array($value)) {
if (!isset($array2[$key])) {
$difference[] = $value;
} elseif(!is_array($array2[$key])) {
$difference[] = $value;
} else {
$new_diff = self::arrayDiffAssocRecursive($value, $array2[$key]);
if ($new_diff != FALSE) {
$difference[] = $array1[$key];
}
}
} elseif (!isset($array2[$key]) || $array2[$key] != $value) {
$difference[] = $value;
}
}
return !isset($difference) ? null : $difference;
}
要获取需要添加的记录,我将按以下顺序使用参数调用上述方法:
$add = Comparator::arrayDiffAssocRecursive($remote, $local);
为了获取需要删除的记录,我将翻转参数:
$delete = Comparator::arrayDiffAssocRecursive($local, $remote);
只要两个数组均等地排序并具有相同数量的记录,该方法就可以工作,但是如果一个或另一个缺少一个或多个记录,则所有剩余记录将不匹配,因此标记为删除和添加,无论它们是否相同保持不变。这种方法有效,但我仍觉得它效率低下。
我想要看到的是一种方法,它可以比较两个数组并返回不匹配的记录,而不管数组的顺序或记录总数。这是可行的还是我完全以错误的方式接近这个?
使用替换而不是插入。只要您拥有不变的主键,它就会根据主键进行更新。如果主键已存在,则进行更新,否则执行插入操作。
我最终使用json_encode()
将二级数组元素压缩成字符串。然后我使用array_diff()
来收集不匹配的元素,最后我使用json_decode()
将$add
和$delete
数组返回到它们的原始状态。
现在我不再需要担心按ASC顺序或它们的键匹配两个数组,因为array_diff()
将发现不同的元素,无论它们位于何处。
如果你想知道为什么我使用json编码/解码vs序列化/反序列化 - 根据另一个stackoverflow线程,json执行得更快。
class ComparatorTest extends TestCase
{
private $arrayA;
private $arrayB;
private $expectedA;
private $expectedB;
public function setUp()
{
parent::setUp();
$this->arrayA = [
['key1' => '000','key2' => '8989'],
['key1' => '123','key2' => '354'],
['key1' => 'aaa','key2' => 'sbbb'],
];
$this->arrayB = [
['key1' => '123','key2' => '354'],
['key1' => 'aaa','key3' => 'asdf'],
['key1' => '654','key2' => '8989'],
];
$this->expectedA = [
['key1' => '000','key2' => '8989'],
['key1' => 'aaa','key2' => 'sbbb']
];
$this->expectedB = [
['key1' => 'aaa','key3' => 'asdf'],
['key1' => '654','key2' => '8989'],
];
}
public function testArrayDiffAssocRecursive()
{
$a = $this->flattenSecondLevelArray($this->arrayA);
$b = $this->flattenSecondLevelArray($this->arrayB);
$addFlat = array_diff($a, $b);
$deleteFlat = array_diff($b, $a);
$add = [];
if (!empty($addFlat)) {
foreach($addFlat as $row) {
$add[] = json_decode($row, true);
}
}
$delete = [];
if (!empty($deleteFlat)) {
foreach($deleteFlat as $row) {
$delete[] = json_decode($row, true);
}
}
$this->assertEquals($this->expectedA, $add);
$this->assertEquals($this->expectedB, $delete);
}
private function flattenSecondLevelArray($array)
{
$flat = [];
if(!empty($array) && is_array($array)) {
foreach ($array as $pair) {
$flat[] = json_encode($pair);
}
}
return $flat;
}
}