基本上我有三个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是否可以这样?
答案 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
注意:
虽然上面的语义很好(它更容易,我可能误读了这个问题),但需要修改才能获得“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 ......