基于一对多关系构建 SQL 查询

发布于 2024-12-10 17:23:52 字数 751 浏览 0 评论 0原文

我有一个搜索页面,我试图在两个表上构建复杂的搜索条件,看起来像:

Users
ID     NAME
1      Paul
2      Remy

...

Profiles
FK_USERS_ID   TOPIC      TOPIC ID
1             language   1
1             language   2
1             expertise  1
1             expertise  2
1             expertise  3
2             language   1
2             language   2

第二个表配置文件,列出了每个用户的“语言”或“专业知识”(以及其他内容),主题 id 是另一个表的外键,具体取决于主题(如果主题是“语言”,则主题 ID 是语言表中语言的 ID,等等...)。

搜索需要找到类似 的内容,其中用户名 LIKE %PAU% 且用户“拥有”语言 1 且拥有语言 2、拥有专业知识 1 且拥有专业知识 2。

任何帮助将不胜感激!我正在两个表上执行LEFT JOIN,尽管我不确定这是正确的选择。我的主要问题在于“AND”。同一用户必须同时具备语言 1 和 2,并且同时具备专业知识 1 和 2。

我使用 PHP 工作,通常会尝试避免内部 SELECT 甚至联接,但我认为内部 SELECT 即将到来?

I have a search page where I am trying to build a complex search condition on two tables which look something like:

Users
ID     NAME
1      Paul
2      Remy

...

Profiles
FK_USERS_ID   TOPIC      TOPIC ID
1             language   1
1             language   2
1             expertise  1
1             expertise  2
1             expertise  3
2             language   1
2             language   2

The second table Profiles, lists the "languages" or the "expertises" (among other stuff) of each user, and topic id is a foreign key to another table depending on the topic (if topic is "language", than topic ID is the ID of a language in the languages table, etc...).

The search needs to find something like where user name LIKE %PAU% and the user "has" language 1 and has language 2 and has expertise 1 and has expertise 2.

Any help would be really appreciated! I am performing a LEFT JOIN on the two tables although I am not sure that is the correct choice. My main problem lies on the "AND". The same user has to have both languages 1 and 2, and at the same time expertise 1 and 2.

I work in PHP and I usually try to avoid inner SELECTs and even joins, but I think an inner SELECT is imminent here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

分開簡單 2024-12-17 17:23:52

您可以通过构建一组与您的个人资料表中的条件相匹配的用户来实现此目的,如下所示:

SELECT FK_USERS_ID 
FROM Profiles 
WHERE topic='x' 
AND TOPIC_ID IN (1,2) 
GROUP BY FK_USERS_ID 
HAVING COUNT(1) = 2

在此处列出与您需要的主题相匹配的用户。通过按用户 ID 分组并指定应返回的行数,您可以有效地说“仅那些在主题 z 中具有 x 和 y 的行。只需确保 COUNT(1) = x 具有相同数量的不同行 解释

然后您可以查询用户表

SELECT ID 
FROM Users 
WHERE name like '%PAU%' 
AND ID IN (<insert above query here>)

您也可以在联接和派生表中执行此操作,但其本质应在上面进行


如果你正在寻找多种组合,你可以使用mysql的多列IN:

SELECT FK_USERS_ID 
FROM Profiles 
WHERE (topic,topic_id) IN (('x',3),('x',5),('y',3),('y',6))
GROUP BY FK_USERS_ID 
HAVING COUNT(1) = 4

这将寻找与x-3、x-5、y-3和y-6对匹配的用途。

您应该能够在 php 中轻松构建 topic-topic_id 对并将其填充到 SQL 字符串中,并且只需将生成的对数计数到变量中以用于 count(1) 数字。请参阅http://www.mysqlperformanceblog .com/2008/04/04/multi-column-in-clause-unexpected-mysql-issue/ 使用此方法进行性能讨论。

You can accomplish this by building a set of users that matches the criterias from your profile tables, something like this:

SELECT FK_USERS_ID 
FROM Profiles 
WHERE topic='x' 
AND TOPIC_ID IN (1,2) 
GROUP BY FK_USERS_ID 
HAVING COUNT(1) = 2

Here you list your users that matches the topics you need. By grouping by the user id and specifying the amount of rows that should be returned, you can effectively say "only those that has x and y in topic z. Just make sure that the COUNT(1) = x has the same number of different TOPIC_IDs to look for.

You can then query the user table

SELECT ID 
FROM Users 
WHERE name like '%PAU%' 
AND ID IN (<insert above query here>)

You can also do it in a join and a derived table, but the essence should be explained above.

EDIT:
if you are looking for multiple combinations, you can use mysql's multi-column IN:

SELECT FK_USERS_ID 
FROM Profiles 
WHERE (topic,topic_id) IN (('x',3),('x',5),('y',3),('y',6))
GROUP BY FK_USERS_ID 
HAVING COUNT(1) = 4

This will look for uses matching the pairs x-3, x-5, y-3 and y-6.

You should be able to build the topic-topic_id pairs easily in php and stuffing it into the SQL string, and also just counting the number of pairs you generate into a variable for using for the count(1) number. See http://www.mysqlperformanceblog.com/2008/04/04/multi-column-in-clause-unexpected-mysql-issue/ for performance talk using this approach.

萌︼了一个春 2024-12-17 17:23:52

不就是一个简单的经典INNER JOIN吗?

SELECT
  p.topic, p.topic_id
