1

基本上我有三个 MySQL 表:

用户- 包含有关用户的基本信息
字段- 描述所述用户的附加字段(例如位置、出生日期等)
数据- 包含通过指向字段表的链接描述的用户数据

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

用户:

 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可以做到这样的事情吗?

4

3 回答 3

1

我假设“搜索多个字段”正在谈论Entity-Attribute-Value 结构

在这种情况下,我建议第一步是创建一个派生查询 - 基本上,我们希望将“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/etc。

然后我们可以将所有这些用作派生查询:

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。

虽然上述内容很好地获得了“或”语义(它更容易而且我可能误读了这个问题),但需要进行修改才能获得“与”语义。这里有一些方法可以编写派生查询来获得这样的结果。(在这一点上,我必须向托尼道歉——我忘记了我已经完成了在我的环境中生成此类查询的所有工作。)

计算匹配数以确保所有行都匹配。这仅在每个实体对每个用户都是唯一的情况下才有效。它还消除了对 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(见托尼的回答)。

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-]selects 出现在 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。

于 2013-10-01T21:15:52.047 回答
0

Well here you hit one of the downsides of the EAV you are using

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`

Messy isn't it? Bet you can't wait to go for, four or five values. Or think about (Forename = Joe Or surname = Bloggs) and City = London...

于 2013-10-01T21:22:53.640 回答
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`

不过我更喜欢加入

于 2013-10-01T21:17:43.673 回答