在多个连接和相关表中查找特定结果

时间:2013-01-14 00:38:51

标签: mysql sql database search

我无法用抽象的术语解释我的问题。这是一个非常简单的问题,但我需要经历这个非常明显的例子。它完全由弥补,因此应该与simmilar应用程序相媲美。

我们有一堆包含用户信息的表,所有的表都是我认为是规范化的,有些值只是通过ID引用到其他表。

我正在使用mySQL(以及带有mysqli扩展名的PHP - 如果重要的话,我怀疑)

所以这就是我所拥有的:

table user_data

=====================================================
|| User_ID || Name || age || gender || location_ID ||
=====================================================
|| U000001 || Paul || 30  || m      || L00001      ||
|| U000002 || John || 20  || m      || L00001      ||
|| U000003 || Mike || 25  || m      || L00002      ||
|| U000004 || Anna || 25  || f      || L00003      ||


table user_personal_info

============================================
|| User_ID || color || food  || profession||
============================================
|| U000001 || red   || pizza || architect ||
|| U000002 || blue  || pasta || policeman ||
|| U000003 || green || steak || plumber   ||
|| U000004 || pink  || salad || teacher   ||


table locations

========================================================
|| location_ID || country || state      || city       ||
========================================================
|| L00001      || USA     || New York   || New York   ||
|| L00002      || USA     || New York   || Buffalo    ||
|| L00003      || USA     || California || Sacramento ||
|| L00004      || Canada  || Ontario    || Toronto    ||
|| L00005      || Canada  || Quebec     || Montreal   ||



table user_activities

=========================================
|| activity_ID  || user_ID || priority ||
=========================================
|| A0003        || U000001 || 5        ||
|| A0005        || U000001 || 4        ||
|| A0004        || U000002 || 2        ||
|| A0006        || U000002 || 1        ||
|| A0001        || U000003 || 3        ||
|| A0002        || U000004 || 4        ||
|| A0001        || U000004 || 1        ||
|| A0003        || U000004 || 5        ||

table activities

=================================
|| activity_ID  || description ||
=================================
|| A0001        || surfing     ||
|| A0002        || exercising  ||
|| A0003        || baseball    ||
|| A0004        || theater     ||
|| A0005        || dancing     ||
|| A0006        || reading     ||

好的,你明白这个概念吧?

要显示每个条目,我创建以下mySQL语句,然后在PHP中循环遍历结果集,依此类推:

SELECT * FROM user_data
JOIN user_personal_info USING (User_ID)

为了显示他们最喜欢的活动,我也必须这样做:

SELECT * FROM user_activities 
WHERE user_ID = (current user_id)

当然,我必须翻译活动ID所代表的内容以及位置ID代表的附加查询......

(顺便说一下:有没有人对如何显示所有用户及与之关联的所有字段提出更好的建议,而不是再进行两次查询?)

现在我想构建一个彻底的搜索功能来查找非常具体的用户。 我知道如何使用PHP过滤我的结果,但这需要我先下载整个数据库,这可能需要很长时间才能完成,一旦有数千名用户在数据库中。

我知道如何找到喜欢食物或颜色的男性,女性或两者的用户,他们来自特定的位置(location_ID = L00001左右)... 我知道如何分配有关年龄的规则(=,>,< ...)。我知道LIKE%?%参数。

我的问题是:

如何查找来自某个国家/地区或某个州的所有用户?
*我如何要求mySQL仅显示那些用户,哪个location_ID与location_IDs数组匹配?*

如何找到具有一个和/或更具体活动的所有用户? 我如何要求mySQL仅显示那些用户,哪些活动的数组至少匹配数组中的所有活动(即AND版本)? *我如何要求mySQL仅显示那些用户,哪些活动包含至少一个来自数组的活动(即OR版本)?*

现在真正重要的问题是:

如何将这些陈述与上述常规陈述合并? 含义:我如何找到来自纽约州的所有使用SURFING并且是MALE且喜欢PIZZA的用户? 要么 我如何找到所有来自美国的读者,跳舞者以及30岁以上且喜欢绿色的用户? 要么 我如何找到来自加利福尼亚州萨克拉门托的PLUMBERS和FEMALE的所有用户?

等。这些例子显然是无穷无尽的!

我相信有人能够告诉我“你应该研究这个关键词”。但是因为我无法以一种明确的方式表达我的问题,所以我找不到太多信息......

