MySQL: Gruppera resultaten?

Tråden skapades och har fått 8 svar. Det senaste inlägget skrevs .
1
  • Medlem
  • Boden
  • 2013-08-15 13:00

Hej.
Jag jobbar på ett galleri som ska ha funktionen, likt iPhoto, att du kan dra musen horisontellt över en bild för att bläddra igenom bilderna i den "gruppen". (Exempel: http://vimeo.com/6639381)

Jag har en MySQL tabell med följande struktur:

Citat:

id____________namn_____________grupp_____________bild
1_____________test1_____________grupp1_____________bild1
2_____________test2_____________grupp1_____________bild2
3_____________test3_____________ingen_______________bild3
4_____________test4_____________grupp2_____________bild4
5_____________test5_____________grupp2_____________bild5
6_____________test6_____________ingen______________bild6
7_____________test7_____________ingen______________bild7

Hur gör jag för att på bästa sätt hämta de 15 senaste inläggen i en tabell och behandla de som har samma värde under "grupp" som en array? Grupper räknas som 1 resultat mot de 15 senaste. Gruppen "ingen" ska inte räknas som en grupp.

Följande ska genereras utifrån tabellen ovan:

Citat:

<div class="square">
       <img src="img/bild1.jpg">
       <img src="img/bild2.jpg"> (2 bilder iom "grupp1")
</div>

<div class="square">
       <img src="img/bild3.jpg">
</div>

<div class="square">
       <img src="img/bild4.jpg">
       <img src="img/bild5.jpg"> (2 bilder iom "grupp2")
</div>

<div class="square">
       <img src="img/bild6.jpg">
</div>

<div class="square">
       <img src="img/bild7.jpg">
</div>

Någon som kan hjälpa mig här, hade verkligen uppskattat det!
Tack på förhand!

@jensm: Om jag förstår dig rätt så är det här lättast att lösa utanför databasen: Gör en lista med grupper och läs resultatet rad för rad, om en bild har samma grupp som den senaste gruppen i listan kan du lägga till bilden bilden där, annars skapa en ny grupp med bilden.

Sök på GROUP_BY på mysql-sida. Minns inte exakt vad den gör längre.

  • Medlem
  • Boden
  • 2013-08-15 14:37

@Mattias Hedman: GROUP BY hämtar rader med unika värden i en kolumn.

Jag har kommit en bit på vägen tack vare GROUP BY faktiskt.

Så här ser min query ut nu:

Citat:

$query = "SELECT
*
,CASE
WHEN grupp = 'ingen' then RAND()
ELSE grupp
END AS grouper
FROM
galleri
GROUP BY
grouper
ORDER BY
id DESC";

Detta ger mig ett resultat där enbart första raden med en specifik grupp hämtas. Gruppen "ingen" hoppas över och hämtas som vanligt.
Alltså, galleriet visas som det ska, förutom att istället för att hämta alla inom en grupp hämtas bara en.

Jag har hittat något som heter GROUP CONCAT som, om jag förstår det rätt, hämtar alla resultat där kolumnen har samma värde och separerar värdena med ett kommatecken. Jag har dock inte fått det att funka, får felmeddelande hela tiden. Någon som vet hur man använder GROUP CONCAT?

GROUP BY ger dig en rad per grupp i resultatet, sedan kan du använda aggregate functions för att arbeta med datan inom dessa för att exempelvis summera värden, hitta min-/max-värden etc. MySQL har även GROUP_CONCAT för att skapa en kommaseparerad lista av de värden som ingår i gruppen, men den funktionen har en "bad smell" som vanligtvis antyder att man bör överväga en annan design, och i det här fallet hjälper det inte eftersom att raderna fortfarande skulle behöva behandlas utanför databasen: Du måste skriva kod som delar upp listorna/grupperna i rader igen för att kunna arbeta med dessa som enskilda bilder.

CREATE TABLE `galleri`
(
    `id`    INTEGER UNSIGNED NOT NULL,
    `namn`  VARCHAR(255) NOT NULL,
    `grupp` VARCHAR(255) NOT NULL,
    `bild`  VARCHAR(255) NOT NULL,

    UNIQUE INDEX (`id`)
);

INSERT INTO
    `galleri`
(
    `id`,
    `namn`,
    `grupp`,
    `bild`
)
VALUES
    (1, "test1", "grupp1", "bild1"),
    (2, "test2", "grupp1", "bild2"),
    (3, "test3", "ingen",  "bild3"),
    (4, "test4", "grupp2", "bild4"),
    (5, "test5", "grupp2", "bild5"),
    (6, "test6", "ingen",  "bild6"),
    (7, "test7", "ingen",  "bild7");
SELECT
    `grupp`,
    GROUP_CONCAT(`id`)   AS `id`,
    GROUP_CONCAT(`namn`) AS `namn`,
    GROUP_CONCAT(`bild`) AS `bild`
FROM
    `galleri`
GROUP BY
    `grupp`;

#
# +--------+-------+-------------------+-------------------+
# | grupp  | id    | namn              | bild              |
# +--------+-------+-------------------+-------------------+
# | grupp1 | 1,2   | test1,test2       | bild1,bild2       |
# | grupp2 | 4,5   | test4,test5       | bild4,bild5       |
# | ingen  | 3,6,7 | test3,test6,test7 | bild3,bild6,bild7 |
# +--------+-------+-------------------+-------------------+
#
# http://sqlfiddle.com/#!2/4333cc/1
#

Det verkar som om du använder PHP? Jag hade gjort något i den här stilen för att gruppera de senaste 15 bilderna istället:

$mysqli  = new \mysqli(/*...*/);
$result  = $mysqli->query("SELECT `g`.* FROM `galleri` AS `g` ORDER BY `g`.`id` DESC;");
$grupper = [];
$c       = 0;

while(($row = $result->fetch_assoc()) !== null)
{
    if($c > 0 && $row["grupp"] !== "ingen" && $row["grupp"] === $grupper[$c - 1][0]["grupp"])
    {
        $grupper[$c - 1][] = $row;
    }
    else
    {
        $grupper[] = [$row];
        ++$c;
    }
}

$result->free();

foreach($grupper as $grupp)
{
    echo sprintf(
        "Grupp: %s\n",
        $grupp[0]["grupp"]
    );
    
    foreach($grupp as $bild)
    {
        echo sprintf(
            "    Id: %-2d Bild: %s\n",
            $bild["id"],
            $bild["bild"]
        );
    }
    
    echo "\n";
}

Resultat:

Grupp: ingen
    Id: 7  Bild: bild7

Grupp: ingen
    Id: 6  Bild: bild6

Grupp: grupp2
    Id: 5  Bild: bild5
    Id: 4  Bild: bild4

Grupp: ingen
    Id: 3  Bild: bild3

Grupp: grupp1
    Id: 2  Bild: bild2
    Id: 1  Bild: bild1

Tips: Om du använder [code] istället för [quote] så behåller forumet din formatering.

Senast redigerat 2013-08-15 17:48

@jensm: Kanske missförstod, är det de 15 senaste "grupperna" du vill ha? Det blir mer komplicerat med de senaste grupperna, men för mindre sets har du ett alternativ här:

SELECT
    COALESCE(`folder`.`id`,    `single`.`id`)    AS `id`,
    COALESCE(`folder`.`namn`,  `single`.`namn`)  AS `namn`,
    COALESCE(`folder`.`grupp`, `single`.`grupp`) AS `grupp`,
    COALESCE(`folder`.`bild`,  `single`.`bild`)  AS `bild`
FROM
    (
        SELECT
            MAX(`id`) AS `latestImage`,
            `grupp`
        FROM
            `galleri`
        GROUP BY
            IF(`grupp` = "ingen", `id`, `grupp`)
        ORDER BY
            `latestImage` DESC
        LIMIT
            5
    ) AS `headImage`
    LEFT JOIN `galleri` AS `folder` ON (
        `headImage`.`grupp` = `folder`.`grupp` AND NOT
        `headImage`.`grupp` = "ingen"
    )
    LEFT JOIN `galleri` AS `single` ON (
        `headImage`.`latestImage` = `single`.`id` AND
        `headImage`.`grupp`       = "ingen"
    )
ORDER BY
    `latestImage` DESC,
    `id` DESC;

#
# +------+-------+--------+-------+
# | id   | namn  | grupp  | bild  |
# +------+-------+--------+-------+
# |    7 | test7 | ingen  | bild7 |
# |    6 | test6 | ingen  | bild6 |
# |    5 | test5 | grupp2 | bild5 |
# |    4 | test4 | grupp2 | bild4 |
# |    3 | test3 | ingen  | bild3 |
# |    2 | test2 | grupp1 | bild2 |
# |    1 | test1 | grupp1 | bild1 |
# +------+-------+--------+-------+
#

Fungerar med koden ovan.

Senast redigerat 2013-08-15 19:01
  • Medlem
  • Boden
  • 2013-08-15 18:33

@Luftvargen: Tack för hjälpen, verkligen jättebussigt! Jag har fått det till att fungera precis som jag vill, ditt förra inlägg var lösningen, jag la bara till ett CASE för att inte gruppera bilderna med "ingen" som grupp. Här är min query:

Citat:

$query = "SELECT GROUP_CONCAT(`bild`) AS `bild`, namn, CASE WHEN grupp = 'ingen' then RAND() ELSE grupp END AS grouper FROM `galleri` GROUP BY `grouper` ORDER BY id DESC LIMIT 15";

Kör citat den här gången annars bryts inte raden

Den ger mig alla bilder separerat med kommatecken, sen är det bara att göra en array av det och loopa igenom när bilderna ritas ut:

$bilder = explode(',', $row[bild]);
for($i=0; $i < count($bilder); $i++){
	echo '<img src="bilder/'.$bilder[$i].'.jpg">';
}

Skoj med SQLFiddle, den hade jag helt missat.

Här är mitt förslag på att lösa grupperingen (sorteringen gör att det blir likadant som i första inlägget).

SELECT
    `grupp`,
    GROUP_CONCAT(`id`)   AS `id`,
    GROUP_CONCAT(`namn`) AS `namn`,
    GROUP_CONCAT(`bild`) AS `bild`
FROM
    `galleri`
GROUP BY
  IF(`grupp` = 'ingen', `id`, `grupp`)
ORDER BY
    `id`;
	
http://sqlfiddle.com/#!2/4333cc/10

För en lösning på hela problemet behövs nästan tydligare instruktioner. Är det så att du vill ha de 15 senaste unika grupperna (eller ingen grupp)? Vill du ha endast de bilder som finns inom raderna som beaktas för 15 senaste, eller vill du ha ett visst antal bilder per grupp, oberoende av hur gamla bilderna är?

  • Medlem
  • Boden
  • 2013-08-15 21:02

Jag har hittat, och postat en fungerande lösning. Givetvis är det trevligt att hålla diskussionen igång för att hitta en ev. bättre lösning..

Jag har 15 rutor (div) som agerar thumbnails. Om en bild i en av rutorna är med i en grupp ska rutan innehålla alla bilder i den gruppen, och ingen annan ruta ska innehålla någon bild från den gruppen. Det är alltså inte de 15 senaste bilderna, inte de 15 senaste grupperna, utan de 15 senaste "bilderna" där alla bilder med samma grupp räknas som EN bild. 15 rutor ska alltså fyllas med bilder, och är en av bilderna med i en grupp ska alla bilder från samma grupp läggas i den rutan.

Min lösning, med hjälp av er (tack!) finns här ovan.

1
Bevaka tråden