我假设“搜索多个字段”正在谈论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
笔记:
- 查询规划器将把所有 RA 的东西都弄清楚;不要担心这个“嵌套”,因为没有依赖子查询。
- 我避免使用隐式交叉连接,因为我觉得它们会混淆大多数查询,这种情况是一个特别好的例子。
- 我已经“作弊”并在派生查询中添加了一个 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。