【问题标题】:"Data warehouse"-like SQLite store design“数据仓库”--like SQLite store 设计
【发布时间】:2016-07-09 15:23:12
【问题描述】:

我有兴趣为处理大量相似数据条目的应用程序设计基于 SQL(实际上是 SQLite)的存储。对于这个例子,让它成为一个聊天消息存储。

应用程序必须提供按消息参与者、标签等过滤和分析数据的能力,所有这些都暗示着 N 对 N 关系。

因此,架构(星型)将类似于:

create table messages (
    message_id INTEGER PRIMARY KEY,
    time_stamp INTEGER NOT NULL
    -- other fact fields
);

create table users (
    user_id INTEGER PRIMARY KEY,
    -- user dimension data
);

create table message_participants (
    user_id INTEGER references users(user_id),
    message_id INTEGER references messages(message_id)
);

create table tags (
    tag_id INTEGER PRIMARY KEY,
    tag_name TEXT NOT NULL,
    -- tag dimension data
);

create table message_tags (
    tag_id INTEGER references tags(tag_id),
    message_id INTEGER references messages(message_id)
);

-- etc.

所以,一切都很好,直到我必须执行基于 N 到 N 维度的分析操作和过滤。鉴于 messages 表中有数百万行和数千个维度(示例中显示的数据不止这些),所有连接对性能的影响都太大了。

例如,我想分析每个用户参与的消息数量,假设数据是根据选择的标签、选择的用户和其他方面过滤的:

select U.user_id, U.user_name, count(1)
from messages as M
join message_participants as MP on M.message_id=MP.message_id
join user as U on MP.user_id=U.user_id
where
    MP.user_id not in ( /* some user ID's set */ )
    and M.time_stamp between @StartTime and @EndTime
    and 
        -- more fact table fields filtering
    and message_id in
        (select message_id
        from message_tags
        where tag_id in ( /* some tag ID's set */ ))
    and
        -- more N-to-N filtering
group by U.user_id

我受限于 SQL,特别是 SQLite。而且我确实在表格上使用了索引。

我看不出有什么方法可以改进架构,也许是一种聪明的方法来去规范化它?

或者也许有一种方法可以以某种方式索引消息行中的维度键(我考虑过使用 FTS 功能,但不确定搜索文本索引并加入结果是否会提供任何性能影响)?

【问题讨论】:

  • 能否提供一个执行不佳的示例 SQL 语句?
  • @trincot 查看示例
  • 你是否为所有外键定义了索引?
  • @trincot 是的,这不是问题。
  • 那么我看不出为什么示例语句不能快速运行,即使有数百万条记录,尽管您可能想要添加一个限制子句。

标签: sql performance sqlite data-warehouse


【解决方案1】:

发表评论的时间过长,可能有助于提高性能,但不能直接回答您的问题(您的架构似乎很好):您是否尝试过弄乱您的查询本身?

我经常看到这种多对多的子选择过滤器,我发现在像这样的大型查询中,我经常看到通过运行 CTE/join 而不是 where blag in (subselect) 来提高性能:

;with tagMesages as (
    select distinct message_id
    from message_tags
    where tag_id in ( /* some tag ID's set */ )
) -- more N-to-N filtering
select U.user_id, U.user_name, count(1)
from messages as M
join message_participants as MP on M.message_id=MP.message_id
join user as U on MP.user_id=U.user_id
join tagMesages on M.message_id = tagMesages.message_id
where
    MP.user_id not in ( /* some user ID's set */ )
    and M.time_stamp between @StartTime and @EndTime
    and 
        -- more fact table fields filtering
group by U.user_id

我们可以看出它们是相同的,但查询规划器有时会发现这更有帮助

免责声明:我不使用 SQLite,我使用 SQL Server,如果我犯了一些明显(或其他)错误,非常抱歉。

【讨论】:

  • 谢谢,但正如我从here 了解到的那样,非递归 CTE 仅出于可读性目的而存在。
  • @galenus 是和否。它相当于join (subselect) s on s.blag = a.blag,但这不是你正在做的——你正在拉一个旧的where a.blag in (subselect),这是完全不同的。在这种情况下,我总是选择 CTE,但欢迎您在连接中使用子选择 - 它仍然与您的代码有很大不同。
  • 我的意思是 SQLite CTE 实现与嵌套子选择相同,以防我以我使用的方式使用它。在 join 的情况下,就像您提到的那样,但我们在 CTE 部分没有任何索引,因此对于大型数据集,我预计性能会更差。
  • @galenus 啊,明白了——我对 SQLite 的了解不足——很抱歉浪费你的时间。
猜你喜欢
  • 1970-01-01
  • 2017-07-19
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
相关资源
最近更新 更多