1

動的に追加されたプロパティ (転置テーブル) に従ってユーザーにクエリを実行する必要があります

私は4つのテーブルを持っています

  • ユーザー- ユーザー向け
  • propertyGroup - プロパティのグループの場合、グループをデータベースに動的に追加できます
  • propertyValue - propertyGroup の可能な値
  • usersPropertyValues - ユーザーを関連するプロパティに接続します

その外観は次のようになります。

users:
------
| id | name | details |
|----|------|---------|
|  1 |  Joe |     foo |
|  2 |  Dan |     bar |

propertyGroup:
--------------
| id |           name |
|----|----------------|
|  1 |        Hobbies |
|  2 |       LikeFood |
|  3 | VisitedCountry |

propertyValue:
--------------
| id | propertyGroupId |         name |
|----|-----------------|--------------|
|  1 |               1 | Technologies |
|  2 |               1 |      Surfing |
|  3 |               2 |         Rice |
|  4 |               2 |         Meat |
|  5 |               2 |          Veg |
|  6 |               3 |          USA |
|  7 |               3 |       FRANCE |
|  8 |               3 |       ISRAEL |
|  9 |               3 |       CANADA |

usersPropertyValues:
--------------------
| userId | propertyValueId |
|--------|-----------------|
|      1 |               1 |
|      1 |               2 |
|      1 |               3 |
|      1 |               5 |
|      1 |               6 |
|      1 |               7 |
|      1 |               8 |
|      2 |               2 |
|      2 |               3 |
|      2 |               4 |
|      2 |               5 |
|      2 |               8 |
|      2 |               9 |
|      2 |               7 |

したがって、mix-em-all クエリは次のようになります。

select * 
from users as u
 join usersPropertyValues as upv on upv.userId = u.id
 join propertyValues as pv on pv.id = upv.propertyValueId
 join propertyGroup as pg on pg.id = pv.propertyGroupId

| id | name | details | userId | propertyValueId | id | propertyGroupId |         name | id |           name |
|----|------|---------|--------|-----------------|----|-----------------|--------------|----|----------------|
|  1 |  Joe |     foo |      1 |               1 |  1 |               1 | Technologies |  1 |        Hobbies |
|  1 |  Joe |     foo |      1 |               2 |  2 |               1 |      Surfing |  1 |        Hobbies |
|  1 |  Joe |     foo |      1 |               3 |  3 |               2 |         Rice |  2 |       LikeFood |
|  1 |  Joe |     foo |      1 |               5 |  5 |               2 |          Veg |  2 |       LikeFood |
|  1 |  Joe |     foo |      1 |               6 |  6 |               3 |          USA |  3 | VisitedCountry |
|  1 |  Joe |     foo |      1 |               7 |  7 |               3 |       FRANCE |  3 | VisitedCountry |
|  1 |  Joe |     foo |      1 |               8 |  8 |               3 |       ISRAEL |  3 | VisitedCountry |
|  2 |  Dan |     bar |      2 |               2 |  2 |               1 |      Surfing |  1 |        Hobbies |
|  2 |  Dan |     bar |      2 |               3 |  3 |               2 |         Rice |  2 |       LikeFood |
|  2 |  Dan |     bar |      2 |               4 |  4 |               2 |         Meat |  2 |       LikeFood |
|  2 |  Dan |     bar |      2 |               5 |  5 |               2 |          Veg |  2 |       LikeFood |
|  2 |  Dan |     bar |      2 |               8 |  8 |               3 |       ISRAEL |  3 | VisitedCountry |
|  2 |  Dan |     bar |      2 |               9 |  9 |               3 |       CANADA |  3 | VisitedCountry |
|  2 |  Dan |     bar |      2 |               7 |  7 |               3 |       FRANCE |  3 | VisitedCountry |

そのすべてがここにあります:http://sqlfiddle.com/#!3/49329/1

propertyValueIdのセットでデータをクエリし、グループに関してこのプロパティのセットに一致するすべてのユーザーを取得したいので、ユーザーはすべてのグループから少なくともプロパティを持っている必要があります。次のような一般的な CNF 句に一致します: (pvId は propertyValueId の省略形です)

Property group A        Property group B              Property group C
(pvId_x or pvId_y) and (pvId_w or pvId_z) and... and (pvId_m or pvId_k) 

