1

特定のアイテムを持つすべてのユーザーを検索するためにクエリを実行する必要がある2 つの参照 (user_idおよび)を持つ 1 つのテーブルがあります。item_idトリッキーな部分は、結果の数だけでなく、どのアイテムがあるかに基づいて、結果を並べ替える必要があることです。

ここにテーブルがあります:

+--------------+-----------------------+------+-----+---------+-------+
| Field        | Type                  | Null | Key | Default | Extra |
+--------------+-----------------------+------+-----+---------+-------+
| user_id      | int(11)               | NO   |     | 0       |       |
| item_id      | int(11) unsigned      | YES  |     | NULL    |       |
+--------------+-----------------------+------+-----+---------+-------+

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

SELECT   user_id, item_id
FROM     user_items
WHERE    item_id IN (2, 122, 132)
GROUP BY user_id, item_id
HAVING   SUM(item_id = 2);

簡単に見えますか?難しい部分は次のとおりです。

item_id = 2 は必須 item_id = 122 および 132 はオプションです。132 以降もオプションです。

次の基準に基づいて結果を並べ替える必要があります。1) すべてのアイテムが見つかった場合。2) アイテム 2 と 122 のみが見つかった場合。3) アイテム 2 のみが見つかった場合。

フィドリング用の SQL フィドル ファイルは次のとおりです: http://sqlfiddle.com/#!2/6b1c1/6/0

私が考えているのは、次のように設定できる方法があれば、次のように言うことです。

IF (item_id = 2 AND item_id = 122 AND item_id = 132) AS matches_all,
IF (item_id = 2, item_id = 122) AS matches_some,
IF (item_id = 2) AS matches_first

更新されたクエリで編集 これまでのところ私が持っているものは次のとおりです。それは私が必要とするものの約 95% です: http://sqlfiddle.com/#!2/6b1c1/47

SELECT   user_id, item_id,
  @tmp_1 := IF(SUM(item_id = 2), 1, 0) AS tmp_1,
  @tmp_2 := IF(SUM(item_id = 122), 1, 0) AS tmp_2,
  @tmp_3 := IF(SUM(item_id = 132), 1, 0) AS tmp_3,
  @tmp_4 := IF(SUM(item_id = 126), 1, 0) AS tmp_4,
  CAST(@tmp_3 + @tmp_4 AS UNSIGNED) AS total_other
FROM     user_items
WHERE    item_id IN (2, 122, 132, 126)
GROUP BY user_id
HAVING SUM(item_id = 2)
ORDER BY tmp_1 DESC, tmp_2 DESC, total_other DESC

さらにいくつかの詳細:

1) 入力できる項目は最大 12 個までなので、必要に応じてそれぞれに独自の一時フィールドを割り当てることができます。

2) 上記のクエリは、tmp_1 と tmp_2 に対して完全に機能します。アイテム 2 と 122 を持つユーザーがいる場合、それらがリストの一番上に配置されます。残りの 3 ~ 4 (3 ~ 12) については、一致数を計算する必要があるため、CAST(@tmp_3 + @tmp_4. それらを計算する方法がわかりません。

3) 項目 3 ~ 12 の合計計算が完了すると、それがORDER BY句の 3 番目で最後の項目になります。

結果の例 SQL fiddle ファイルで提供されているスキーマに基づいて、item_id を持つすべてのユーザーを検索して返される結果を次に示します。2, 122, 132, 126

+---------+--------------+----------------+-------------+
| USER_ID | PRIMARY_ITEM | SECONDARY_ITEM | OTHER_ITEMS |
+---------+--------------+----------------+-------------+
| 39      | 1            | 1              | 2           |
| 54      | 1            | 1              | 0           |
| 55      | 1            | 0              | 0           |
+---------+--------------+----------------+-------------+
4

3 に答える 3

1

アップデート:

質問の更新 (目的の結果セットを含む) に基づいて、その結果セットを返すクエリを次に示します。(これは、元の回答で説明したインライン ビューのクエリと非常によく似ています)

  SELECT i.user_id                         AS user_id
       , MAX(IF(i.item_id= 2   ,1,0))      AS primary_item
       , MAX(IF(i.item_id= 122 ,1,0))      AS secondary_item
       , MAX(IF(i.item_id= 132 ,1,0)) +
         MAX(IF(i.item_id= 126 ,1,0))      AS other_items
    FROM user_items i
   WHERE i.item_id IN (2, 122, 132, 126)
   GROUP BY i.user_id
  HAVING primary_item
   ORDER 
      BY primary_item   DESC
       , secondary_item DESC
       , other_items    DESC
       , i.user_id

