1

私はこのデータベーススキーマを持っています。単純にするために、オブジェクトにタグ付けしているものを呼び出しています。

ユーザー

  • ユーザーID
  • ログインする

ユーザーからオブジェクトへのテーブル(多対多)

  • ユーザーID
  • object_id

オブジェクトテーブル

  • object_id
  • object_stuff

user_idテーブルでタグ付けするオブジェクト(多くの場合、余分な列があります)

  • ユーザーID
  • object_id
  • tag_id

タグテーブル

  • tag_id
  • タグ名

特定のユーザーのオブジェクトとオブジェクトのタグ(ユーザーがタグを付けている場合)を取得するために、このようなクエリを使用しています。これを最適化できると思われる場合は、遠慮なくお知らせください

@user_id = 'whatever user_id I want';
SELECT 
    o.object_id AS object_id,
    o.object_stuff AS object_stuff,
    IF (tags.tag_name IS NOT NULL, GROUP_CONCAT(tags.tag_name SEPARATOR '|'), 'N/A') AS tags
FROM  object_table AS o
LEFT OUTER JOIN  obj_to_users_table AS o2u ON o.object_id = o2u.object_id
LEFT OUTER JOIN  users AS u ON u.user_id = o2u.user_id 
LEFT OUTER JOIN  obj_to_tag_users AS o2tu ON o.object_id = o2tu.object_id AND u.user_id = o2tu.user_id
LEFT OUTER JOIN  tags AS t ON t.tag_id = o2t.tag_id 
WHERE u.user_id = @user_id GROUP BY o.object_id

これにより、次のような結果が得られます。

object_id     object_stuff        tags
1             stuff1...           tag1|tag2|tag3
2             stuff2...           tag4|tag6|tag1
3             stuff3...           tag7|tag2|tag5

私の問題は、たとえば、「tag2」としてタグ付けされたオブジェクトを検索したいので、次のように取得する必要があることです。

object_id     object_stuff        tags
1             stuff1...           tag1|tag2|tag3
3             stuff3...           tag4|tag2|tag1

しかし、代わりに、オブジェクトに付随する他のタグを失います。

object_id     object_stuff        tags
1             stuff1...           tag2
3             stuff3...           tag2

「tag2」としてタグ付けされたオブジェクトを取得できるだけでなく、オブジェクトが結果セットに持つ他のタグも保持できるように、WHERE句を変更するにはどうすればよいですか?

... WHERE u.user_id = @user_id AND t.tag_name LIKE '%tag2%' ...
4

1 に答える 1

1

まず、連結された結果を計算します。

次に、これらの連結された結果に対して検索を実行して、タグのグループ化を保持します。

SELECT * 
FROM
(
    SELECT 
        o.object_id AS object_id,
        o.object_stuff AS object_stuff,
        IF (tags.tag_name IS NOT NULL, 
            GROUP_CONCAT(tags.tag_name SEPARATOR '|'), 'N/A') AS tags
    FROM  object_table AS o
    LEFT OUTER JOIN  obj_to_users_table AS o2u 
        ON o.object_id = o2u.object_id
    LEFT OUTER JOIN  users AS u 
        ON u.user_id = o2u.user_id 
    LEFT OUTER JOIN  obj_to_tag_users AS o2tu 
        ON o.object_id = o2tu.object_id AND u.user_id = o2tu.user_id
    LEFT OUTER JOIN  tags AS t ON t.tag_id = o2t.tag_id 
    WHERE u.user_id = @user_id 
    GROUP BY o.object_id
) sub
WHERE sub.tags LIKE '%tag2%'

編集:


を使用する例を次に示しますEXISTS。また、特定のタグを検索すると、とにかく何も削除されないため、sをsに切り替えLEFT JOINました。INNER JOINobjectstags

このクエリはより複雑に見えますが、使用可能なインデックスを使用する可能性が高くなります。

追加のsのセットを使用して同じタイプのクエリを実行することについても説明しましたが、追加のsによって導入された可能性のある重複を削除するには、サブクエリINNER JOINを使用する必要があるため、さらに複雑に見えます。...代わりに使用することをお勧めします...DISTINCTJOINEXISTS

SELECT 
    o.object_id AS object_id,
    o.object_stuff AS object_stuff,
    GROUP_CONCAT(t.tag_name SEPARATOR '|') AS tags
FROM  object_table AS o
INNER JOIN  obj_to_users_table AS o2u 
    ON o.object_id = o2u.object_id
INNER JOIN  users AS u 
    ON u.user_id = o2u.user_id 
INNER JOIN  obj_to_tag_users AS o2tu 
    ON o.object_id = o2tu.object_id AND u.user_id = o2tu.user_id
INNER JOIN tags AS t 
    ON t.tag_id = o2t.tag_id
WHERE 
    u.user_id = @user_id 
    AND EXISTS (
        SELECT 1
        FROM tags AS targetTag
        WHERE 
            targetTag.tag_id = o2t.tag_id
            AND targetTag.tag_name = 'tag2'
    )
GROUP BY o.object_id
于 2012-12-19T18:40:02.877 に答える