我有一张名为Bookings的表。此表包含表示针对特定服务的预订的数据,其中包含许多变量。
前段时间我遇到了当前数据结构的问题,因此影响时间,日期或价格的预订更改会影响其他相关财务记录,日期预订列表等。
我当时的解决方案是创建一个修改表,用于跟踪对预订所做的任何更改。然后,每当要求预订模型返回预订时,它将添加修改(在afterFind()
蛋糕回调中)并提供最新版本的预订,如下所示(原谅绘画):
当您要求预订模式返回预订#1234时,此方法可以正常工作。它返回最新的预订表示,包括所有修改(彼此叠加),包括一个包含所有修改和原始预订数据的数组供参考。
我的问题是我最近意识到我需要能够使用自定义条件查询此模型,如果其中一个条件是在其中一个修改中实现的,那么结果就不会因为模型正在搜索原始记录而不是最终呈现的记录,所以不匹配。我查询模型以返回abc
为蓝色(不是灰色)的行的示例:
在该示例中,模型直接查看abc
为蓝色且不返回此结果的行的原始数据,因为蓝色值位于之后附加的修改找到原始结果。
我现在所做的是查询预订模型的beforeFind()
回调,以查找符合给定条件的修改,加入预订以确保任何其他条件仍匹配。当它返回上面示例中的蓝色时,它会将结果作为类属性存储在数组中,并继续使用常规find()
,但排除返回该预订的ID(因为我们'我发现了一个更新的版本。)然后它会将它们合并在一起,在afterFind()
中再次对它们进行排序等。
这很有效,虽然我希望它有点啰嗦。
毕竟,我已经意识到在本应用程序的其他部分,有些模型可以手动加入预订表并搜索预订。所以现在我需要一种方法能够将所有这些手动连接中的修改直接合并到MySQL中的表中,而不会影响原始数据,最好不要更改我的代码。
我的想法是我需要删除手动连接并改为创建模型关联。当我查询说有多个预订的客户模型(将修改应用于每个预订)时,预订模型的beforeFind()
和afterFind()
是否仍会运行?
我的另一个选择是通过删除修改中可能包含的任何条件从MySQL返回更多行,然后使用PHP根据我的搜索条件过滤结果。这个选项让我感到害怕,因为如果没有这个标准,结果集可能会很大......
如何实现此数据结构?我的关键要求仍然是我不想更改原始的预订记录,而是在顶部添加修改记录,但我需要能够通过模型查询预订(包括修改)。
我想尝试在幕后尽可能多地保留这种集成,这样我就不必通过我的整个应用程序来更改n
个看起来像这样的查询数量:
$get_blue = $this->Booking->find('all', array(
'conditions' => array(
'Booking.abc' => 'blue'
)
));
我希望能够隐含地包含对预订的任何修改,以便在上述查询中返回最新的预订。
另一个问题是,将预订模型手动加入搜索查询时,如下所示:
$get_transactions_on_blue_bookings = $this->Transaction->find('all', array(
'joins' => array(
array(
'table' => 'sql_bookings_table', // non-standard Cake format, I know - it's an example
'alias' => 'Booking',
'type' => 'LEFT',
'conditions' => 'Booking.booking_id = Transaction.booking_id'
)
),
'conditions' => array(
'Booking.abc' => 'blue'
)
));
正如您所看到的,上面的查询将不包括上面我的MSPaint示例中的修改,因为它在SQL中手动加入表(修改集成在before
和afterFind()
回调中预订模式的功能)。
对此的任何帮助将不胜感激。
我知道这已经足够了,但我想我会补充一点,我想跟踪这些更改并且不更新原始记录的原因是财务方面无法更改,因为它会影响报道。
到目前为止,我能看到的最快,最简单的解决方案是在所有情况下直接对原始预订进行修改,除非它影响财务信息,这仍然作为修改进行跟踪(因为我目前不需要基于搜索关于这个信息)。
答案 0 :(得分:5)
听起来您正在尝试实施 Temporal Database 。时态支持是ANSI / ISO SQL:2011标准的主要补充之一。 MySQL(像大多数RDBMS一样)落后于标准。将时态数据库视为CVS / SVN / Git的DBMS等价物。
相比之下,我们使用的没有时间特征的传统数据库可称为当前数据库。
在当前数据库中,如果您尝试实施时态支持,则可以通过多种方式在不同方法中失败:
单表方法。当您需要进行修改时,您对原始记录执行UPDATEs
,除非您拥有某种自行开发的触发/审核逻辑,历史遗迹缺席。即使您有审计/更改日志,也必须进行一些丑陋的挖掘以重建更改历史记录。
双表方法。您可以将数据拆分为两个表,一个包含基本/原始记录(例如预订),另一个表格,而不是在原地进行修改。表格,用于更改/修改/增量。那么至少你保留了原始数据,但是你必须再次编写复杂的逻辑来查看原始数据并进行分层修改。如果你只想要一些的修改,那就更糟了。
预先计算的结果表方法。您保留3个或更多表:基本记录,修改,以及尝试始终具有结果的表(保持最新的基数+修改)。祝你在INSERTs
时编写触发器和程序以进行此计算,如果需要UPDATE
或DELETE
,Heaven会帮助您。设置很脆弱,可能会失去同步,例如死锁和放大器。回滚。如果你没有在带有触发器/程序的数据库中执行此操作,你可以尝试在应用程序代码中实现结果计算,但是运气好 - 并且它可能会因多线程消费者而变得丑陋。而且,只有一些修改才能轻松访问结果。
结论:如果您不仅限于MySQL,您应该考虑使用具有内置时态支持的数据库。否则,你将重新实施这个轮子。
答案 1 :(得分:2)
如果您反过来并将原始记录应用于修改,那么如何将修改应用于原始记录呢?您可以修改修改表(或新表)以保留原始记录并应用修改,并将搜索引导到那里。
另一个想法是,如果所有需要保留的财务数据,为什么不将它保存在另一个字段或表中,并在需要时引用它? 我同意重新设计可能是长期解决方案的最佳/最聪明的方法,但我认为我会将我的想法放在桌面上,以防他们可以提供帮助。
答案 2 :(得分:2)
如果在修改原始表之前使用备份表来存储原始表中的数据,该怎么办?然后,您可以使用回滚功能将数据还原到以前的状态。
以下是我的数据库更新过程理论的流程图: http://i1371.photobucket.com/albums/ag300/joshua127/BookingFlowchartinsert_zps5c2d55f8.png
以下是我的选择过程理论的流程图: http://i1371.photobucket.com/albums/ag300/joshua127/BookingFlowchartselect_zps702fa902.png
希望这有帮助,只是另一种看待它的方式。
P.S。为了保持财务信息不变,您可以编写更新函数来计算要更新的列数(基于列名的更新数组),并提供变量以仅保留这些列的特定值。您可以在SQL语句中引用数组索引($ array ['index'])以使其动态化。
答案 3 :(得分:1)
在我看来,你需要的是一种桌子的历史,以便你能够知道当时发生的事情。
我通常通过创建一个名为原始追加_history
的并行表来实现这种方法。在你的情况下Bookings_history
。结构类似于原始结构,但在列前面加上:
a)timestamp
,在修改发生时保存
b)id
,以识别原始表中的行
将创建此两列的唯一索引。
每次修改发生时,在应用修改之前,您将原始行复制到历史记录表。然后在原始表上应用修改。这样,历史表就像一个堆栈,您可以在其中保存原始数据的快照。
我特别喜欢这个模型,因为在历史表上连接表和应用搜索引擎可以用与原始表类似的方式完成,因为结构非常相似。此外,如果您想了解修改,只需要比较历史表的行。
我希望这会有所帮助。
答案 4 :(得分:1)
从您收集到的答案来看,无论您做什么,都需要进行一些或多次重新设计。
我还没有看到的解决方案之一,我过去用来解决此类问题(即更改的订单)是将所有内容保存在同一个表中并使用字段来区分它们。
您可以更改bookings
表,以便为每个预订(即version_number
)和is_latest
字段添加递增的整数。这样,您可以使用is_latest=true
进行查询以获取当前记录及其version_number
。如果它是0,则没有变化,如果它> 0则会有变化(该数字将等于变化的数量)。你将能够"倒带"或"重播"历史记录,如果你从最新版本转到0或反之亦然,每次你有一个完整的记录,你的应用程序可以理解而无需修改。
如果is_latest
被编入索引,查询速度将(几乎)等于原始表格查询速度,当然,如果您需要多次获得原始预订,您可以添加更多的布尔值,例如is_original
。< / p>
这样做的好处是,它很可能只需要您更改Booking
模型,但这取决于您的代码。
编辑:我认为这种方法与您对报告和财务记录的要求最为相容,因为您始终可以轻松获得原始记录(版本0)。