FROM
  profiles p
INNER JOIN
  users u
ON
  u.id = p.fk_users_id
WHERE
  u.name LIKE '%Paul%'

此查询将返回与模式匹配的用户的所有语言和专业知识及其 ID,在本例中,用户的名字中包含 Paul。这是你喜欢的吗?或者其他什么?

Isn't it just a simple classical INNER JOIN?

SELECT
  p.topic, p.topic_id
FROM
  profiles p
INNER JOIN
  users u
ON
  u.id = p.fk_users_id
WHERE
  u.name LIKE '%Paul%'

This query would return all the languages and expertise with their IDs for the users matching the pattern, in this case containing Paul in their name. Is this what you like? Or something else?

尘世孤行 2024-12-17 17:23:52
select *
from users u, profiles p
where u.id = p.fk_users_id
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'language' 
            and topic_id = 1)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'language' 
            and topic_id = 22)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'expertise' 
            and topic_id = 1)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'expertise' 
            and topic_id = 1)
and u.name like '%PAU%'

编辑:

好的,@cairnz 的答案略有不同:

SELECT ID 
FROM Users 
WHERE name like '%PAU%' 
AND ID IN (SELECT FK_USERS_ID 
           FROM Profiles 
           WHERE topic='x' 
           AND ((TOPIC_ID = 1 AND TOPIC = 'language') 
                OR (TOPIC_ID = 2 AND TOPIC = 'language')
                OR (TOPIC_ID = 1 AND TOPIC = 'expertise')
                OR (TOPIC_ID = 2 AND TOPIC = 'expertise'))
           GROUP BY FK_USERS_ID 
           HAVING COUNT(1) = 4)
select *
from users u, profiles p
where u.id = p.fk_users_id
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'language' 
            and topic_id = 1)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'language' 
            and topic_id = 22)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'expertise' 
            and topic_id = 1)
and exists (select 1 
            from profiles 
            where fk_users_id = u.id 
            and topic = 'expertise' 
            and topic_id = 1)
and u.name like '%PAU%'

EDIT:

Ok, a slight variation on @cairnz' answer:

SELECT ID 
FROM Users 
WHERE name like '%PAU%' 
AND ID IN (SELECT FK_USERS_ID 
           FROM Profiles 
           WHERE topic='x' 
           AND ((TOPIC_ID = 1 AND TOPIC = 'language') 
                OR (TOPIC_ID = 2 AND TOPIC = 'language')
                OR (TOPIC_ID = 1 AND TOPIC = 'expertise')
                OR (TOPIC_ID = 2 AND TOPIC = 'expertise'))
           GROUP BY FK_USERS_ID 
           HAVING COUNT(1) = 4)
北渚 2024-12-17 17:23:52

我会根据您“需要”的每个条件多次执行 JOIN 条件。我还将确保基于要查找的键的每个部分在配置文件表上建立索引...(FK_User_ID、Topic_ID、主题)

SELECT STRAIGHT_JOIN
      U.ID 
   FROM Users U
      JOIN Profiles P1
         on U.ID = P1.FK_User_ID
         AND P1.Topic_Id = 1
         AND P1.Topic = "language"
      JOIN Profiles P2
         on U.ID = P2.FK_User_ID
         AND P2.Topic_Id = 2
         AND P2.Topic = "language"
      JOIN Profiles P3
         on U.ID = P3.FK_User_ID
         AND P3.Topic_Id = 1
         AND P3.Topic = "expertise"
      JOIN Profiles P4
         on U.ID = P4.FK_User_ID
         AND P4.Topic_Id = 2
         AND P4.Topic = "expertise"
   WHERE
      u.name like '%PAU%' 

这样,提供的其他答案中表达的任何其他标准都不会有太大影响。这些表是按标准设置的,就好像同时进行一样,如果有任何缺失,它们将立即从结果中排除,而不是尝试对每个条目进行子选择计数(我认为这可能是您遇到的滞后) 。

因此,您的每个“必需”条件都将采用相同的“JOIN”构造,正如您所看到的,我只是增加连接实例的“别名”。

I would do based on JOIN conditions multiple times against each condition that you are "requiring". I would also ensure an index on the Profiles table based on the each part of the key looking for... (FK_User_ID, Topic_ID, Topic)

SELECT STRAIGHT_JOIN
      U.ID 
   FROM Users U
      JOIN Profiles P1
         on U.ID = P1.FK_User_ID
         AND P1.Topic_Id = 1
         AND P1.Topic = "language"
      JOIN Profiles P2
         on U.ID = P2.FK_User_ID
         AND P2.Topic_Id = 2
         AND P2.Topic = "language"
      JOIN Profiles P3
         on U.ID = P3.FK_User_ID
         AND P3.Topic_Id = 1
         AND P3.Topic = "expertise"
      JOIN Profiles P4
         on U.ID = P4.FK_User_ID
         AND P4.Topic_Id = 2
         AND P4.Topic = "expertise"
   WHERE
      u.name like '%PAU%' 

This way, any additional criteria as expressed in other answer provided shouldn't be too much an impact. The tables are setup by the criteria as if simultaneous, and if any are missing, they will be excluded from the result immediately instead of trying to do a sub-select counting for every entry (which I think might be the lag you are encountering).

So, each of your "required" criteria would take the same "JOIN" construct, and as you can see, I'm just incrementing the "alias" of the join instance.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文