如何从SQL中的相邻表中选择另一个表中列出的id?

时间:2013-04-02 15:02:38

标签: php mysql codeigniter

关于我的问题的更多背景知识。我有一个"追随者"我的网站上的侧边栏,用户可以关注其他用户。问题是,目前此侧栏中显示的用户还可以包括他们已经关注的用户。我想修改功能,这样就不再发生了。这是我目前使用的模型函数......

public function get_oldest($limit = 10, $user_id)
{
  $this->db
      ->select($this->db->dbprefix('profiles').'.*, g.description as group_name, users.*')
      ->join('groups g', 'g.id = users.group_id')
      ->join('profiles', 'profiles.user_id = users.id', 'left')
      ->group_by('users.id');
  $this->db->order_by('users.created_on', 'ASC');
  $this->db->limit($limit);
  $results = $this->db->get('users')->result_array();

  return $results;
}

这是控制器功能......

function get_oldest() {
  $this->load->model('user_account/user_account_m');

  $limit = $this->attribute('limit');

  $user_id = $this->current_user->id;

  $users = $this->user_account_m->get_oldest($limit, $user_id);
  return $users;
}

sql数据库中的" Follower"(default_follow)表具有以下结构......

  

id =关注事件的身份
   follower_id =正在执行以下操作的人的user_id
   followed_id =被关注者的user_id

sql数据库中的" Profile"(default_profiles)表具有以下结构......

user_id
display_name
first_name
last_name

我知道我需要以某种方式使用当前用户的ID来实现这一点,我只是很难弄清楚如何使用。在此先感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

在sql中设置差异的一种方法是EXCEPT关键字。我在sqlfiddle上放了一个玩具渲染: http://sqlfiddle.com/#!6/2308a/6

SELECT distinct user_id
FROM users
EXCEPT 
SELECT followed_id
FROM follow
WHERE follower_id = <idhere>

顶级sql将获取系统中的所有user_id。底部将抓取当前用户关注的所有人,并且EXCEPT关键字将应用设置差异以向您提供当前用户尚未关注的所有用户。

答案 1 :(得分:1)

您正在寻找的功能称为子查询。

在SQL中,您的CodeIgniter ActiveRecord查询大致翻译为:

SELECT profiles.*, g.description as group_name, users.*
INNER JOIN groups g ON g.id = users.group_id
LEFT JOIN profiles ON profiles.user_id = users.id
GROUP BY users.id
ORDER BY users.created_on;

要过滤掉现有的关注者,我们将为使用IN关键字和子查询的子句添加WHERE子句:

SELECT profiles.*, g.description as group_name, users.*
INNER JOIN groups g ON g.id = users.group_id
LEFT JOIN profiles ON profiles.user_id = users.id
WHERE users.id NOT IN (SELECT follower_id FROM default_follow WHERE followed_id = ?)
GROUP BY users.id
ORDER BY users.created_on;

?表示一个查询参数,在您的情况下,它将是登录用户的ID。

转换回CodeIgniter,你有几个选择:

1)将NOT IN子句直接写入CodeIgniter的“where”函数调用,连接用户ID。

$this->db
        ->select($this->db->dbprefix('profiles').'.*, g.description as group_name, users.*')
        ->join('groups g', 'g.id = users.group_id')
        ->join('profiles', 'profiles.user_id = users.id', 'left')
        ->where('users.id NOT IN (SELECT follower_id FROM default_follow WHERE followed_id = '.$user_id.')', NULL, FALSE)
        ->group_by('users.id');

2)将子查询分解为单独的查询,并在第二个查询中使用结果:

$this->subquery->select('follower_id')
               ->where('followed_id', $user_id);
$followers = $this->subquery->get('default_follow')->result_array();
$this->db
        ->select($this->db->dbprefix('profiles').'.*, g.description as group_name, users.*')
        ->join('groups g', 'g.id = users.group_id')
        ->join('profiles', 'profiles.user_id = users.id', 'left')
        ->where_not_in('users.id', $followers)
        ->group_by('users.id');

选项1很好,因为它允许数据库完成所有工作,但是根据$ user_id来自哪里,您可能会打开SQL注入攻击。您希望提前对输入进行消毒。

选项2对SQL注入是安全的,但迫使PHP完成一些工作。它不会那么快,特别是对于拥有大量粉丝的用户。有些数据库对可以包含在显式IN子句中的元素数量有限制(特别是,Oracle将你限制为1000),但看起来你正在使用没有这种限制的mySQL。

我希望有一个选项三,但CodeIgniter的ActiveRecord功能不提供对子查询的原生支持(尚未)。鉴于上述选项,我将采用选项1并确保自己防止SQL注入。

这是使用CodeIgniter ActiveRecord查询的一个不错的参考:

http://ellislab.com/codeigniter/user-guide/database/active_record.html