根据另一个表中的多个行搜索表

时间:2013-10-01 21:05:24

标签: mysql sql entity-attribute-value relational-division

基本上我有三个MySQL表:

用户 - 包含有关用户的基本信息 字段 - 描述所述用户的其他字段(例如位置,dob等) 数据 - 包含通过字段表链接描述的用户数据

基本设计如下(以下是精简版)

用户:

 ID | username | password | email | registered_date

字段

 ID | name | type

数据:

 ID | User_ID | Field_ID | value

我想要做的是按用户所拥有的字段的值搜索用户,例如示例字段可能是:

全名
镇/市
邮编

我有以下内容,当您只想搜索一个字段时,它会起作用:

SELECT `users`.`ID`,
       `users`.`username`,
       `users`.`email`,
       `data`.`value`,
       `fields`.`name`

FROM `users`,
     `fields`,
     `data`

WHERE `data`.`Field_ID` = '2'
AND `data`.`value` LIKE 'london'
AND `users`.`ID` = `data`.`User_ID`
AND `data`.`Field_ID` = `fields`.`ID`

GROUP BY `users`.`ID`

但是如果要搜索多个字段呢?例如说我要搜索全名“Joe Bloggs”,城镇/城市设置为“伦敦”?这对我来说是一个真正的难点。

MySQL是否可以这样?

3 个答案:

答案 0 :(得分:1)

我假设“搜索多个字段”正在讨论Entity-Attribute-Value structure

在这种情况下,我建议第一步是创建一个派生查询 - 基本上,我们希望将“已加入的EAV数据”限制为包括具有我们的值的记录有兴趣找到。 (我改变了一些列名,但同样的前提仍然存在。)

SELECT d.userId
FROM data d
JOIN fields f
  ON f.fieldId = d.fieldId
-- now that we establish data/field relation, filter rows
WHERE f.type = "location" AND d.value = "london"
   OR f.type = "job" AND d.value = "programmer"

此结果行来自符合我们条件的过滤 EAV三元组。在这种情况下只选择userId(因为它将用于连接用户关系),但也可以通过推送fieldId / value /等。

然后我们可以使用所有作为派生查询:

SELECT * 
FROM users u
JOIN (
  -- look, just goes in here :)
  SELECT DISTINCT d.userId
  FROM data d
  JOIN fields f
    ON f.fieldId = d.fieldId
  WHERE f.type = "location" AND d.value = "london"
     OR f.type = "job" AND d.value = "programmer"
) AS e
ON e.userId = u.userId

注意:

  1. 查询计划程序会将所有RA内容都计算出来,并且非常敏锐;不要担心这种“嵌套”,因为没有依赖子查询。
  2. 我避免使用隐式交叉连接,因为我觉得它们混淆了大多数查询,这种情况是一个特别好的例子。
  3. 我“欺骗”并在派生查询中添加了DISTINCT。这将确保每个用户最多可以加入/返回一条记录,并避免使用GROUP BY。

  4. 虽然上面的语义很好(它更容易,我可能误读了这个问题),但需要修改才能获得“AND”语义。以下是一些可以编写派生查询的方法。 (此时我必须向Tony道歉 - 我忘记了我已经完成了所有的管道工作,以便在我的环境中轻松地生成这些查询。)

    计算匹配数以确保所有行匹配。这仅在每个实体每个用户都是唯一的情况下才有效。它还消除了DISTINCT保持正确多样性的需要。

    SELECT d.userId
    FROM data d
    JOIN fields f
      ON f.fieldId = d.fieldId
    -- now that we establish data/field relation, filter rows
    WHERE f.type = "location" AND d.value = "london"
       OR f.type = "job" AND d.value = "programmer"
    GROUP BY d.userId
    HAVING COUNT(*) = 2
    

    找到相交的匹配项:

    SELECT d.userId
    FROM data d
    JOIN fields f ON f.fieldId = d.fieldId  
    WHERE f.type = "location" AND d.value = "london"
    INTERSECT
    SELECT d.userId
    FROM data d
    JOIN fields f ON f.fieldId = d.fieldId  
    WHERE f.type = "job" AND d.value = "programmer"
    

    使用JOINS(见Tony的回答)。

    SELECT d1.userId
    FROM data d1
    JOIN data d2   ON d2.userId = d1.userId
    JOIN fields f1 ON f1.fieldId = d1.fieldId
    JOIN fields f2 ON f2.fieldId = d2.fieldId
    -- requires AND here across row
    WHERE f1.type = "location" AND d1.value = "london"
      AND f2.type = "job"      AND d2.value = "programmer"
    

    内部JOIN本身在条件之外应用时提供连接语义。在这种情况下,我显示“重新规范化”数据。这也可以这样编写,使得[sub-]选择出现在select子句中。

    SELECT userId
    FROM (
      -- renormalize, many SO questions on this
      SELECT q1.userId, q1.value as location, q2.value as job
      FROM (SELECT d.userId, d.value
         FROM data d
         JOIN fields f ON f.fieldId = d.fieldId
         WHERE f.type = "location") AS q1
      JOIN (SELECT d.userId, d.value
         FROM data d
         JOIN fields f ON f.fieldId = d.fieldId
         WHERE f.type = "job") AS q2
      ON q1.userId = q2.userId
    ) AS q
    WHERE location = "london"
      AND job = "programmer"
    

    通过代码可以相对容易地生成上述重复性,并且某些数据库(例如SQL Server)支持CTE,这使得编写更加简单。 YMMV。

答案 1 :(得分:0)

如果我理解你的话,这就是你想要的:

FROM `users`,
     `fields`,
     `data` `location`
     `data` `name`

WHERE `location`.`Field_ID` = '2'
AND `location`.`value` LIKE 'london'
AND `location`.`Field_ID` = `fields`.`ID`
AND `name`.`Field_ID` = 'whathere? something for its name'
AND `name`.`value` LIKE 'london'
AND `name`.`Field_ID` = `fields`.`ID`
AND `users`.`ID` = `data`.`User_ID`

我更喜欢加入

答案 2 :(得分:0)

在这里,你遇到了正在使用的EAV的一个缺点

SELECT u.ID, u.username,u.email, d1.value, f1.Name, d2.Value, f2.name
FROM `users` u,
inner join data d1 On d1.User_id = u.id
inner join data d2 On d2.User_id = u.id
inner join fields f1 on f1.id = d1.field_id
inner join fields f2 on f2.id = d2.field_id
WHERE d1.Field_id = '2' and d1.Value = 'london'
and d2.field_id = '??' and d2.value = 'Joe Bloggs' 
GROUP BY `users`.`ID`

凌乱不是吗?打赌你迫不及待想要四五个值。或者考虑(Forename = Joe或surname = Bloggs)和City = London ......