1

各ユーザーがクリックしたリンクを追跡するテーブルが1つあり、リンクを含む別のテーブルがあります。各テーブル構造は次のとおりです。

リンク: id | リンク| 値| date_added

クリック: user_id | link_id | date_clicked

現在、これは検索を実行するために使用しているコードであり、機能します。クリックされたリンクテーブルは非常に高速に大きくなるため、より効率的な方法があるかどうかを知りたいだけです。

$history_query = mysql_query("SELECT * FROM clicked_links WHERE user_id = '$id'") or die(mysql_error());
$history_array = array();
while ($h = mysql_fetch_array($history_query)) {
    $history_array[] = $h['link_id'];
}
$clicked = implode(',', $history_array);

$link_query = mysql_query("SELECT * FROM chip_links WHERE id NOT IN ($clicked) ORDER BY value DESC") or die(mysql_error());
while ($r = mysql_fetch_array($link_query)) {
    echo "<div id='claim{$r['id']}' style='text-align: center; font-weight: bold; font-size: 18px; float: left; width: 183px;'>
    <a href='{$r['link']}' id='{$r['id']}' class='collect' target='_blank'>
    Claim {$r['value']} points!
    </a>
    </div>";
}
4

2 に答える 2

1

個別のクエリを実行するよりも、単一のクエリを実行して結果セットを取得する方が効率的です。

すべての値を返しlink_id、それらを配列に入れ、配列を文字列に入れ、その文字列を別のクエリにプッシュして、データベースにシャッフルする必要はありません...データベースにはすでにそれがあります。

このクエリは、$history_queryまたは$history_arrayを必要とせずに、現在の$link_queryと同等の結果セットを返します。

SELECT l.id
     , l.link
     , l.value
  FROM chip_links l
 WHERE l.id NOT IN
       ( SELECT c.link_id
           FROM clicked_links c
          WHERE c.user_id = '$id'
            AND c.link_id IS NOT NULL
       )
 ORDER BY l.value DESC

clicked_linksテーブルのlink_idがNULLでないという何らかの保証link_id IS NOT NULLがない場合は、link_id値がNULLの場合、クエリは行を返さないため、そのサブクエリに述語を含める必要があります。(これは、構造に関するよく知られた回避可能な問題NOT IN (subquery)です。

MySQLはそれを(うまくいけばより効率的ですが)同等のNOT EXISTS相関サブクエリに最適化する可能性があります:次のように:

SELECT l.id
     , l.link
     , l.value
  FROM chip_links l
 WHERE NOT EXISTS 
       ( SELECT 1 
           FROM clicked_links c
          WHERE c.user_id = '$id'
           AND c.link_id = l.id
       )
 ORDER BY l.value DESC

ただし、最高のパフォーマンスを得るには、おそらくアンチジョインパターンを使用することをお勧めします。

LEFT JOIN操作は基本的に一致する行を検索し、IS NOT NULL述語は一致する行をスローします。したがって、返されるのは、からchip_links「一致する」行がない場所からの行clicked_linksです。

MySQLオプティマイザは通常、次のようなクエリを使用して最も効率的なプランを生成します。

SELECT l.id
     , l.link
     , l.value
  FROM chip_links l
  LEFT
  JOIN clicked_links c
    ON c.link_id = l.id
   AND c.user_id = '$id'
 WHERE c.link_id IS NULL
 ORDER
    BY l.value DESC

大規模なセットで良好なパフォーマンスを得るには、インデックスも必要になる可能性があります

... ON clicked_links (user_id, link_id)

... ON chip_links (value, id, link)

これにより、ソート操作を必要とせずに、インデックスからクエリを完全に満たすことができます。EXPLAIN出力には、「Using index」が含まれ、「Usingfilesort」は含まれません。

于 2013-01-15T23:47:28.247 に答える
0

特定のユーザーがクリックしていないすべてのリンクを通知するこのワンショットクエリのようなもの

SELECT l.* FROM chip_links l
LEFT JOIN clicked_links c ON (c.link_id=l.id AND l.user_id='$id')
WHERE c.link_id IS NULL
ORDER BY l.value DESC;

左結合に慣れていない場合は、join句が一致するclicked_linksの行が含まれますが、一致しない場合はnullが返されます。不一致に関心があるので、WHERE句は、これらが取得する唯一の行であることを保証します。

これは、2つのクエリといくつかのPHPコードを使用するよりもおそらく効率的ですが、確実にわかるのはベンチマークだけです。また、の出力を調べEXPLAIN SELECT ...て、適切なインデックスが使用されていることを確認する必要があります。

于 2013-01-15T23:43:47.747 に答える