sql >> Databáze >  >> RDS >> Mysql

MYSQL dotaz – Získejte nejnovější komentář související s příspěvkem

Tato chybová zpráva

je obvykle způsobena definicí vašich sloupců a tabulek. Obvykle to znamená, že na obou stranách rovnítka jsou různá řazení. Co musíte udělat, je vybrat jeden a zahrnout toto rozhodnutí do vašeho dotazu.

Problém s řazením zde byl v CROSS JOIN @prev_value, který pro použití vyžadoval explicitní řazení.

Také jsem mírně změnil logiku "row_number" na jednoduché křížové spojení a posunul logiku if do krajních poloh ve výběrovém seznamu.

Některá ukázková data jsou zobrazena níže. K testování dotazů jsou potřeba ukázková data. Každý, kdo se pokusí odpovědět na vaši otázku funkčními příklady, bude potřebovat data. Důvod, proč jej zde uvádím, je dvojí.

  1. abyste rozuměli jakémukoli výsledku, který prezentuji
  2. aby jste v budoucnu, až se zeptáte na další otázku týkající se SQL, chápali důležitost poskytování dat. Nejen pro nás je pohodlnější, že to uděláte. Pokud tazatel poskytne vzorová data, tazatel jim již bude rozumět – nebude to výmysl nějakého cizince, který věnoval část svého času pomoci.

Ukázková data

Upozorňujeme, že v tabulkách chybí některé sloupce, byly zahrnuty pouze sloupce uvedené v podrobnostech tabulky.

Tato ukázková data obsahují 5 komentářů k jednomu příspěvku (nezaznamenává se žádné hodnocení Líbí se)

CREATE TABLE Posts 
(
`id` int, 
`uuid` varchar(7) collate utf8_unicode_ci,
`imageLink` varchar(9) collate utf8_unicode_ci, 
`date` datetime
 );
    
INSERT INTO Posts(`id`, `uuid`, `imageLink`, `date`)
VALUES
(145, 'abcdefg', 'blah blah', '2016-10-10 00:00:00') ;

CREATE TABLE   USERS
(
`id` int, 
`username` varchar(15) collate utf8_unicode_ci,
 `profileImage` varchar(12) collate utf8_unicode_ci,
 `date` datetime
) ;
        
INSERT INTO     USERS(`id`, `username`, `profileImage`, `date`)
VALUES
(145, 'used_by_already', 'blah de blah', '2014-01-03 00:00:00') ;
    
    
CREATE TABLE Activity
(
`id` int, 
`uuid` varchar(4) collate utf8_unicode_ci, 
`uuidPost` varchar(7) collate utf8_unicode_ci,
 `type` varchar(40) collate utf8_unicode_ci, 
`commentText` varchar(11) collate utf8_unicode_ci, `date` datetime
) ;
        
INSERT INTO Activity (`id`, `uuid`, `uuidPost`, `type`, `commentText`, `date`)
 VALUES
(345, 'a100', 'abcdefg', 'comment', 'lah lha ha', '2016-07-05 00:00:00'),
(456, 'a101', 'abcdefg', 'comment', 'lah lah lah', '2016-07-06 00:00:00'),
(567, 'a102', 'abcdefg', 'comment', 'lha lha ha', '2016-07-07 00:00:00'),
(678, 'a103', 'abcdefg', 'comment', 'ha lah lah', '2016-07-08 00:00:00'),
(789, 'a104', 'abcdefg', 'comment', 'hla lah lah', '2016-07-09 00:00:00') ;

[Standardní chování SQL:2 řádky na dotaz příspěvku]

Toto byl můj prvotní dotaz s několika opravami. Změnil jsem pořadí sloupců ve výběrovém seznamu, takže při prezentaci výsledků snadno uvidíte některá data související s komentáři. Prostudujte si prosím výsledky, které jsou k dispozici, abyste pochopili, co dotaz udělá. Sloupce před # neexistují ve vzorových datech, se kterými pracuji, z důvodů, které jsem již uvedl.

SELECT
      Posts.id
    , Posts.uuid
    , rcom.uuidPost
    , rcom.commentText
    , rcom.`date` commentDate 
    #, Posts.caption
    #, Posts.path
    , Posts.`date`
    , USERS.id
    , USERS.username
    #, USERS.fullname
    , USERS.profileImage
    , COALESCE(A.LikeCNT, 0) num_likes
FROM Posts
INNER JOIN USERS ON Posts.id = 145
            AND USERS.id = 145
