【发布时间】:2018-03-10 10:28:38
【问题描述】:
我的应用程序需要从具有主键 user_id 的 user 表中获取一些基本数据 - 以及从辅助表中获取有关用户的各种其他数据,每个辅助表都有 user_id 作为外键。有很多这样的辅助表,例如 name、addresss、phone 等等——关于一个人的事情会随着时间而改变。
更具体地说,我只需要每个辅助表的最近行中的一些值。每个表都有一个“最新”列,它是最近 UPDATE 或 INSERT 的 unix 时间戳(我们不能在此应用程序中删除)。
以下工作正常:
SELECT u.username, u.user_id, u.password, u.email, u.active
, n.first , n.middle , n.last
, uo.organization_id /* , other_cols_from_other_tables */
FROM user u
LEFT JOIN user_org uo ON (uo.user_id = u.user_id AND
uo.latest in (select max(latest) from name uo1
where uo1.user_id = u.user_id))
/* here, other LEFT JOINs like the above one */
WHERE u.username = :username
但是,由于速度慢,普遍不鼓励子查询解决方案,其中一些查询将在每个请求上运行。所以我想出了以下在某些情况下有效的方法并摆脱了子查询:
SELECT u.username, u.user_id, u.password, u.email, u.active
, n.first , n.middle , n.last
, uo.organization_id /* , other_cols_from_other_tables, etc. */
FROM user u
INNER JOIN
( SELECT user_id, MAX(latest) utd
FROM user_org
GROUP BY user_id
) uo1 ON uo1.user_id = u.user_id
LEFT JOIN user_org uo
ON (uo.user_id = u.user_id and uo.latest = uo1.utd)
/* here, other clauses like the part from 'FROM' to here */
WHERE u.username = :username
不幸的是,后者严重依赖辅助表中的数据,因此如果特定用户的任何辅助表中缺少数据,则整个查询都会失败。
我在 SO 和 www 上对此进行了研究,并且有 许多 解决方案可以避免子查询,但是我在该主题上发现的所有问题都存在于主查询中,而不是左连接中.
我需要的逻辑是“如果该辅助表中有该用户的数据,则从该表中的最近行中获取指定的列,否则为空”。
在我看来,在每个表的最新行上放置一个“当前行”标记列可以避免整个问题并且比任何其他解决方案运行得更快,但会违反规范化(我仍然必须拥有'latest' 列来维护以前数据的可排序历史记录)。
有没有获得标准化+速度的解决方案?这是 mariadb 所以它需要 Mysql 语法。
编辑:仍然想要更好的方法,但决定使用额外的列。现在避免了上面描述的问题,并且 SELECT SQL 被大大简化并且可能更快。缺点是增加了保存的复杂性,但 SELECT 更频繁。
【问题讨论】:
-
不幸的是,mySQL 还不支持分析函数(将 RowNumber 限制为 1,按您的最新排序)或交叉/外部应用,限制为 1。有一种方法可以模拟它,类似于您的但是已经完成了:stackoverflow.com/questions/36869221/cross-outer-apply-in-mysql
-
ROW_NUMBER或RANK会很棒,但 MySQL 不支持它们。CROSS APPLY获取最后一个相关行会很棒,但 MySQL 不支持它。MAX/MIN KEEP LAST会很棒,但 MySQL 不支持它。所以剩下的就是你展示的方法。但是,如果您只需要表中的一个列/值,则可以在SELECT子句中使用相关子查询和LIMIT 1。 -
编辑: 你说你在使用 MariaDB?那你为什么要标记 MySQL 呢?从 10.2 版开始,MariaDB 支持窗口函数,例如
ROW_NUMBER或RANK。 -
我已将标签从 MySQL 更改为 MariaDB。
-
有更多关于 Mysql 的文档,而 Mariadb 应该是一个替代品,所以它会全部适用,所以我认为用“Mysql”标记它对搜索者更有帮助.然而,这个特征不同,表明分歧正在开始。
标签: subquery left-join mariadb