我有一个半大(10,000,000+记录)的信用卡交易数据库,我需要定期查询。我已经设法将大多数查询优化为0.1秒以下,但我很难为子查询做同样的事情。
以下查询的目的是获取"非活动"的数量。当前用户的公司和所有公司(以便形成比较)的信用卡(在最近x天/周内没有进行过卡交易的信用卡)。
子查询首先获得所有信用卡的最后一张卡交易,然后父查询删除任何过期的信用卡,并根据其关联的公司对卡进行分组,以及是否认为它们是"非活动" ((UNIX_TIMESTAMP() - (14 * 86400))
用于代替PHP时间计算。
SELECT
SUM(IF(LastActivity < (UNIX_TIMESTAMP() - (14 * 86400)), 1, 0)) AS AllInactiveCards,
SUM(IF(LastActivity >= (UNIX_TIMESTAMP() - (14 * 86400)), 1, 0)) AS AllActiveCards,
SUM(IF(LastActivity < (UNIX_TIMESTAMP() - (14 * 86400)) AND lastCardTransactions.CompanyID = 15, 1, 0)) AS CompanyInactiveCards,
SUM(IF(LastActivity >= (UNIX_TIMESTAMP() - (14 * 86400)) AND lastCardTransactions.CompanyID = 15, 1, 0)) AS CompanyActiveCards
FROM CardTransactions
JOIN
(
SELECT
CardSerialNumberID,
MAX(CardTransactions.Timestamp) AS LastActivity,
CardTransactions.CompanyID
FROM CardTransactions
GROUP BY
CardTransactions.CardSerialNumberID, CardTransactions.CompanyID
) lastCardTransactions
ON
CardTransactions.CardSerialNumberID = lastCardTransactions.CardSerialNumberID AND
CardTransactions.Timestamp = lastCardTransactions.LastActivity AND
CardTransactions.CardExpiryTimestamp > UNIX_TIMESTAMP()
正在使用的索引在内部查询的CardSerialNumberID, CompanyID, Timestamp
和外部查询的CardSerialNumberID, Timestamp, CardExpiryTimestamp, CompanyID
上。
查询需要 0.4秒才能执行多次,但初始运行可以慢到 0.9 - 1.1 秒,这是一个很大的问题。使用4-5种这类查询加载页面。
我想到的一个想法是计算与此分开的例程中的整体非活动卡号,也许每天运行。这将允许我调整此查询以仅拉取单个公司的记录,从而减少数据集并降低查询时间。但是,这只是一个临时修复,因为数据库将继续增长,直到分析相同数量的数据。
注意:上面的查询字段已被修改,使其更通用,因为此查询所使用的特定主题非常复杂。因此,没有DB模式可供给(如果有的话,你需要一个10,000,000+记录的数据集来测试我认为的查询)。我正在寻找一个概念性修复,而不是任何人实际给我一个调整后的查询。
非常感谢任何帮助!
答案 0 :(得分:1)
您正在查询表事务两次,因此您的查询的大小为Transactions x Transactions,这可能很大。
一个想法是监控过去x天/周的所有信用卡,并将它们保存在每天更新的额外表INACTIVE_CARDS中(添加一个包含不活动天数的字段)。然后,您可以将子查询中的SELECT限制为仅在INACTIVE_CARDS中搜索
SELECT
CardSerialNumberID,
MAX(Transactions.Timestamp) AS LastActivity,
Transactions.CompanyID
FROM Transactions
WHERE CardSerialNumberID in INACTIVE_CARDS
GROUP BY
Transactions.CardSerialNumberID, Transactions.CompanyID
当然,卡片可能在过去一小时内变为活动状态,但您无需检查所有交易。
答案 1 :(得分:0)
请为Transactions
的两个实例使用不同的“别名”。你所拥有的是令人困惑的阅读。
内部GROUP BY
:
SELECT card_sn, company, MAX(ts)
FROM Trans
GROUP BY card_sn, company
现在这个指数对于内部是好的(“覆盖”):
INDEX(CardSerialNumberID, CompanyID, Timestamp)
建议自己测试(计时)子查询。
对于外部查询:
INDEX(CardSerialNumberID, Timestamp, -- for JOINing (prefer this order)
CardExpiryTimestamp, CompanyID) -- covering (in this order)
请将CardTransactions.CardExpiryTimestamp > UNIX_TIMESTAMP()
移至WHERE
条款。对于读者来说,ON
子句仅包含 将两个表绑定在一起的条件是有帮助的。 WHERE
包含任何其他过滤。 (无论您在何处放置该子句,优化器都会运行此查询。)
喔。可以在子查询中应用该过滤器吗?它将使子查询运行得更快。 (它可能影响最佳INDEX
,所以我等待你的回答。)
我假设大多数行都没有“过期”。如果他们有,那么其他技术可能会更好。
要获得更好的性能,请查看构建和维护信息的摘要表。或者,也许,重建(每日)一张包含这些统计数据的表格。然后引用摘要表而不是原始数据。
如果这不起作用,请考虑在网页开头使用“4-5”信息构建临时表,然后将其输入tmp表。
答案 2 :(得分:0)
而不是重复计算 - 14天和当前的UNIX_TIMESTAMP(),请遵循以下建议 https://code.tutsplus.com/tutorials/top-20-mysql-best-practices--net-7855 然后在SELECT .....之前。
代码类似于:
$uts_14d = UNIX_TIMESTAMP() - (14 * 86400);
$uts = UNIX_TIMESTAMP();
并将($ uts_14d和$ uts)变量替换为5行代码?