假设我有一张顾客表和一张购买表 . 每次购买都属于一个客户 . 我想在一个SELECT语句中获取所有客户的列表以及他们上次购买的列表 . 什么是最佳做法?有关构建索引的建议吗?
请在答案中使用这些表/列名称:
-
customer:id,name
-
购买:id,customer_id,item_id,日期
在更复杂的情况下,通过将最后一次购买放入客户表中,是否(性能方面)有利于非规范化数据库?
如果(购买)ID保证按日期排序,是否可以通过使用 LIMIT 1
之类的内容简化语句?
9 回答
试试这个,这会有所帮助 .
我在我的项目中使用了这个 .
在SQLite上测试:
max()
聚合函数将确保从每个组中选择最新的购买(但假设日期列的格式为max()给出最新的 - 通常是这种情况) . 如果您想使用相同的日期处理购买,那么您可以使用max(p.date, p.id)
.在索引方面,我会在购买时使用索引(customer_id,date,[您要在选择中返回的任何其他购买列]) .
LEFT OUTER JOIN
(与INNER JOIN
相对)将确保包括从未进行过购买的客户 .另一种方法是在您的连接条件中使用
NOT EXISTS
条件来测试以后的购买:您尚未指定数据库 . 如果它是允许分析函数的那个,那么使用这种方法可能比GROUP BY更快(在Oracle中肯定更快,在SQL Server后期版本中更快可能更快,不了解其他版本) .
SQL Server中的语法是:
请试试这个,
如果你正在使用PostgreSQL,你可以使用
DISTINCT ON
来查找组中的第一行 .PostgreSQL Docs - Distinct On
请注意,
DISTINCT ON
字段(此处为customer_id
)必须与ORDER BY
子句中最左侧的字段匹配 .警告:这是一个非标准条款 .
这是在StackOverflow上定期出现的
greatest-n-per-group
问题的示例 .以下是我通常建议解决的方法:
说明:给定一行
p1
,应该没有相同客户的行p2
和更晚的日期(或者在关系的情况下,后来的id
) . 当我们发现这是真的时,p1
是该客户的最新购买 .关于索引,我将在
purchase
上创建一个复合索引(customer_id
,date
,id
) . 这可以允许使用覆盖索引来完成外连接 . 请务必在您的平台上进行测试,因为优化与实现有关 . 使用RDBMS的功能来分析优化计划 . 例如 .EXPLAIN
在MySQL上 .有些人使用子查询而不是我上面显示的解决方案,但我发现我的解决方案可以更容易地解决关系 .
您也可以尝试使用子选择执行此操作
选择应加入所有客户及其 Last 购买日期 .
我发现这个线程是我问题的解决方案 .
但是当我尝试它们时,性能很低 . 贝娄是我建议更好的表现 .
希望这会有所帮助 .