4

関連するテーブルが 2 つあります。

messages
--------

mid subject
--- -----------------
1   Hello world
2   Bye world
3   The third message
4   Last one


properties
----------

pid mid name             value
--- --- ---------------- ----------- 
1   1   read             false
2   1   importance       high
3   2   read             false
4   2   importance       low
5   3   read             true
6   3   importance       low
7   4   read             false
8   4   importance       high

そしてmessages、テーブルの基準を使用して取得する必要がありpropertiesます。return unread (read=false) high prio (importance=high) messages例:返す必要があるような基準がある場合

mid subject
--- -----------------
1   Hello world
4   Last one

SELECT 句 (MySQL ダイアレクト) を使用してこれを取得するにはどうすればよいですか?

4

6 に答える 6

6

SQL では、WHERE 句の式は一度に 1 行しか参照できません。そのため、プロパティ テーブルから複数の行を結果の 1 行に取得する何らかの方法が必要です。自己結合でこれを行います。

SELECT ...
FROM messages AS m
JOIN properties AS pRead 
    ON m.mid = pRead.mid AND pRead.name = 'read'
JOIN properties AS pImportance 
    ON m.mid = pImportance.mid AND pImportance.name = 'importance'
WHERE pRead.value = 'false' AND pImportance.value = 'high';

これは、EAVアンチパターンを使用することがいかに厄介かを示しています。1 つの属性が 1 つの列に属する従来の属性を使用する場合と比較します。

SELECT ...
FROM messages AS m
WHERE m.read = 'false' AND m.importance = 'high';

ちなみに、@Abe Miessler と @Thomas からの両方の回答は、必要以上に mid に一致します。これらは、read=false または重要度=high のすべての mid に一致します。これらのプロパティを AND に相当するものと組み合わせる必要があります。

于 2010-06-30T21:19:10.423 に答える
5

以下のクエリが機能すると思います。
更新: @Gratzy は正しいです。このクエリは機能しません。私が提案した構造の変更を見てください。

SELECT DISTINCT m.id as mid, m.subject
FROM message as m
INNER JOIN properties as p
ON m.mid = p.mid
where (p.name = 'read' and p.value = 'false') or (p.name = 'importance' AND p.value = 'high')

ただし、プロパティテーブルの構造は少しずれているようです...

次のようにテーブルを構成することは可能でしょうか。

messages
--------

mid subject           Read      Importance
--- ----------------- --------- ------------
1   Hello world       false     3
2   Bye world         false     1
3   The third message true      1
4   Last one          false     3

importance
----------

iid importanceName
--- --------------
1   low
2   medium
3   high

次のクエリを使用します。

SELECT m.id as mid, m.subject
FROM message as m
where m.read = false AND m.importance = 3
于 2010-06-30T21:09:24.053 に答える
1

明らかに、EAV (Entity-Attribute-Value) スキーマを使用しています。このような構造を避ける多くの理由の 1 つは、クエリがより困難になることです。ただし、指定した例では、次のようなことができます。

Select ...
From messages As M
Where Exists    (
                Select 1
                From Properties As P1
                Where P1.mid = M.mid
                    And P1.name = 'unread' And P1.value = 'false'
                )
    And Exists  (
                Select 1
                From Properties As P2
                Where P2.mid = M.mid
                    And P2.name = 'importance' And P2.value = 'high'
                )

より簡潔な解決策は次のとおりです。

Select ...
From messages As M
Where Exists    (
                Select 1
                From Properties As P1
                Where P1.mid = M.mid
                    And ((P1.name = 'unread' And P1.value = 'false')
                            Or (P1.name = 'importance' And P1.value = 'high'))
                Having Count(*) = 2
                )
于 2010-06-30T21:10:50.517 に答える
0

既存のデータ モデルを維持したい場合は、Bill Karwin の最初の提案に従ってください。この select 句で実行して、何をしているのかを理解してください。

select m.*, r.value as read, i.value as importance
from message m
join properties r
    on r.mid = m.mid and r.name = 'read'
join properties i
    on i.mid = m.mid and i.name = 'importance'
where r.value = 'false' and i.value = 'high';

ただし、この方法を使用する場合は、不適切なデータの保存と取得を回避するために、いくつかの制約を設ける必要があります。

  1. メッセージ (mid) の一意のインデックスとプロパティ (pid) の一意のインデックス。どちらも既にお持ちだと思います。
  2. プロパティ (mid, name) の一意のインデックス。これにより、メッセージに対して各プロパティを 1 回だけ定義できます。そうしないと、クエリから重複した結果が得られる可能性があります。これにより、両方の結合でインデックス アクセスが許可されるため、クエリのパフォーマンスも向上します。
于 2010-06-30T22:25:28.623 に答える
0
Select m.mid, m.subject
from properties p 
inner join properties p1 on p.mid = p1.mid
inner join messages m on p.mid = m.mid
where
p.name = 'read' 
and p.value = 'false'
and p1.name = 'importance'
and p2.value = 'high'

フィルター基準を where 句に入れ、結合を両方のテーブルにあり、結合の実際の基準である要素に残すことを好みます。

于 2010-06-30T21:20:19.093 に答える
0

もう 1 つの方法は (テストされていませんが) 派生テーブルを使用して、すべてのメッセージが満たさなければならない基準を保持し、double の標準的なリレーショナル除算手法を使用することです。NOT EXISTS

SELECT mid,
       subject
FROM   messages m
WHERE  NOT EXISTS
       ( SELECT *
       FROM    ( SELECT 'read' AS name,
                       'false' AS value

               UNION ALL

               SELECT 'importance' AS name,
                      'high'       AS value
               )
               c
       WHERE   NOT EXISTS
               (SELECT *
               FROM    properties P
               WHERE   p.mid  = m.mid
               AND     p.name =c.name
               AND     p.value=c.value
               )
       )
于 2010-06-30T21:54:40.993 に答える