【问题标题】:Database schema and queries for activity stream in social network社交网络中活动流的数据库模式和查询
【发布时间】:2015-05-15 15:59:36
【问题描述】:

首先,我不是 DBA 或 SQL 专家。但是我已经承担了一个个人项目,该项目要求我在制作社交网络时要全力以赴。 (不,我不是要重塑 Facebook。我的目标是小众受众。)是的,我听说过诸如 http://activitystrea.ms/ 之类的框架,但我觉得数据序列化应该是满足我需求的最后手段.

不管怎样,How to implement the activity stream in a social network 帮我搞定了,但我还有一些未解决的问题。

以下是我的数据库架构(为简化起见,省略了一些行):

动作表:

id    name
-------------
1     post
2     like
3     follow
4     favorite
5     tag
6     share

活动表:

id             (int)
user_id        (int)
action_id      (tinyint)
target_id      (int)
object_id      (tinyint)
date_created   (datetime)

object_id 指的是target_id 是哪个对象类型。这里的思路是表示(User + Action + Target Object)

  • 用户帖子媒体
  • 用户最喜欢的场景
  • 用户关注用户

对象(类型)表:

id    name
-------------
1     media
2     scene
3     brand
4     event
5     user

这里的问题是每个object 都有自己单独的表。例如:

媒体表:

id            (int)
type          (int)
thumbnail     (varchar)
source        (varchar)
description   (varchar)

事件表:

id        (int)
user_id   (int)
name      (varchar)
city      (int)
address   (varchar)
starts    (time)
ends      (time)
about     (varchar)

用户表:

id                (int)
username          (varchar)
profile_picture   (varchar)
location          (int)

那么,查询该数据库的最佳(即最有效)方法是什么?

显然我可以在activity 表上执行SELECT 语句,然后基于object_id 使用PHP 中的条件逻辑对适当的object 表进行单独查询(例如, media)。

或者在所有 5 个object 表上实现某种左或内JOIN 会更聪明(更有效)吗,如下所示:MySQL if statement conditional join。我并不完全熟悉 JOINS 的工作原理,以及 SQL 是否足够聪明,只为每个 activity 行扫描适当的 object 表,而不是所有连接的表。

当然,第一个解决方案意味着对数据库的更多调用,这是不太可取的。但是,我不确定如何在不实现某些条件逻辑的情况下仅在一个查询中检索所有相关列(例如,media“source”、event“address”)。

【问题讨论】:

  • 如果您像在多对多关系中那样为每种对象类型创建一个连接表会怎样。有了这种机制,您可以在一个查询中加入所有内容,而无需条件连接,只需获取实际相互关联的值。
  • @RaphaelMüller 听起来像是一个潜在的解决方案。你能告诉我这可能看起来像什么(即数据库模式)吗?就像我说的,我不是 SQL 专家,所以我很难想象它。
  • 我只是想到了另一种可能的解决方案。在对活动表进行标准查询后,我可以遍历活动行,为每个对象类型及其 target_id(s) 创建一个数组。然后我可以像这样查询每个对象表(select * from media where id in (1, 2, 3, 4...)),并使用 php 条件逻辑来显示结果。这样,我最多只能进行 6 个查询(假设有 5 个对象)。这是我想出的最简单的解决方案,但还是太复杂了吗?
  • 这个使用 php 逻辑的解决方案也是我的第二个想法;)我使用类似的东西来用模块组合动态内容(每个模块都有自己的模板,后面有对应的表)

标签: php mysql sql database social-networking


【解决方案1】:

假设,你稍微改变一下你的活动表:

活动表:

id             (int)
user_id        (int)
action_id      (tinyint)
object_id      (tinyint)
date_created   (datetime)

以及每个目标类型的连接表:

activity_id    (int)
target_id      (int)

最后是您的目标表(媒体)

id            (int)
type          (int)
thumbnail     (varchar)
source        (varchar)
description   (varchar)

和目标表(事件)

id        (int)
user_id   (int)
name      (varchar)
city      (int)
address   (varchar)
starts    (time)
ends      (time)
about     (varchar)

现在,您可以选择数据

SELECT
 activity.id,
 activity.user_id,
 activity.action_id,
 action.name,
 activity.object_id,
 object.name,
 media.id as media_id,
 media.type,
 media.thumbnail,
 media.source,
 media.description,
 event.id as event_id,
 event.name,
 ...
FROM
 activity
 LEFT JOIN action ON (action.id = activity.action_id)
 INNER JOIN mediaToActivity ON (mediaToActivity.activity_id = activity.id)
 LEFT JOIN media ON (media.id = mediaToActivity.target_id)
 INNER JOIN eventToActivity ON (eventToActivity.activity_id = activity.id)
 LEFT JOIN event ON (event.id = eventToActivity.target_id)

使用此查询,您应该在一个查询中获取所有行(但只有实际存在的行才会填充数据)

注意,我现在还没有测试过这个......

【讨论】:

  • 有趣.. 感谢您的解释!这种解决方案是否只能针对每种目标类型使用单独的连接表,还是可以使用原始模式完成相同的查询?
  • 如果您在旧布局上使用普通的LEFT JOIN,它也应该可以工作。然后相应的应该变成NULL
  • 啊,我明白了。我明天将不得不测试这些解决方案并报告。寻找最有效的方法!
  • 我刚刚注意到,如果您使用旧版式,则必须在 JOIN 中进行限制,否则您的 target_id 必须在所有表中都是唯一的。例如您的 JOIN 可能如下所示:LEFT JOIN event ON (event.id = activity.target_id AND activity.object_id = 4)
  • 我测试了这个解决方案(使用原始模式 - 没有单独的连接表),它完全符合我的需要。显然,每一行中有很多 NULL 值,但我不知道如何避免这种情况。 (我不确定这在性能方面是否很重要?)无论如何,谢谢。这似乎是最简单的解决方案,但我欢迎其他人提出建议!
【解决方案2】:

我从您的讨论中拼凑出您的解决方案。 Fiddle

create table activity (
  id            int,   
  user_id       int,
  action_id     int,
  target_id     int,
  object_id     int,
  date_created  datetime
);
create table action (
  id int,
  name varchar(80)
);
create table object (
  id int,
  name varchar(80)
);
create table media (
  id int,
  type int,
  thumbnail varchar(255),
  source varchar(255),
  description varchar(255)
);
create table event (
  id       int,
  user_id   int,
  name      varchar(255),
  city      int,
  address   varchar(255),
  starts    time,
  ends      time,
  about     varchar(255)
);

-- setup
insert into action values (1, "post");
insert into object values (1, "media");
insert into object values (2, "event");

-- new event
insert into event values (1, null, "breakfast", null, "123 main st", null, null, "we will eat");
insert into activity values (1, null, 1, 1, 2,  null);

-- new media
insert into media values (1, null, null, null, "new media");
insert into activity values (2, null, 1, 1, 1,  null);

SELECT *
FROM
 activity
 left join event on (event.id = activity.target_id and activity.object_id = 2)
 left join media on (media.id = activity.target_id and activity.object_id = 1);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 2016-10-02
    • 2016-09-27
    相关资源
    最近更新 更多