Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Pisanie złożonego zapytania MySQL

Kusiłoby mnie, aby mieć podzapytanie, które pobiera wszystkie słowa, których nauczyła się osoba, i łączy je ze sobą, ze słowami GROUP_CONCAT razem z liczbą. Więc dając:-

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

Zapytanie podrzędne będzie więc wyglądało tak:-

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

dając

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

Następnie połącz wyniki tego z innymi tabelami, sprawdzając artykuły, w których pasują główne idword, ale żadne inne nie zostały znalezione.

Coś takiego (chociaż nie przetestowano jako brak danych testowych):-

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 

EDYCJA - zaktualizowano, aby wykluczyć artykuły zawierające więcej niż 10 słów, które nie zostały jeszcze nauczone.

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 - próba skomentowania powyższego zapytania:-

To po prostu wybiera kolumny

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

To podzapytanie pobiera każde z nauczonych słów, wraz z oddzieloną przecinkami listą słów z większą liczbą nauczone_kolejności. To jest dla konkretnego identyfikatora użytkownika

(
    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 

To jest tylko po to, aby uzyskać artykuły, w których używane są słowa (tj. słowa wyuczone z powyższego podzapytania)

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

To podzapytanie pobiera artykuły zawierające mniej niż 10 słów, których dany użytkownik jeszcze nie nauczył.

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

To złączenie ma na celu znalezienie artykułów, które zawierają słowa na liście oddzielonej przecinkami od pierwszego zapytania podrzędnego (tj. słów z większą liczbą order_learned). Odbywa się to jako LEFT OUTER JOIN, ponieważ chcę wykluczyć wszystkie znalezione słowa (odbywa się to w klauzuli WHERE, sprawdzając 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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySql — sposób na aktualizację fragmentu ciągu?

  2. Jak zautomatyzować migrację z samodzielnego MySQL do klastra Galera za pomocą Ansible

  3. Jak przechowywać strefę czasową użytkowników w mysql?

  4. Jak mogę ograniczyć liczbę wierszy na wartość pola w SQL?

  5. Jak odjąć 30 dni od aktualnej daty i godziny w mysql?