列を計算する式をother_items拡張して、他の items_id 値をいくつでも処理できることに注意してください。(同じ item_id が2回指定されていないことを確認したいだけです。そうしないと、2回「カウント」されます)。

       , MAX(IF(i.item_id= 132 ,1,0)) +
         MAX(IF(i.item_id= 133 ,1,0)) +
         MAX(IF(i.item_id= 135 ,1,0)) +
         MAX(IF(i.item_id= 137 ,1,0)) +
         MAX(IF(i.item_id= 143 ,1,0))      AS other_items

これは基本的に、各アイテムのチェックを行い、1 または 0 を導き出し、1 と 0 を合計して合計を出します。

また、IF() 関数呼び出しは必要ないことに注意してください。これらの式は、実際には次のように縮小できます。

       , MAX(i.item_id= 2)                 AS primary_item
       , MAX(i.item_id= 122)               AS secondary_item

このWHERE句は、正しい結果セットを返すために実際には必要ないことに注意してください。(しかし、そこにある場合、述語は、SELECT リストでチェックされている item_id 値と一致する必要があります。

また、ORDER BY に を含める必要がないことにも注意してください。これprimary_item DESCは、クエリで の値が 1 になることが保証されているためprimary_itemです。順序付けを で開始するだけで十分secondary_item DESCです。

カバリング インデックスon (user_id,item_id)を使用するとパフォーマンスが向上する可能性があります。または、先頭の列が のインデックスのitem_id方がパフォーマンスが向上する場合があります。(WHERE 句がない場合、クエリはテーブル内のすべての行を検査する必要があります。基本的には、完全なテーブル スキャンまたは完全なインデックス スキャンです。)

結果セットから、ユーザーが 1 つ以上のアイテムを持っている場合 (ユーザーが持っている特定のアイテムの数ではなく)、'1' を返したいようです。各アイテムの数の場合、MAX()集計を集計に置き換えますが、それSUM()は OTHER_ITEMS 列の内容を解読する上でより問題になります。

このHAVING primary_item句は、少なくとも 1 つのitem_id = 2.


アップデート:

フランシスは言った...そのクエリは[あなたの元の回答で]ユーザーごとに複数の結果を返しますが、これは私が求めていたものではありません。

A:これは、返してほしい結果セットの例を示すことが有益であることを示す代表的な例です。クエリuser_idの SELECT リストには と item_id` の両方があり、ユーザーごとに 1 行のみ、または user_id と item_id の組み合わせごとに 1 行のみを返すことを示すものはありません。

それを取得するには、節の前にGROUP BY d.user_idまたは節を追加するだけです。GROUP BY d.user_id, d.item_idORDER BY


これはエレガントではありませんが、指定した結果セットが返されると思います。

SELECT d.user_id
     , d.item_id 
  FROM user_items d
  JOIN ( 
         SELECT i.user_id
              , MAX(IF(i.item_id=2  ,1,0)) AS item_2
              , MAX(IF(i.item_id=122,1,0)) AS item_122
              , MAX(IF(i.item_id=132,1,0)) AS item_132
           FROM user_items i
          WHERE i.item_id IN (2, 122, 132)
          GROUP BY i.user_id
         HAVING item_2
          ORDER BY 3 DESC, 4 DESC, 1
       ) f
    ON d.user_id = f.user_id
 WHERE d.item_id IN (2, 122, 132)
 ORDER BY (f.item_122 AND f.item_132) DESC
        , f.item_122 DESC
        , d.user_id
        , d.item_id

インライン ビュー ( としてエイリアス化されたクエリf) は、どのアイテムがユーザーに対して見つかったかの「チェック」を行います。


これがどのように機能するかを確認するには、まず、そのインライン ビューの結果を確認します...

         SELECT i.user_id
              , MAX(IF(i.item_id=2  ,1,0)) AS item_2
              , MAX(IF(i.item_id=122,1,0)) AS item_122
              , MAX(IF(i.item_id=132,1,0)) AS item_132
           FROM user_items i
          WHERE i.item_id IN (2, 122, 132)
          GROUP BY i.user_id
         HAVING item_2
          ORDER BY 3 DESC, 4 DESC, 1

このWHERE句はここでは省略できます。ここでの目的のために、基本的には、user_id のリストと、指定されたアイテムのどれを持っているかを示すインジケーターを取得しています。

MAX 集計内の式は、item_id がそれぞれ 2、122、または 132 に一致するかどうかをチェックし、1 または 0 を返します。MAX集計を使用して、見つかった 1 の値を引き出します。

が必要なGROUP BYので、user_id の個別のリストを取得します。

HAVINGを持たないユーザーitem_id = 2が省略されるように、句を使用します。このように書くことができます

         HAVING item_2 > 0 

(ゼロより大きい値を追加しますが、item_2 の値が 0 または 1 になることが保証されているため、これは必須ではありません)

ここORDER BYでは実際には必要ありません (これを user_items テーブルに JOIN するためです) (これORDER BYは最も外側のクエリでのみ必要です)。しかし、この結果セットを順序付けできることを示しています。

(これが私の要件であれば、ここでやめて、この結果セットを利用するかもしれませんが、それはあなたが指定した結果セットではありません。)

そのクエリを (インライン ビュー、またはMySQL 用語で派生テーブルuser_itemsとして使用して) テーブルに結合し、そのクエリの user_id に一致するユーザーのみの行を返します。

句を追加する必要があるため、指定されたリストの値WHEREのみを引き出します。item_id

そしてORDER BY、指定された順序で結果セットを取得する必要があります。

于 2012-12-15T00:37:07.120 に答える
0

わかりました、これが私が思いついたものです。最初の 2 つ以降のアイテムを計算する必要があるだけなので、一時フィールドを使用するよりもクリーンなソリューションと、最終的に機能するソリューションを考え出しました。

SELECT   user_id,
         IF(SUM(item_id = 2), 1, 0) AS primary_item,
         IF(SUM(item_id = 122), 1, 0) AS secondary_item,
         (IF(SUM(item_id = 132), 1, 0) + IF(SUM(item_id = 126), 1, 0)) AS other_items
FROM     user_items
WHERE    item_id IN (2, 122, 132, 126)
GROUP BY user_id
HAVING   SUM(item_id = 2)
ORDER BY primary_item DESC, secondary_item DESC, other_items DESC

これにより、1 番目と 2 番目のアイテムのフィールドが得られたので、それらが一致したかどうかを確認し、残りのすべてのアイテムを数えることができます。これには、最大 10 個の他のアイテムが存在する可能性があります。

次に、最初のアイテム、2 番目のアイテム、その他すべてのアイテムの合計数に基づいて並べ替えます。

ここで最終結果を確認できます: http://sqlfiddle.com/#!2/6b1c1/131

于 2012-12-15T07:21:03.560 に答える
0

必要なのは、どのフィールドが必須で、どのフィールドがオプションかのルールまたはマッピングのようです。わからない、おそらくid < 10が必要で、それ以外はすべてオプションであるというある種の数学的ルールがある場合、それを使用していくつかの派手なwhere句を実行できます。

item_id が完全にランダムであると仮定すると、アイテムをランク付け/優先順位付けするマッピング テーブルを作成することをお勧めします。多分 item_rank テーブルのようなもの:

-------------------------
| | item_id | is_optional |
-------------------------
| | 2 | 1 |
-------------------------
| | 122 | 0 |
-------------------------
| | 133 | 0 |
-------------------------

次に、クエリは次のとおりです。

SELECT user_map.user_id, user_map.item_id,
FROM user_map
INNER JOIN item_rank
ON user_map.item_id = item_rank.item_id
    AND user_map.item_id IN (2,122,133)
GROUP BY user_map.user_id
HAVING item_rank.is_optional > 0
ORDER BY COUNT( user_map.item_id );

私はこの解決策が好きではありませんが、あなたが一日の終わりに達成しようとしていることをもう少し知らなければ、より具体的な解決策を提供することはできません.

余談ですが、問題が困難な場合、それは一般的に、問題を解決しようとしている方法が間違っていることを意味します。建築上の制約に陥ったとき、最初からやり直して考え直すと、常によりクリーンな解決策を見つける傾向があります。明らかに、あなたがどれだけ進んでいるかに依存しますが、それだけの価値があるかもしれません.

幸運を!

于 2012-12-15T00:42:24.200 に答える