我通过添加负载和大量项目来强调测试应用程序,并强迫它做很多工作。
select *, (
select price
from prices
WHERE widget_id = widget.id
ORDER BY id DESC
LIMIT 1
) as maxprice
FROM widgets
ORDER BY created_at DESC
LIMIT 20 OFFSET 0
查询在使用基本Heroku共享数据库的测试环境中超时。 (使用最大5g的193mb)
什么能解决这个超时问题?价格每小时更新一次,因此您每小时可获得8500x新行。
这个应用程序的数量非常多(实际上它不太可能有8500个小部件),但我想知道什么是合适的解决方案?
我的查询愚蠢吗? (即,进行该子选择是一种糟糕的查询方式 - 我的SQL知识很糟糕,这个项目的目标之一就是改进它!)
或者我只是达到了共享数据库的限制,并且应该在价格表的大小下进入专用数据库(例如Heroku的每月最低200美元专用postgres实例)。就我如何设计数据库而言,是否存在更深层次的问题? (即它是一对多,一个小部件有很多价格。)有更明智的方法吗?
我对sql和查询等大规模的世界全新,因此上面表达的完全无知。 :)
答案 0 :(得分:1)
@Dave想要每个小部件latest price
。您可以在子查询和每个小部件LIMIT 1
中执行此操作,但在现代PostgreSQL中,窗口函数可以更优雅地完成工作。考虑first_value()
/ last_value()
:
SELECT w.*
, first_value(p.price) OVER (PARTITION BY w.id
ORDER BY created_at DESC) AS latest_price
FROM (
SELECT *
FROM widgets
ORDER BY created_at DESC
LIMIT 20
) w
JOIN prices p ON p.widget_id = w.id
GROUP BY w.col1, w.col2 -- spell out all columns of w.*
SELECT w.*
, max(p.price) AS max_price
FROM (
SELECT *
FROM widgets
ORDER BY created_at DESC
LIMIT 20
) w
JOIN prices p ON p.widget_id = w.id
GROUP BY w.col1, w.col2 -- spell out all columns of w.*
修复表别名。
检索widgets
的所有列,例如问题演示
在PostgreSQL 8.3中,您必须拼出SELECT
子句中GROUP BY
列表的所有非聚合列。在PostgreSQL 9.1或更高版本中,主键列将覆盖整个表。我引用手册here:
在主要时,允许查询目标列表中的非GROUP BY列 key在GROUP BY子句中指定
我建议永远不要使用像maxWidgetPrice
这样的mixed case identifiers。 PostgreSQL默认情况下将不带引号的标识符折叠为小写。帮自己一个忙,并专门使用小写标识符。
始终在可能的情况下使用显式JOIN条件。这是规范的SQL方式,而且更具可读性。
OFFSET 0
只是噪音
然而,表现的关键是正确的索引。我会去两个这样的索引:
CREATE INDEX widgets_created_at_idx ON widgets (created_at DESC);
CREATE INDEX prices_widget_id_idx ON prices(widget_id, price DESC);
第二个是multicolumn index,在使用第一个索引确定前20个小部件后,应该为检索最大奖励提供最佳性能。不确定PostgreSQL 8.3(Heroku共享数据库的默认值)是否足够智能以充分利用它。 PostgreSQL 9.1肯定是。
对于最新价格(请参阅评论),请改用此索引:
CREATE INDEX prices_widget_id_idx ON prices(widget_id, created_at DESC);
你不必(也不应该)只相信我。使用带有和不带索引的EXPLAIN ANALYZE测试性能和查询计划,并亲自查看。索引创建应该非常快,即使是一百万行。
如果您考虑在Heroku上切换到独立的PostgreSQL数据库,您可能会对this recent Heroku blog post感兴趣:
答案 1 :(得分:0)
我不清楚你在问什么,但这是我的理解:
找到您想要定价的小部件。在这种情况下,您似乎正在寻找最近的20个小部件:
SELECT w.id
FROM widgets
ORDER BY created_at DESC
LIMIT 20 OFFSET 0
对于您找到的20个小部件中的每个小部件,您似乎希望从小部件表中找到最高的关联价格:
SELECT s.id, MAX(p.price) AS maxWidgetPrice
FROM (SELECT w.id
FROM widgets
ORDER BY created_at DESC
LIMIT 20 OFFSET 0
) s -- widget subset
, prices p
WHERE s.id = p.widget_id
GROUP BY s.id
需要对price.widget_id建立索引才能使其生效。如果它相对较大,您不希望每次都处理整个价格表,只需要您需要的行子集。
编辑:添加“分组依据”(并且没有,这没有经过测试)