Force Union 以避免得到重复的结果,然后将其删除?

Force Union to avoid getting a duplicate result which is then removed?

我有一个查询如下:

(SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
        FROM rarity_db cr
        JOIN card_db cd
        ON cr.id = cd.id
        WHERE cr.rarity = 'Common'
        AND cr.set_code like '%lob%' 
        GROUP BY cd.id ORDER BY RAND() LIMIT 7)

        UNION

        (SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
        FROM rarity_db cr
        JOIN card_db cd
        ON cr.id = cd.id
        WHERE cr.rarity = 'Rare'
        AND cr.set_code like '%lob%'
        GROUP BY cd.id ORDER BY RAND() LIMIT 1)

        UNION

        (SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
        FROM rarity_db cr
        JOIN card_db cd
        ON cr.id = cd.id
        WHERE cr.rarity != 'Rare'
        AND cr.set_code like '%lob%'
        GROUP BY cd.id ORDER BY RAND() LIMIT 1)

第三个查询的逻辑是它有可能是普通的或高于稀有的稀有度。目前这是一个随机机会,所以顺序如下:

因为我使用的是UNION,所以重复项被删除了。我知道我可以使用 UNION ALL 来保留重复项,但问题是我需要在不删除任何内容的情况下没有重复项。

我绝对需要 9 个结果,但通过上面的查询,我偶尔可以获得 8 个结果,因为最后一个查询可以获得与前 7 个相同的结果之一。

是否有一种纯粹的 SQL 方法来解决这个问题,还是我必须求助于 PHP?

从 PHP 的角度来看,我可以执行前两个查询,将第一个查询的所有结果推送到一个数组中,然后执行第三个查询并告诉它也避免存储在该数组中的任何结果.

这是迄今为止我能想到的唯一解决方法。

MySQL 版本:5.6.45-cll-lve

如果你用的是MySQL8,你可以用一个common table expression,比如:

WITH cte AS
(SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
FROM rarity_db cr
JOIN card_db cd
ON cr.id = cd.id
WHERE cr.rarity = 'Common'
AND cr.set_code like '%lob%' 
GROUP BY cd.id ORDER BY RAND() LIMIT 7)

SELECT * FROM cte

UNION

(SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
FROM rarity_db cr
JOIN card_db cd
ON cr.id = cd.id
WHERE cr.rarity = 'Rare'
AND cr.set_code like '%lob%'
GROUP BY cd.id ORDER BY RAND() LIMIT 1)

UNION

(SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
FROM rarity_db cr
JOIN card_db cd
ON cr.id = cd.id
WHERE cr.rarity != 'Rare'
AND cr.set_code like '%lob%'
AND cr.id NOT IN (SELECT c.id FROM cte) -- <---- check this
GROUP BY cd.id ORDER BY RAND() LIMIT 1)

我认为以下内容可行(它应该是 sql 等同于我在上面评论中发布的想法):

    SELECT * FROM( 

    (SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
    FROM rarity_db cr
    JOIN card_db cd
    ON cr.id = cd.id
    WHERE cr.rarity != 'Rare'
    AND cr.set_code like '%lob%'
    GROUP BY cd.id ORDER BY RAND() LIMIT 1)

    UNION 

    (SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
    FROM rarity_db cr
    JOIN card_db cd
    ON cr.id = cd.id
    WHERE cr.rarity = 'Rare'
    AND cr.set_code like '%lob%'
    GROUP BY cd.id ORDER BY RAND() LIMIT 1)

    UNION    

    (SELECT cr.id, cr.price, cd.times, cd.name, cr.rarity 
    FROM rarity_db cr
    JOIN card_db cd
    ON cr.id = cd.id
    WHERE cr.rarity = 'Common'
    AND cr.set_code like '%lob%' 
    GROUP BY cd.id ORDER BY RAND() LIMIT 8) -- Set this limit to 8

    ) AS t LIMIT 9 -- then take first 9

编辑:更新了它,因为我以前的版本仍然可能失败并且 return 只有 8 个结果

然后,如果您需要按照原始查询的特定顺序反转 PHP 中的数组