「複数のフィールドを検索する」とは、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 のみが選択されますが (user 関係に対して結合するために使用されるため)、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 を追加しました。これにより、ユーザーごとに最大 1 つのレコードが結合/返されることが保証され、GROUP BY の使用が回避されます。
上記は「OR」セマンティクスを適切に取得しますが (どちらも簡単で、質問を誤解している可能性があります)、「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 自体は、条件の外側に適用されると、結合セマンティクスを提供します。この場合、データを「再正規化」します。これは、select 節に [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。