sql >> Database >  >> RDS >> Mysql

Een complexe MySQL-query schrijven

Ik zou in de verleiding komen om een ​​subquery te hebben die alle woorden krijgt die een persoon heeft geleerd en die tegen zichzelf voegt, met de woorden GROUP_CONCAT samen met een telling. Dus geven:-

Octopus, NULL, 0
Dog, "Octopus", 1
Spoon, "Octopus,Dog", 2

Dus de subquery zou zoiets zijn als:-

SELECT sub0.idwords, GROUP_CONCAT(sub1.idwords) AS excl_words, COUNT(sub1.idwords) AS older_words_cnt
FROM words_learned sub0
LEFT OUTER JOIN words_learned sub1
ON sub0.userId = sub1.userId
AND sub0.order_learned < sub1.order_learned
WHERE sub0.userId = 1
GROUP BY sub0.idwords

geven

idwords    excl_words    older_words_cnt
1          NULL          0
2          1             1
3          1,2           2

Voeg vervolgens de resultaten hiervan toe aan de andere tabellen en controleer op artikelen waar de belangrijkste id-woorden overeenkomen, maar geen van de andere wordt gevonden.

Iets als dit (hoewel niet getest omdat er geen testgegevens zijn):-

SELECT sub_words.idwords, words_inc.idArticle
(
    SELECT sub0.idwords, SUBSTRING_INDEX(GROUP_CONCAT(sub1.idwords), ',', 10) AS excl_words, COUNT(sub1.idwords) AS older_words_cnt
    FROM words_learned sub0
    LEFT OUTER JOIN words_learned sub1
    ON sub0.userId = sub1.userId
    AND sub0.order_learned < sub1.order_learned
    WHERE sub0.userId = 1
    GROUP BY sub0.idwords
) sub_words
INNER JOIN words words_inc
ON sub_words.idwords = words_inc.idwords
LEFT OUTER JOIN words words_exc
ON words_inc.idArticle = words_exc.idArticle
AND FIND_IN_SET(words_exc.idwords, sub_words.excl_words)
WHERE words_exc.idwords IS NULL
ORDER BY older_words_cnt
LIMIT 100 

BEWERKEN - bijgewerkt om artikelen uit te sluiten met meer dan 10 woorden die nog niet zijn geleerd.

SELECT sub_words.idwords, words_inc.idArticle,
sub2.idArticle, sub2.count, sub2.content
FROM
(
    SELECT sub0.idwords, GROUP_CONCAT(sub1.idwords) AS excl_words, COUNT(sub1.idwords) AS older_words_cnt
    FROM words_learned sub0
    LEFT OUTER JOIN words_learned sub1
    ON sub0.userId = sub1.userId
    AND sub0.order_learned < sub1.order_learned
    WHERE sub0.userId = 1
    GROUP BY sub0.idwords
) sub_words 
INNER JOIN words words_inc
ON sub_words.idwords = words_inc.idwords
INNER JOIN
(
    SELECT a.idArticle, a.count, a.content, SUM(IF(c.idwords_learned IS NULL, 1, 0)) AS unlearned_words_count
    FROM Article a
    INNER JOIN words b
    ON a.idArticle = b.idArticle
    LEFT OUTER JOIN words_learned c
    ON b.idwords = c.idwords
    AND c.userId = 1
    GROUP BY a.idArticle, a.count, a.content
    HAVING unlearned_words_count < 10
) sub2
ON words_inc.idArticle = sub2.idArticle
LEFT OUTER JOIN words words_exc
ON words_inc.idArticle = words_exc.idArticle
AND FIND_IN_SET(words_exc.idwords, sub_words.excl_words)
WHERE words_exc.idwords IS NULL
ORDER BY older_words_cnt
LIMIT 100

EDIT - poging tot commentaar op de bovenstaande vraag:-

Dit selecteert alleen de kolommen

SELECT sub_words.idwords, words_inc.idArticle,
sub2.idArticle, sub2.count, sub2.content
FROM

Deze subquery haalt elk van de aangeleerde woorden op, samen met een door komma's gescheiden lijst van de woorden met een grotere order_learned. Dit is voor een bepaalde gebruikers-ID

(
    SELECT sub0.idwords, GROUP_CONCAT(sub1.idwords) AS excl_words, COUNT(sub1.idwords) AS older_words_cnt
    FROM words_learned sub0
    LEFT OUTER JOIN words_learned sub1
    ON sub0.userId = sub1.userId
    AND sub0.order_learned < sub1.order_learned
    WHERE sub0.userId = 1
    GROUP BY sub0.idwords
) sub_words 

Dit is alleen om de artikelen te krijgen waarin de woorden (dwz de woorden die zijn geleerd van de bovenstaande subquery) worden gebruikt in

INNER JOIN words words_inc
ON sub_words.idwords = words_inc.idwords

Deze subquery haalt de artikelen op met minder dan 10 woorden die nog niet zijn geleerd door de specifieke gebruiker.

INNER JOIN
(
    SELECT a.idArticle, a.count, a.content, SUM(IF(c.idwords_learned IS NULL, 1, 0)) AS unlearned_words_count
    FROM Article a
    INNER JOIN words b
    ON a.idArticle = b.idArticle
    LEFT OUTER JOIN words_learned c
    ON b.idwords = c.idwords
    AND c.userId = 1
    GROUP BY a.idArticle, a.count, a.content
    HAVING unlearned_words_count < 10
) sub2
ON words_inc.idArticle = sub2.idArticle

Deze join is bedoeld om artikelen te vinden met woorden in de door komma's gescheiden lijst van de eerste subquery (dwz woorden met een grotere order_learned). Dit wordt gedaan als een LEFT OUTER JOIN omdat ik alle gevonden woorden wil uitsluiten (dit wordt gedaan in de WHERE-clausule door te controleren op NULL)

LEFT OUTER JOIN words words_exc
ON words_inc.idArticle = words_exc.idArticle
AND FIND_IN_SET(words_exc.idwords, sub_words.excl_words)
WHERE words_exc.idwords IS NULL
ORDER BY older_words_cnt
LIMIT 100


  1. Eenvoudigste overgang van MySQL naar MySQLi

  2. Is de kolomvolgorde van belang in uw MySQL-tabellen?

  3. MySQL MAX_JOIN_SIZE fouten

  4. Hibernate eqOrIsNull() gebruiken