排除子查询返回需要互斥的结果的记录

时间:2013-07-03 07:53:48

标签: mysql

标题不够明确......

我每小时都在运行一个查询来准备MEMORY表,然后用于下一个小时的高密集流量。现在查询看起来像:

INSERT INTO tmp_table
            SELECT DISTINCT SQL_NO_CACHE
            B.*, G.node
            FROM books B
            RIGHT JOIN book_genres G on G.asin=B.asin
            WHERE EXISTS
            (
              SELECT 1 FROM genres K WHERE K.node=G.node
            )
            AND...[nothing special here]

所以books表只保存带有asin的PRIMARY KEY的书籍数据。 book_genres包含asin和node字段,包含许多在站点的各个部分中使用的节点。但是,tmp_table只需要包含流派表中的节点子集,即子查询。希望这足以在不发布完整模式的情况下完成。

我们现在想要使某些类型相互排斥。这意味着在构建tmp_table后执行此操作:

# Delete records that should have been excluded
DELETE T FROM tmp_table T INNER JOIN 
(
    SELECT N.* FROM tmp_table N INNER JOIN
    (
        SELECT DISTINCT ASIN FROM tmp_table 
        INNER JOIN genres ON genres.node=tmp_table.node
        WHERE isFiction=1
    ) F 
    ON F.asin=N.asin 
    INNER JOIN genres ON  genres.node=N.node
    WHERE genres.isNonFiction=1 
) D 
USING (asin, node)
WHERE D.asin=T.asin AND D.node=T.node

因此,如果asin属于isFiction = 1的类型,则删除所有nonFiction = 1的类型。

但这感觉很难看:使用查询添加数据然后再次删除它。有没有什么方法可以将它组合成一个查询以避免双通。或者我只为自己工作,鉴于(经过一些调整和编辑),上述工作可以很快地完成。

任何想法都会受到赞赏,谢谢。

添加了示例:

books table:
asin        title       price     etc...
B111111111  Book1       $0.99     ....
B222222222  Book2       $0.99     ....
B333333333  Book2       $0.99     ....

book_genres table:
asin        node
B111111111  1111
B111111111  2222
B111111111  3333
B111111111  5555
B222222222  1111
B222222222  3333
B222222222  4444
B333333333  1111
B333333333  2222

genres table:
node    name         isFiction    isNonFiction
1111    Bestsellers  0            0
2222    Romance      1            0
3333    Biographies  0            1
4444    History      0            1

所以在运行INSERT INTO之后,tmp_table将如下所示:

asin       title       node 
B11111111  Book1       1111
B11111111  Book1       2222
B11111111  Book1       3333
B22222222  Book2       1111
B22222222  Book2       3333
B22222222  Book2       4444
B33333333  Book3       1111
B33333333  Book3       2222

Book1没有节点5555的记录,因为它不在流派表中,我们在tmp_table中不需要它。其他所有内容几乎都是数据的非规范化,因为WHERE在数据库中识别出数以千计的数百本书的子部分,这样可以更快地使用。

下一步确​​保如果书籍具有isFiction节点,则删除该书籍的所有isNonFiction节点。

运行DELETE后,tmp_table的最终结果为:

asin       title       node 
B11111111  Book1       1111
B11111111  Book1       2222
B22222222  Book2       1111
B22222222  Book2       3333
B22222222  Book2       4444
B33333333  Book3       1111
B33333333  Book3       2222

唯一的区别是Book1删除了节点3333,因为Book1位于2222节点,其中isFiction = 1且节点3333具有isNonFiction = 1。 Book2未更改,因为它不包含isFiction节点。同样,Book3没有变化,因为它不包含isNonFiction节点。

在这个阶段,它正在使用这个实现,虽然运行时间从大约20多秒增加到大约40倍。真的没有意外,因为DELETE有点复杂。这可能是一个不错的解决方案,但如果其他人有一个想法可以使整个事情变得更简单或更快,我会很高兴。

...标记

1 个答案:

答案 0 :(得分:0)

这很难看。它工作正常,直到数据库上有任何其他重大负载,然后这一切都非常缓慢。这主要取决于服务器的IO限制,但更简单的方法是将isfiction和isNonFiction放入MEMORY表中,然后DELETE语句可以如下所示:

    DELETE tmp_table FROM tmp_table
         INNER JOIN
         (
            SELECT ASIN, MAX( isFiction ) AS isFiction, MAX( isNonFiction ) AS isNonFiction
            FROM tmp_table
            GROUP BY ASIN
            HAVING isFiction =1
            AND isNonFiction =1
         ) D
         WHERE D.ASIN=tmp_table.ASIN AND tmp_table.isNonFiction=1

在测试中,这将使整个过程从大约90秒降低到10秒。