5

リレーショナル データベースの性質上、このようなクエリは非常に簡単だと思っていましたが、うまくいっているようです。私も周りを検索しましたが、本当に役立つものは何も見つかりませんでした。状況は次のとおりです。

製品と製品タグの単純な関係があるとします。これは 1 対多の関係であるため、次のようになります。

productid  |  tag
========================
1          |  Car
1          |  Black
1          |  Ford
2          |  Car
2          |  Red
2          |  Ford
3          |  Car
3          |  Black
3          |  Lexus
4          |  Motorcycle
4          |  Black
5          |  Skateboard
5          |  Black
6          |  Skateboard
6          |  Green

すべてを照会する最も効率的な方法は何(Ford OR Black OR Skateboard) AND NOT (Motorcycles OR Green)ですか? 実行する必要がある別のクエリは、 all のようなもの(Car) or (Skateboard) or (Green AND Motorcycle) or (Red AND Motorcycle)です。

products テーブルには約 150k のレコードがあり、tags テーブルには 600k のレコードがあるため、クエリは可能な限り効率的である必要があります。これは私がいじっているクエリの 1 つ (例 #1) ですが、約 4 秒ほどかかるようです。どんな助けでも大歓迎です。

SELECT p.productid
FROM   products p
       JOIN producttags tag1 USING (productid)
WHERE  p.active = 1
       AND tag1.tag IN ( 'Ford', 'Black', 'Skatebaord' )
       AND p.productid NOT IN (SELECT productid
                               FROM   producttags
                               WHERE  tag IN ( 'Motorcycle', 'Green' ));

 

アップデート

これまでに見つけた最速のクエリは、次のようなものです。100〜200ミリ秒かかりますが、かなり柔軟性がなく、醜いようです。Ford基本的に、 、Black、またはに一致するすべての製品を取得していますSkateboard。一致した製品のすべてのタグをコロンで区切られた文字列に連結し、:Green:ANDで一致するすべての製品を削除しています:Motorcycle:。何かご意見は?

SELECT p.productid,
       Concat(':', Group_concat(alltags.tag SEPARATOR ':'), ':') AS taglist
FROM   products p
       JOIN producttags tag1 USING (productid)
       JOIN producttags alltags USING (productid)
WHERE  p.active = 1
       AND tag1.tag IN ( 'Ford', 'Black', 'Skateboard' )
GROUP  BY tag1.productid
HAVING ( taglist NOT LIKE '%:Motorcycle:%'
         AND taglist NOT LIKE '%:Green:%' ); 
4

5 に答える 5

3

サブクエリなしで除外結合を記述します。

SELECT p.productid
FROM   products p
INNER JOIN producttags AS t ON p.productid = t.productid
LEFT OUTER JOIN producttags AS x ON p.productid = x.productid 
       AND x.tag IN ('Motorcycle', 'Green')
WHERE  p.active = 1
       AND t.tag IN ( 'Ford', 'Black', 'Skateboard' )
       AND x.productid IS NULL;

この順序で 2 つの列 (active、productid) にわたって製品のインデックスがあることを確認してください。

また、この順序で 2 つの列 (productid、tag) の producttags にインデックスを作成する必要があります。

もう 1 つのクエリとして、all (Car) or (Skateboard) or (Green AND Motorcycle) or (Red AND Motorcycle) などのクエリを実行する必要があります。

これらの複雑な条件は、MySQL オプティマイザーにとって難しい場合があります。一般的な回避策の 1 つは、UNION を使用してより単純なクエリを結合することです。

SELECT p.productid
FROM   products p
INNER JOIN producttags AS t1 ON p.productid = t1.productid
WHERE  p.active = 1
   AND t1.tag IN ('Car', 'Skateboard')

UNION ALL

SELECT p.productid
FROM   products p
INNER JOIN producttags AS t1 ON p.productid = t1.productid
INNER JOIN producttags AS t2 ON p.productid = t2.productid 
WHERE  p.active = 1
   AND t1.tag IN ('Motorcycle')
   AND t2.tag IN ('Green', 'Red');

PS: タグ付けテーブルは Entity-Attribute-Value テーブルではありません。

于 2012-10-29T22:06:17.617 に答える
2

すべての一意のIDの一致と一意のIDを取得してフィルターで除外し、次にそれらのリストを左に結合して(tigeryanのように)、一致するIDをフィルターで除外します。また、すべてのクエリを個別に保持することで、クエリを読みやすく、変更しやすくする必要があります。見た目は違うかもしれませんが、かなり速いはずです。

SELECT * FROM products p
WHERE 
p.active=1 AND
productid IN (
SELECT matches.productid FROM (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Ford','Green','Skatebaord')
) AS matches
LEFT JOIN (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Motorcycles','Green')
) AS filter ON filter.productid=matches.productid
WHERE filter.productid IS NULL
)

mysqlがクエリを最適化する方法によっては、JOINがINよりも高速な場合があります。

SELECT p.* FROM (
SELECT matches.productid FROM (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Ford','Green','Skatebaord')
) AS matches
LEFT JOIN (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Motorcycles','Green')
) AS filter ON filter.productid=matches.productid
WHERE filter.productid IS NULL
) AS idfilter
    JOIN products p ON p.productid=idfilter.productid AND p.active=1

内部選択を最初に実行する必要があるため、2番目のクエリは結合順序を強制する必要があります。

于 2012-10-26T02:11:54.547 に答える
0

私は通常、からのレコードを削除しようとすることでこれを攻撃します...

select p.productid 
from product p 
left join producttags tag1 
    on p.productid = tag1.productid and tag1.tag NOT IN ('Motorcycles','Green')
where tag1.tag IN ('Ford','Black','Skateboard') and p.active = 1
于 2012-10-24T02:49:43.577 に答える
0

これはどうですか:

SELECT DISTINCT p.id FROM products AS p
JOIN producttags AS included ON (
    included.productid = p.id
    AND included.tag IN ('Ford', 'Black', 'Skatebaord') 
)
WHERE active = 1
AND p.id NOT IN (
    SELECT DISTINCT productid FROM producttags
    WHERE tag IN ('Motorcycle', 'Green')
)
于 2012-10-29T21:57:39.630 に答える