1

基本的に、3 つの MySQL テーブルがあります。

ユーザー- ユーザーに関する基本情報が含まれます
フィールド- そのユーザーの追加フィールドを記述します (場所、生年月日など)
データ- フィールド テーブルへのリンクを介して記述されたユーザー データが含まれます

基本的な設計は次のとおりです (以下は簡略化されたバージョンです)。

ユーザー:

 ID | username | password | email | registered_date

田畑

 ID | name | type

データ:

 ID | User_ID | Field_ID | value

私がやりたいのは、ユーザーが持っているフィールドの値でユーザーを検索することです。たとえば、フィールドの例は次のとおりです。

氏名
市区町村
郵便番号
など

私は次のものを持っています。これは、1 つのフィールドだけで検索したい場合に機能します。

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 のみが選択されますが (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

ノート:

  1. クエリ プランナーは、すべての RA 要素をピーチ キーンで把握します。依存サブクエリがないため、この「ネスト」について心配する必要はありません。
  2. ほとんどのクエリを混乱させると感じているため、暗黙的なクロス結合の使用を避けています。このケースは特に良い例です。
  3. 私は「ごまかして」、派生クエリに 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。

于 2013-10-01T21:15:52.047 に答える
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 に答える