我无法用抽象的术语解释我的问题。这是一个非常简单的问题,但我需要通过这个非常明显的例子。它是完全编造的,因此应该与类似的应用程序相媲美。
我们有一堆包含用户信息的表,我认为所有表都是规范化的,一些值仅通过 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_ID 匹配的用户?*
我如何找到进行一项和/或多项特定事件的所有用户? 我如何要求 mySQL 只显示那些用户,他们的事件数组至少匹配数组中的所有事件(这将是 AND 版本)? *我如何要求 mySQL 只显示那些用户,他们的事件数组至少包含数组中的一个事件(即 OR 版本)?*
现在真正重要的问题是:
如何将这些陈述与上面的正常陈述结合起来? 含义:我如何从纽约州找到所有喜欢冲浪、男性和喜欢 PIZZA 的用户? 或者 我如何找到来自美国的所有喜欢阅读和跳舞的用户以及超过 30 岁且喜欢 GREEN 的用户? 或者 我如何找到来自加利福尼亚州萨克拉门托的所有管道工和女性用户?
等等等,例子显然是无穷无尽的!
我相信有人会告诉我“你应该研究这个关键字”。但是因为我无法简洁地表达我的问题,所以我没有成功找到很多信息......
更新:
感谢您的回答。我被指出了一些有用的事情,这里是我不知道但现在做的事情的总结:
- 更有效地利用 JOIN
- IN 运算符
- GROUP BY 运算符结合 HAVING COUNT()
- 和子选择
感谢您向我指出这些事情! :)
最佳答案
好吧,我认为您正在寻找的关键字之一是 IN
运营商。
SELECT * FROM locations WHERE country IN ('USA', 'Canada', 'Denmark')
将返回 IN 子句中的值之一与国家字段匹配的所有行。所以就像这样写:
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 JOIN
或 RIGHT JOIN
等。通过 SELECT *
简单地检索所有数据也不是一个好习惯。 ,但实际上只选择您需要的字段。
此外,您可以/应该创建一个/多个 View 来表示您需要的联合数据并从中选择。
我如何找到某个国家或某个州的所有用户?
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 运算符,如上所示。
如何将这些陈述与上面的正常陈述结合起来?
以您的示例为例,我如何从纽约州找到所有喜欢冲浪、男性和喜欢 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。 外层选择只是从子集中的每个用户中选择数据。
现在,我没有对此进行测试,因此它可能会有所不同。并且可能有更简单的方法。至少您可以按照我之前提到的那样创建一个 View 并从中进行选择来简化此查询。
关于mysql - 在多个连接表和相关表中查找特定结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14309963/