LEFT JOIN (
          SELECT
                COUNT(A.uuidPost) LikeCNT
              , A.UUIDPost
          FROM Activity A
          WHERE type = 'like'
          GROUP BY
                A.UUIDPOST
          ) A ON A.UUIDPost = Posts.uuid 
LEFT JOIN (
      SELECT
            @row_num := IF(@prev_value=UUIDPOST,@row_num+1,1) as row_number
          , commentText
          , uuidPost
          , `date`
          , @prev_value := UUIDPOST
      FROM Activity
      CROSS JOIN ( SELECT @row_num := 1, @prev_value := '' collate utf8_unicode_ci  ) xy
      WHERE type = 'comment'
      ORDER BY
            uuidPost
          , `date` DESC
      ) rcom ON rcom.uuidPost  = Posts.UUID
            AND rcom.row_number <= 2
ORDER BY
      posts.`date` DESC
      ;
      
      

Podívejte se na funkční ukázku tohoto dotazu na SQLFiddle

Výsledky :

|  id |    uuid | uuidPost | commentText |                   date |                      date |  id |        username | profileImage | num_likes |
|-----|---------|----------|-------------|------------------------|---------------------------|-----|-----------------|--------------|-----------|
| 145 | abcdefg |  abcdefg | hla lah lah | July, 09 2016 00:00:00 | October, 10 2016 00:00:00 | 145 | used_by_already | blah de blah |         0 |
| 145 | abcdefg |  abcdefg |  ha lah lah | July, 08 2016 00:00:00 | October, 10 2016 00:00:00 | 145 | used_by_already | blah de blah |         0 |

Existují 2 ŘADY - podle očekávání. Jeden řádek pro nejnovější komentář a další řádky pro další nejnovější komentář. Toto je normální chování pro SQL a dokud nebyl pod tuto odpověď přidán komentář, čtenáři otázky by předpokládali, že toto normální chování je přijatelné.

V otázce chybí jasně formulovaný „očekávaný výsledek“.

[Možnost 1:Jeden řádek na dotaz příspěvku, s AŽ 2 komentáři, přidanými sloupci]

V komentáři níže bylo odhaleno, že jste nechtěli 2 řádky na příspěvek a to by byla snadná oprava. No, je to trochu snadné, ALE existují možnosti a možnosti jsou diktovány uživatelem ve formě požadavků. POKUD otázka měla „očekávaný výsledek“, věděli bychom, kterou možnost zvolit. Nicméně zde je jedna možnost

SELECT
      Posts.id
    , Posts.uuid
    , max(case when rcom.row_number = 1 then rcom.commentText end) Comment_one
    , max(case when rcom.row_number = 2 then rcom.commentText end) Comment_two
    #, Posts.caption
    #, Posts.path
    , Posts.`date`
    , USERS.id
    , USERS.username
    #, USERS.fullname
    , USERS.profileImage
    , COALESCE(A.LikeCNT, 0) num_likes
FROM Posts
INNER JOIN USERS ON Posts.id = 145
            AND USERS.id = 145
LEFT JOIN (
          SELECT
                COUNT(A.uuidPost) LikeCNT
              , A.UUIDPost
          FROM Activity A
          WHERE type = 'like'
          GROUP BY
                A.UUIDPOST
          ) A ON A.UUIDPost = Posts.uuid 
LEFT JOIN (
      SELECT
            @row_num := IF(@prev_value=UUIDPOST,@row_num+1,1) as row_number
          , commentText
          , uuidPost
          , `date`
          , @prev_value := UUIDPOST
      FROM Activity
      CROSS JOIN ( SELECT @row_num := 1, @prev_value := '' collate utf8_unicode_ci  ) xy
      WHERE type = 'comment'
      ORDER BY
            uuidPost
          , `date` DESC
      ) rcom ON rcom.uuidPost  = Posts.UUID
            AND rcom.row_number <= 2
GROUP BY
      Posts.id
    , Posts.uuid
    #, Posts.caption
    #, Posts.path
    , Posts.`date`
    , USERS.id
    , USERS.username
    #, USERS.fullname
    , USERS.profileImage
    , COALESCE(A.LikeCNT, 0)
ORDER BY
      posts.`date` DESC
      ;

Podívejte se na druhý dotaz pracující na SQLFiddle

Výsledky dotazu 2 :

|  id |    uuid | Comment_one | Comment_two |                      date |  id |        username | profileImage | num_likes |
|-----|---------|-------------|-------------|---------------------------|-----|-----------------|--------------|-----------|
| 145 | abcdefg | hla lah lah |  ha lah lah | October, 10 2016 00:00:00 | 145 | used_by_already | blah de blah |         0 |

