rkr / data-diff
一个方便的工具,可以快速以键值对方式比较结构化数据
Requires
- php: >= 7.1
- ext-json: *
- ext-mbstring: *
- ext-pdo: *
- ext-pdo_sqlite: *
Requires (Dev)
- phpstan/phpstan: >= 0.1
- phpunit/phpunit: >= 7.0, < 10.0
Suggests
- rkr/data-diff-helpers: Some Helpers for rkr/data-diff
- dev-master
- 0.2.2
- 0.2.1
- 0.2
- 0.1.18.1
- 0.1.18
- 0.1.17.3
- 0.1.17.2
- 0.1.17.1
- 0.1.17
- 0.1.16
- 0.1.15
- 0.1.14
- 0.1.13
- 0.1.12.3
- 0.1.12.2
- 0.1.12.1
- 0.1.12
- 0.1.11.7
- 0.1.11.6
- 0.1.11.5
- 0.1.11.4
- 0.1.11.3
- 0.1.11.2
- 0.1.11.1
- 0.1.11
- 0.1.10.4
- 0.1.10.3
- 0.1.10.2
- 0.1.10.1
- 0.1.10
- 0.1.9
- 0.1.8.2
- 0.1.8.1
- 0.1.8
- 0.1.7
- 0.1.6.1
- 0.1.6
- 0.1.5.1
- 0.1.5
- 0.1.4.1
- 0.1.4
- 0.1.3.3
- 0.1.3.2
- 0.1.3.1
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1
This package is auto-updated.
Last update: 2024-09-21 20:15:41 UTC
README
一个方便的工具,可以快速以键值对方式比较结构化数据
composer
WTF
如果你有很多结构化数据需要导入本地数据库(例如)并且不想在每次运行时都覆盖所有内容,那么这个组件对你来说很有趣。相反,你希望了解实际发生了什么变化,并据此采取行动。
使用方法
一开始,你有两个要比较的二维数据列表。通常,此类数据列表的一些列会说明新行和缺失行的实际差异。一些列会指出已找到现有行的更改。你也可以有不会引起任何操作的列,但它们的数据可能在后续处理中需要。
例如,你有一些文章元数据,这些数据可以从外部数据源获取,并且你希望将这些数据放入本地数据库中。应该将外部数据导入该本地数据库,并且每当数据集被添加、删除或更改时(例如,记录日志),你希望采取行动。
外部数据
name;reference;price;stock;last-change
Some Notebook;B0001;1499.90;1254;2016-04-01T10:00:00+02:00
A Hairdryer;C0001;49.95;66;2016-04-01T10:00:00+02:00
A Pencil;D0001;2.9499;2481;2016-04-01T10:00:00+02:00
本地数据
name;reference;price;stock
A shiny Smartphone;A0001;519.99;213
A Hairdryer;C0001;49.95;12
A Pencil;D0001;2.95;2481
在列表中,我们有三行数据。但在两个列表中都有一个在另一个列表中不可用的行,而唯一共同的行(“吹风机;C0001”和“铅笔;D0001”)在“价格”和“库存”列中有所不同,而“名称”在两个列表中相同。无论在“current-datetime”列中是什么,都不应该进行比较,但在插入或更新时也应该考虑。最终目标是将所有更改从外部数据源带到本地数据库。_知道一个“current-datetime”已经更改,而所有其他列保持不变,但这在这种情况下我不重要。_
通过比较两个不同的键值列表来计算实际的比较结果。通过三种方法进行比较,可以找到添加的键、缺失的键和键相等的更改数据。因此,为了获取这些信息,你需要了解如何表达特定的行已添加、删除或更改。这并不总是清晰的任务,并且取决于所涉及的数据。在这个例子中,我将设置一些规则,这些规则_可能_与你的场景不同。
在这个例子中,我们只考虑使用reference
来判断列表中的一行是新的还是已删除。因此,本地数据库有一个指向文章A0001
的reference
,它不包括在外部数据中。因此,我们希望因为这一点而将A0001
从我们的本地数据中删除。B0001
不在我们的本地数据中,因此应该添加。吹风机库存不同,铅笔价格略有不同。由于我们以两位小数的精度本地存储价格,这两个铅笔价格实际上相同,比较不应报告对行D0001
的更改。
首先,你需要告诉Storage
确切什么是键,什么是值,以定义Storage应理解为何键值列表的模式。我们不想转换列表,因为数据已经很好。
因此,让我们给列一些意义
reference
列告诉我们,特定的行是否存在。这是每行的标识。一行可能有多个列作为标识符(例如reference
和environment-id
),但在此情况下,我只有一个标识符。- 当一行已经存在于其他列表中时,才应考虑
name
列。 - 当一行已经存在于其他列表中时,才应考虑
price
列。 - 当一行已经存在于其他列表中时,才应考虑
stock
列。 last-change
列根本不需要检查。
因此,当我们构建一个键值数组来进行实际比较时,键部分由reference
组成,值部分由列name
、price
和stock
表示。
第一个列表的键值数组将如下所示
'B0001' => ['Some Notebook', 1499.90, 1254]
'C0001' => ['A Hairdryer', 49.95, 66]
'D0001' => ['A Pencil', 2.9499, 2481]
第二个列表的键值数组将如下所示
'A0001' => ['A shiny Smartphone', 519.99, 213]
'C0001' => ['A Hairdryer', 49.95, 12]
'D0001' => ['A Pencil', 2.95, 2481]
现在,让我们以三种不同的方式比较这些数组
在第一个列表中但不在第二个列表中存在的行
'B0001' => ['Some Notebook', 1499.90, 1254]
在第二个列表中但不在第一个列表中存在的行
'A0001' => ['A shiny Smartphone', 519.99, 213]
在第一个列表中,但与第二个列表相比值已更改的行?
'C0001' => ['A Hairdryer', 49.95, 66]
'D0001' => ['A Pencil', 2.9499, 2481]
您拥有了匹配两个列表之间所有差异所需的所有信息。
这里有一个特殊情况。铅笔在第一个列表中的价格是2.9499
。但由于我们只想比较两位小数的价格,因此实际上价格是相同的,因为计算出的价格在两种情况下都是2.95
。这是Schema
组件发挥作用的领域。
当您定义一个MemoryDiffStorage
时,您指定两个模式。一个用于键部分,一个用于值部分
<?php use DataDiff\MemoryDiffStorage; $ds = new MemoryDiffStorage([ 'reference' => 'STRING', ], [ 'name' => 'STRING', 'price' => 'MONEY', 'stock' => 'INT', ]);
MemoryDiffStorage
由两个存储器组成:StoreA
和StoreB
。只要行至少包含在模式中定义的列,您就可以在每个存储器中插入任意数量的行和列。 列还需要有适当的名称,因为这些名称不会自动翻译。尽管如此,您可以在使用addRow
和addRows
的第二个参数添加行时指定翻译。这意味着,如果您的列在数据库和其他源中有不同的名称,您必须在将数据放入每个Store
之前对这些键进行归一化。
以下是一个示例
<?php use DataDiff\MemoryDiffStorage; require 'vendor/autoload.php'; $ds = new MemoryDiffStorage([ 'reference' => 'STRING', ], [ 'name' => 'STRING', 'price' => 'MONEY', 'stock' => 'INT', ]); $ds->storeA()->addRow(['name' => 'Some Notebook', 'reference' => 'B0001', 'price' => '1499.90', 'stock' => '1254', 'last-change' => '2016-04-01T10:00:00+02:00']); $ds->storeA()->addRow(['name' => 'A Hairdryer', 'reference' => 'C0001', 'price' => '49.95', 'stock' => '66', 'last-change' => '2016-04-01T10:00:00+02:00']); $ds->storeA()->addRow(['name' => 'A Pencil', 'reference' => 'D0001', 'price' => '2.9499', 'stock' => '2481', 'last-change' => '2016-04-01T10:00:00+02:00']); $ds->storeB()->addRow(['name' => 'A shiny Smartphone', 'reference' => 'A0001', 'price' => '519.99', 'stock' => '213']); $ds->storeB()->addRow(['name' => 'A Hairdryer', 'reference' => 'C0001', 'price' => '49.95', 'stock' => '12']); $ds->storeB()->addRow(['name' => 'A Pencil', 'reference' => 'D0001', 'price' => '2.95', 'stock' => '2481']);
一个很好的经验法则是使用store a
来存储您已经拥有的数据,并使用store b
来存储要比较的数据(例如从外部数据源导入的数据)。
接下来,我们可以查询其中一个存储器以查找列表中的差异。由于store a
包含我们的本地数据,我们使用store b
来查询差异
获取所有存在于store b
但不存在于store a
的数据集
foreach($ds->storeB()->getNew() as $row) { $data = $row->getData(); printf("This row is not present in store a: %s\n", $data['reference']); }
结果是此行不在存储器b中:B0001
。
获取所有存在于store a
但不存在于store b
的数据集
foreach($ds->storeB()->getMissing() as $row) { $data = $row->getForeignData(); printf("This row is not present in store a: %s\n", $data['reference']); }
结果是此行不在存储器a中:A0001
。
获取所有更改的数据集
foreach($ds->storeB()->getChanged() as $row) { printf("This row is not present in store a: %s\n", $row->getDiffFormatted()); }
结果是此行不在存储器a中:stock:12 -> 66,last-change:-> 2016-04-01T10:00:00+02:00
。
如您所注意到的,D0001
不在结果集中。这是因为模式已经规范化了列price
的十进制精度,因此没有发生任何差异。
您还可以按每个模式中定义的键和值访问数据。如果您想从模式构建SQL语句,这很有帮助。您可以将键视为UPDATE
语句中的WHERE
条件,并将值视为实际要更改的数据(《SET》)
print_r($row->getLocal()->getKeyData());
print_r($row->getLocal()->getValueData());
示例
<?php use DataDiff\MemoryDiffStorage; require 'vendor/autoload.php'; $ds = new MemoryDiffStorage([ 'client_id' => 'INT', ], [ 'description' => 'STRING', 'total' => 'MONEY', ]); for($i=2; $i <= 501; $i++) { $row = ['client_id' => $i, 'description' => 'This is a test', 'total' => $i === 50 ? 60 : 59.98999, 'test' => $i % 2]; $ds->storeA()->addRow($row); } for($i=1; $i <= 500; $i++) { $row = ['client_id' => $i, 'description' => 'This is a test', 'total' => 59.98999, 'test' => $i % 3]; $ds->storeB()->addRow($row); } $res = $ds->storeA()->getNew(); foreach($res as $key => $value) { printf("Added : %s\n", $value['client_id']); } $res = $ds->storeA()->getChanged(); foreach($res as $key => $value) { printf("Changed: %s\n", $value['client_id']); } $res = $ds->storeA()->getMissing(); foreach($res as $key => $value) { printf("Removed: %s\n", $value['client_id']); } echo "\n"; $res = $ds->storeA()->getChanged(); foreach($res as $key => $value) { print_r($value->getDiff()); }
输出
Added : 501
Changed: 50
Removed: 1
Array
(
[total] => Array
(
[local] => 60
[foreign] => 59.98999
)
[test] => Array
(
[local] => 0
[foreign] => 2
)
)