【发布时间】:2010-10-20 07:14:35
【问题描述】:
我正在开发一个报告系统,该系统允许用户任意查询一组事实表,并限制每个事实表的多个维度表。我编写了一个查询构建器类,它根据约束参数自动组装所有正确的连接和子查询,并且一切都按设计工作。
但是,我感觉我没有生成最有效的查询。在一组有几百万条记录的表上,这些查询大约需要 10 秒才能运行,我希望将它们降低到不到一秒的范围内。我有一种感觉,如果我能去掉子查询,结果会更有效率。
我不会向您展示我的实际架构(它要复杂得多),而是向您展示一个类似的示例来说明这一点,而无需解释我的整个应用程序和数据模型。
假设我有一个音乐会信息数据库,其中包含艺术家和场地。用户可以任意标记艺术家和场地。所以架构看起来像这样:
concert
id
artist_id
venue_id
date
artist
id
name
venue
id
name
tag
id
name
artist_tag
artist_id
tag_id
venue_tag
venue_id
tag_id
很简单。
现在假设我想查询数据库中今天一个月内发生的所有音乐会,所有带有“techno”和“长号”标签的艺术家,在音乐会上表演“cheap-beer”和“great-mosh-”坑的标签。
我能想到的最佳查询如下所示:
SELECT
concert.id AS concert_id,
concert.date AS concert_date,
artist.id AS artist_id,
artist.name AS artist_name,
venue.id AS venue_id,
venue.name AS venue_name,
FROM
concert
INNER JOIN (
artist ON artist.id = concert.artist_id
) INNER JOIN (
venue ON venue.id = concert.venue_id
)
WHERE (
artist.id IN (
SELECT artist_id
FROM artist_tag
INNER JOIN tag AS a on (
a.id = artist_tag.tag_id
AND
a.name = 'techno'
) INNER JOIN tag AS b on (
b.id = artist_tag.tag_id
AND
b.name = 'trombone'
)
)
AND
venue.id IN (
SELECT venue_id
FROM venue_tag
INNER JOIN tag AS a on (
a.id = venue_tag.tag_id
AND
a.name = 'cheap-beer'
) INNER JOIN tag AS b on (
b.id = venue_tag.tag_id
AND
b.name = 'great-mosh-pits'
)
)
AND
concert.date BETWEEN NOW() AND (NOW() + INTERVAL 1 MONTH)
)
查询有效,但我真的不喜欢有多个子查询。如果我可以完全使用 JOIN 逻辑来完成相同的逻辑,我感觉性能会大大提高。
在理想情况下,我会使用真正的 OLAP 服务器。但是我的客户将部署到 MySQL 或 MSSQL 或 Postgres,我不能保证兼容的 OLAP 引擎将可用。所以我坚持使用带有星型模式的普通 RDBMS。
不要太在意这个例子的细节(我的真实应用程序与音乐无关,但它有多个事实表,与我在这里展示的关系相似)。在这个模型中,'artist_tag' 和 'venue_tag' 表用作事实表,而其他一切都是维度。
重要的是要注意,在此示例中,如果我只允许用户限制单个艺术家标签或场地标签值,则查询编写起来要简单得多。只有当我允许查询包含 AND 逻辑时,它才会变得非常棘手,需要多个不同的标签。
那么,我的问题是:您知道针对多个事实表编写高效查询的最佳技术是什么?
【问题讨论】:
-
我觉得这里的症结确实是查询的AND性质,而不是“多个事实表”。 (尽管它们确实相互复合。)我在下面给出的答案通过在 HAVING 子句中执行查询的 AND 组件来解决这个问题,而不是多次连接到相同的事实表。
-
时间标记为已解决/关闭/... :)
标签: sql performance olap fact-table dimensional-modeling