【问题标题】:filter mysql inner join one to many results using and使用and过滤mysql内部连接一对多结果
【发布时间】:2012-05-23 00:14:13
【问题描述】:

例如,我有两个表

用户

----------------------------------
id  | name | blah
----------------------------------
1   | Samy | stackoverflow
2   | jhon | some thing
----------------------------------

技能

---------------------------------------
id | user_id | skill_title | level
---------------------------------------
1  | 1       | php         | good
2  | 1       | css         | excellent
3  | 1       | photoshop   | fair
4  | 2       | php         | good
---------------------------------------

我像这样运行查询

SELECT * FROM users 
INNER JOIN skills ON users.id = skills.user_id
WHERE ($skill_title[0] LIKE 'skills_title' AND 
       $skill_title[1] LIKE 'skills_title')

其中 $skill_title 是一个数组 我需要的是选择拥有所有这些技能的用户,即 PHP、CSS 如果我像上面那样进行查询,它永远不会带来数据,因为它将每条记录与所有数组元素进行比较,如果我用 Or 替换它会起作用,但它不会给用户带来所有技能

有什么想法吗?

【问题讨论】:

  • 一次要搜索多少技能?
  • 变量可能是 10 大或更少

标签: php mysql inner-join


【解决方案1】:

@joeshmo 的回答可能是您最好的解决方案,但也值得一试。如果您拥有大量用户,但很少有人具备您正在寻找的技能,这可能会更快:

SELECT * FROM users
WHERE
    id IN (SELECT user_id FROM skills WHERE skill_title = '$skills[0]')
    AND id IN (SELECT user_id FROM skills WHERE skill_title = '$skills[1]')
    ...

您的 PHP 脚本可能如下所示:

$skills = array("php", "css", ... ); // replace ... with the skills you are looking for
$whereClause = array();
for ($i=0, $count=count($skills); $i < $count; $i++)
    $whereClause[] = "id IN (SELECT user_id FROM skills WHERE skill_title = '{$skills[$i]}')";

$query = "SELECT * FROM users". (count($whereClause) > 0 ? " WHERE ". implode(" AND ", $whereClause) : "");


正如@joeshmo 建议的那样,另一个可能的解决方案是使用多个INNER JOINs。通过执行以下操作,您可能可以使其更小更清洁:
SELECT *
FROM
    users u
    INNER JOIN skills s1 ON u.id = s1.user_id AND s1.skill_title = '$skills[0]'
    INNER JOIN skills s2 ON u.id = s2.user_id AND s2.skill_title = '$skills[1]'
    ...

所以你的 PHP 脚本可能看起来像这样:

$skills = array("php", "css", ... );
$joins = array();
for ($i=0, $count=count($skills); $i < $count; $i++)
    $joins[] = "INNER JOIN skills s{$i} ON u.id = s{$i}.user_id AND s{$i}.skill_title = '{$skills[$i]}'";

$query = "SELECT * FROM users u ". implode(" ", $joins);

我会尝试这两种解决方案,看看哪一种对您的数据集表现更好。

【讨论】:

  • 这工作正常,但我必须关心性能,所以是否有任何标准化来解决这个问题
  • @SamyMassoud:查看更新后的答案。我还建议确保您的表上有正确的索引。至少,您可能希望在users 表上使用PRIMARY KEY (id),在skills 表上使用FOREIGN KEY (user_id) REFERENCES users (id)
【解决方案2】:
$skills = Array("php","css", ..... );
$query = "  select u.id, u.name from users u left join skills s on u.id=s.user_id "
        ."  where s.skill_title in (\"".implode($skills,"\",\"")."\") "
        ."  group by s.user_id having count(s.id) = ".count($skills);

【讨论】:

  • 我认为您没有正确使用 implode 函数。如果你用一个参数,它应该只是数组,不会有glue。如果您将它与两个参数一起使用,第一个参数是 glue (对于您的示例应该类似于 "\", \""),第二个参数应该是数组。没有三参数选项。
  • 对不起?我实际上用两个参数调用它,并且 implode 以任何顺序接受参数
  • 有趣,所以这基本上是上面的 or 版本,但你要确保他们有 n 匹配。如果他们在那里有两次 php,这将失败,对吧?
  • @guido:我的错误。我一定是看错了,以为逗号是第三个参数。不知道可以切换两个参数的顺序。
  • @joeshmo 是的,但他应该使用具有两列主键的连接表来规范化模式,问题就消失了;使用分组实际上并不像上面那样,只涉及一个连接;但是使用“in”相当慢
【解决方案3】:

我想你想为你正在寻找的每一项技能再次加入它。

SELECT * FROM users 
INNER JOIN skills as first_skill ON users.id = first_skill.user_id
INNER JOIN skills as second_skill ON users.id = second_skill.user_id
WHERE ($skill_title[0] LIKE first_skill.skill_title AND 
       $skill_title[1] LIKE second_skill.skill_title)

您的查询的问题在于它只加入了技能表一次。您的查询是这样的:

-----------------------------------------------------
id  | name | blah           | skill_title | level
-----------------------------------------------------
1   | Samy | stackoverflow  | php         | good
1   | Samy | stackoverflow  | css         | excellent
1   | Samy | stackoverflow  | photoshop   | fair
2   | jhon | some thing     | php         | good
-----------------------------------------------------

没有行同时具有 css 和 php。我的查询是这样的:

----------------------------------------------------------------------------------------------------------------
id  | name | blah           | first_skill.skill_title | first_skill.level | second_skill.skill_title | second_skill.level |
----------------------------------------------------------------------------------------------------------------
1   | Samy | stackoverflow  | php                     | good              | php                      | good               |
1   | Samy | stackoverflow  | php                     | good              | css                      | excellent          |
1   | Samy | stackoverflow  | php                     | good              | photoshop                | fair               |
... (there would be nine for Samy)
2   | jhon | some thing     | php                     | good              | php                      
----------------------------------------------------------------------------------------------------------------

第二行同时包含phpcss。那就是匹配的那个。

这对于多达十个技能来说是乏味的。您可以改为执行多个查询。

【讨论】:

  • 我不确定你所说的“$skill_title 不是静态的”是什么意思,但在我看来,这个查询 will 只会让用户使用 all 所需的技能。
  • @joeshmo:您不能将 WHERE 子句中的条件直接放在 JOIN 条件中吗?喜欢INNER JOIN skills AS first_skill ON users.id = first_skill.user_id AND first_skill.skill_title = '$skill_title[0]'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2013-01-31
  • 2020-04-11
  • 2018-12-11
  • 1970-01-01
  • 2020-10-15
相关资源
最近更新 更多