Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Escrevendo uma consulta MySQL complexa


Eu ficaria tentado a ter uma subconsulta que pegasse todas as palavras que uma pessoa aprendeu e juntasse isso contra si mesma, com as palavras GROUP_CONCAT junto com uma contagem. Então dando:-
Octopus, NULL, 0
Dog, "Octopus", 1
Spoon, "Octopus,Dog", 2

Portanto, a subconsulta seria algo como:-
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

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

Em seguida, junte os resultados disso com as outras tabelas, verificando artigos em que as principais idwords correspondam, mas nenhuma das outras seja encontrada.

Algo assim (embora não testado como sem dados de teste):-
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 

EDIT - atualizado para excluir artigos com mais de 10 palavras que ainda não foram aprendidas.
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 - tente comentar a consulta acima:-

Isso apenas seleciona as colunas
SELECT sub_words.idwords, words_inc.idArticle,
sub2.idArticle, sub2.count, sub2.content
FROM

Essa subconsulta obtém cada uma das palavras aprendidas, juntamente com uma lista separada por vírgulas das palavras com uma ordem_aprendida maior. Isso é para um ID de usuário específico
(
    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 

Isso é apenas para obter os artigos em que as palavras (ou seja, as palavras aprendidas na subconsulta acima) são usadas em
INNER JOIN words words_inc
ON sub_words.idwords = words_inc.idwords

Esta subconsulta obtém os artigos com menos de 10 palavras que ainda não foram aprendidas pelo usuário em particular.
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

Essa junção é para encontrar artigos que tenham palavras na lista separada por vírgulas da 1ª subconsulta (ou seja, palavras com uma ordem_aprendida maior). Isso é feito como LEFT OUTER JOIN, pois quero excluir quaisquer palavras encontradas (isso é feito na cláusula WHERE verificando 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