更新

感谢您的回答。我指出了一些有用的东西,这里是我不知道但现在做的事情的总结:

  • 更有效地利用JOIN
  • IN运算符
  • GROUP BY运算符与HAVING COUNT()
  • 结合使用
  • 和SUB SELECTS

感谢您向我指出这些事情! :)

1 个答案:

答案 0 :(得分:1)

好吧,我认为您正在寻找的其中一个关键字是IN运营商。

SELECT * FROM locations WHERE country IN ('USA', 'Canada', 'Denmark')

将返回IN子句中的一个值与country字段匹配的所有行。所以就像写这个:

SELECT * FROM locations WHERE country = 'USA' OR country = 'Canada' OR country = 'Denmark'

至于你的其他问题:

有没有人对如何显示所有用户及与其相关的所有字段提出更好的建议,而不是再进行两次查询?

只需将它们加在一起,例如:

SELECT * FROM user_data
JOIN locations ON user_data.location_ID = locations.location_ID
JOIN user_personal_info ON user_data.User_ID = user_personal_info.User_ID
JOIN user_activities ON user_personal_info.User_ID = user_activities.User_ID
JOIN activities ON user_activities.activity_ID = activities.activity_ID

当然,根据您的结构,您可以使用LEFT JOINRIGHT JOIN等。通过SELECT *简单地检索所有数据也不是一个好习惯,但实际上只是选择您需要的字段。 此外,您可以/应该创建一个或多个视图来表示您需要的联接数据,并从中选择它们。

如何查找来自某个国家/地区或某个州的所有用户?

SELECT user_data.* FROM user_data
JOIN locations ON user_data.location_ID = locations.location_ID
WHERE locations.country = 'USA' AND state = 'New York'

取决于您如何从用户获取数据以及如何为PHP中的语句准备数据。例如,假设您的用户搜索某个国家/地区,并通过post方法获取该数据:

<?php
  $country = sanitize($_POST['country']);  // assuming a sanitation function for user input
  // whether by doing a sub-select
  $sql = "SELECT user_data.* FROM user_data WHERE user_data.location_ID = (SELECT locations.location_ID FROM locations WHERE locations.country LIKE '%{$country}%')";

  // or doing a join
  $sql = "SELECT user_data.* FROM user_data JOIN locations ON user_data.location_ID = locations.location_ID WHERE locations.country LIKE '%{$country}%'";
?>

当然,同样的原则适用于国家。

如何找到具有一个和/或更具体活动的所有用户?

在这里,您需要加入活动表并使用IN运算符,如上所示。

如何将这些语句与上面的常规语句合并?

举个例子我如何找到来自纽约州的所有使用SURFING且谁是男性并且喜欢PIZZA的用户?

SELECT user_data.* FROM user_data
 JOIN locations ON user_data.locations_ID = locations.location_ID
 JOIN user_activities = user_data.User_ID = user_activities.user_ID
 JOIN activities ON user_activities.activity_ID = user_activities.activity_ID
WHERE locations.sate = 'New York'
  AND activities.description IN ('surfing')
  AND user_data.gender = 'm'
  AND user_personal_info.food = 'pizza'

希望这会有所帮助,让你朝着正确的方向前进。

<强>更新

当然,这里的IN运算符可以用description = 'surfing'代替,因为它只有一个值。如果您添加其他值description IN ('surfing', 'reading'),那么您就是正确的surfing OR reading。因此,如果您希望获得surfing AND reading的所有用户,我想我会通过子选择来实现:

SELECT user_data.* FROM user_data
 WHERE user_data.User_ID IN (
   SELECT user_activities.user_ID FROM user_activities
     JOIN activities ON user_activities.activity_ID = activities.activity_ID
    WHERE activities.description IN ('surfing', 'reading')
   GROUP BY user_activities.activity_ID
     HAVING COUNT(user_activities.user_ID) = 2
 )

因此,子选择意味着:计算以“冲浪”或“阅读”出现的每个用户ID,如果计数等于2(意味着它们与两者匹配),则检索用户ID。 外部选择只是从子集的每个用户中选择数据。

现在,我没有对此进行测试,因此可能会有所不同。而且可能有更简单的方法。至少你可以做些什么来简化这个查询,就像我之前提到的那样创建一个视图并从中进行选择。