上記の例では、pvId_x&pvId_y はグループ A に属し、pvId_w&pvId_z はグループ B に属します。

私がうまくいかなかったのは、ここに示されているように、IN演算子のAND演算子を連結しようとしました( or 一部をシミュレートします - 論理和):

select distinct u.name, u.id  
from users as u
 join usersPropertyValues as upv on upv.userId = u.id
 join propertyValues as pv on pv.id = upv.propertyValueId
 join propertyGroup as pg on pg.id = pv.propertyGroupId
 where (pv.propertyGroupId = 1 AND pv.id IN(1,2)) and (pv.propertyGroupId = 2 AND pv.id IN(5,6))

両方のユーザーを取得する代わりに (両方とも (1 または 2) と (5 または 6) を持っています)、何も取得しませんでした。

結果セットが空である理由は理解していますが、適切な where 句を実装する方法は理解していません。- どのようにそれを行うことができますか?

私の質問: 上記の SQL 構造に CNF ロジックを実装するにはどうすればよいですか?

編集:除外された結果の例: (sqlfiddle の例に関して:

input  --> output 
{1,5}  --> Joe (user with hobby:tech, likefood:veg)
{2,8}  --> Joe,Dan (user with hobby:surfing, VisitedCountry:Israel)
{2,8,9}  --> Dan (user with hobby:surfing, VisitedCountry:Israel,Canada)

ところで、これを最後に JPA に実装する必要があるため、JPA に解決策があればそれも素晴らしいことです。そうでない場合は、翻訳します...ありがとう

4

2 に答える 2

1

あなたの表記法を理解しているので、2 つの propertyvalueid に関連付けられているすべてのユーザーに対してクエリを実行する必要があります (つまり、{1,5} は、hobby:tech、likefood:veg のプロパティ値 ID に移動します)。

簡単に言い表すと、次のように、まず一方のリストを取得してから、もう一方のリストを取得し、両方の要素を見つけます。

select *
from users 
where id in (
  select userid from userPropertyValues where propertyvalueid  = 1
  intersect
  select userid from userPropertyValues where propertyvalueid  = 5
) sub

intersect 演算子の代わりに内部結合を使用することもできますが、intersect の方がセクシーです。プラットフォームがサポートしていない場合は、結合を使用してください。


結合として:

select *
from users 
join (select userid from userPropertyValues where propertyvalueid=1) a on a.userid = users.id
join (select userid from userPropertyValues where propertyvalueid=5) b on b.userid = users.id

またはもっと簡単に言うと (これは AND 条件 - 1 と 5)

select *
from users 
join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1
join userPropertyValues b on b.userid = users.id and b.propertyvalueid=5

(これは OR 条件 - 1 OR 5)

select *
from users 
left join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1
left join userPropertyValues b on b.userid = users.id and b.propertyvalueid=5
where coalesce(a.userid, b.userid) is not null

あなたも言うことができます

where a.userid is not null or b.userid is not null

---

フィドルから

select *
from users 
where id in (
  select userid from usersPropertyValues where propertyvalueid  = 1
  intersect 
  (
  select userid from usersPropertyValues where propertyvalueid  = 3 
    UNION ALL
  select userid from usersPropertyValues where propertyvalueid  = 4
  )
);

交差する前の部分を見てみましょう

select *
from users 
join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1

後の部分は

select *
from users 
left join userPropertyValues b on b.userid = users.id and b.propertyvalueid=1
left join userPropertyValues c on c.userid = users.id and c.propertyvalueid=5
where coalesce(b.userid, c.userid) is not null

したがって、それらをまとめます(交差はANDと同じであるため、両方とも真です):

select *
from users 
join userPropertyValues a on a.userid = users.id and a.propertyvalueid=1
left join userPropertyValues b on b.userid = users.id and b.propertyvalueid=1
left join userPropertyValues c on c.userid = users.id and c.propertyvalueid=5
where coalesce(b.userid, c.userid) is not null
于 2015-11-30T18:44:16.480 に答える
-1

where句を変更したり、byまたはinしたりすると役立つと思います

どこで (pv.propertyGroupId = 1 AND pv.id IN(1,2))または(pv.propertyGroupId = 2 AND pv.id IN(5,6))

于 2015-11-30T18:12:04.677 に答える