** Možnost 2, zřetězit nejnovější komentáře do jednoho seznamu odděleného čárkami **

SELECT
      Posts.id
    , Posts.uuid
    , group_concat(rcom.commentText) Comments_two_concatenated
    #, Posts.caption
    #, Posts.path
    , Posts.`date`
    , USERS.id
    , USERS.username
    #, USERS.fullname
    , USERS.profileImage
    , COALESCE(A.LikeCNT, 0) num_likes
FROM Posts
INNER JOIN USERS ON Posts.id = 145
            AND USERS.id = 145
LEFT JOIN (
          SELECT
                COUNT(A.uuidPost) LikeCNT
              , A.UUIDPost
          FROM Activity A
          WHERE type = 'like'
          GROUP BY
                A.UUIDPOST
          ) A ON A.UUIDPost = Posts.uuid 
LEFT JOIN (
      SELECT
            @row_num := IF(@prev_value=UUIDPOST,@row_num+1,1) as row_number
          , commentText
          , uuidPost
          , `date`
          , @prev_value := UUIDPOST
      FROM Activity
      CROSS JOIN ( SELECT @row_num := 1, @prev_value := '' collate utf8_unicode_ci  ) xy
      WHERE type = 'comment'
      ORDER BY
            uuidPost
          , `date` DESC
      ) rcom ON rcom.uuidPost  = Posts.UUID
            AND rcom.row_number <= 2
GROUP BY
      Posts.id
    , Posts.uuid
    #, Posts.caption
    #, Posts.path
    , Posts.`date`
    , USERS.id
    , USERS.username
    #, USERS.fullname
    , USERS.profileImage
    , COALESCE(A.LikeCNT, 0)
ORDER BY
      posts.`date` DESC
      

Viz tento třetí dotaz fungující na SQLFiddle

Výsledky dotazu 3 :

|  id |    uuid | Comments_two_concatenated |                      date |  id |        username | profileImage | num_likes |
|-----|---------|---------------------------|---------------------------|-----|-----------------|--------------|-----------|
| 145 | abcdefg |    hla lah lah,ha lah lah | October, 10 2016 00:00:00 | 145 | used_by_already | blah de blah |         0 |

** Souhrn **

Předložil jsem 3 dotazy, každý zobrazuje pouze 2 nejnovější komentáře, ale každý dotaz to dělá jiným způsobem. První dotaz (výchozí chování) zobrazí 2 řádky pro každý příspěvek. Možnost 2 přidá sloupec, ale odebere druhý řádek. Možnost 3 zřetězí 2 nejnovější komentáře.

Vezměte prosím na vědomí, že:

  • V otázce chybí definice tabulky pokrývající všechny sloupce
  • V otázce chybí ukázková data, takže je pro vás obtížnější porozumět všem zde uvedeným výsledkům, ale také je pro nás obtížnější připravit řešení
  • Otázka také postrádá definitivní "očekávaný výsledek" (chtěný výstup), což vedlo k další složitosti při odpovídání

Doufám, že další poskytnuté informace budou k něčemu užitečné a že nyní také víte, že pro SQL je normální prezentovat data jako více řádků. Pokud si nepřejete toto normální chování, uveďte prosím konkrétně, co ve své otázce skutečně chcete.

Postscript. Chcete-li zahrnout ještě další poddotaz pro "follows", můžete použít podobný poddotaz jako ten, který již máte. Může být přidán před nebo po tomto dílčím dotazu. Můžete jej také vidět, jak se používá na sqlfiddle zde

LEFT JOIN (
          SELECT
                COUNT(*) FollowCNT
              , IdOtherUser
          FROM Activity
          WHERE type = 'Follow'
          GROUP BY
                IdOtherUser
          ) F ON USERS.id = F.IdOtherUser

Zatímco přidání dalšího poddotazu může vyřešit vaši touhu po dalších informacích, celkový dotaz se může zpomalit úměrně růstu vašich dat. Jakmile se rozhodnete pro funkci, kterou skutečně potřebujete, může být užitečné zvážit, jaké indexy na těchto tabulkách potřebujete. (Domnívám se, že by vám bylo doporučeno požádat o tuto radu samostatně, a pokud ano, ujistěte se, že zahrnete 1. úplný DDL vašich tabulek a 2. plán vysvětlení dotazu.)



  1. Proveďte více příkazů SQL v jazyce Java

  2. Výběr záznamů v pořadí podle počtu záznamů, které obsahují určité pole

  3. Několik nápadů o nízkoúrovňovém sdružování zdrojů v PostgreSQL

  4. jak udělit oprávnění uživatele ke konkrétnímu schématu?