Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Schreiben einer komplexen MySQL-Abfrage

Ich wäre versucht, eine Unterabfrage zu haben, die alle Wörter erhält, die eine Person gelernt hat, und diese mit sich selbst verbindet, mit den Wörtern GROUP_CONCAT zusammen mit einer Zählung. Also geben:-

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

Die Unterabfrage würde also etwa so aussehen:-

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

geben

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

Verbinden Sie dann die Ergebnisse mit den anderen Tabellen und suchen Sie nach Artikeln, bei denen die Haupt-ID-Wörter übereinstimmen, aber keine der anderen gefunden werden.

So etwas (obwohl nicht getestet, da keine Testdaten):-

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 

BEARBEITEN - aktualisiert, um Artikel mit mehr als 10 Wörtern auszuschließen, die noch nicht gelernt wurden.

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

BEARBEITEN - Versuch, die obige Abfrage zu kommentieren:-

Dadurch werden nur die Spalten ausgewählt

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

Diese Unterabfrage ruft jedes der gelernten Wörter zusammen mit einer durch Kommas getrennten Liste der Wörter mit einer größeren order_learned ab. Dies gilt für eine bestimmte Benutzer-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 

Dies dient nur dazu, die Artikel zu erhalten, in denen die Wörter (dh die aus der obigen Unterabfrage gelernten Wörter) verwendet werden

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

Diese Unterabfrage ruft die Artikel ab, die weniger als 10 Wörter enthalten, die der jeweilige Benutzer noch nicht gelernt hat.

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

Diese Verknüpfung dient dazu, Artikel zu finden, die Wörter in der durch Kommas getrennten Liste aus der ersten Unterabfrage enthalten (dh Wörter mit einer größeren order_learned). Dies geschieht als LEFT OUTER JOIN, da ich alle gefundenen Wörter ausschließen möchte (dies geschieht in der WHERE-Klausel, indem auf NULL geprüft wird)

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