5 Stimmen

Auswählen aller Kinder von allen Eltern in mySQL

Ich habe eine einfache Tabelle wie diese:

+------------+---------+-----------+--------------+
| comment_id | post_id | parent_id | comment_text |
+------------+---------+-----------+--------------+
|          1 |     200 |         0 |          a   |
|          2 |     200 |         0 |          b   |
|          3 |     200 |         1 |          c   |
|          4 |     200 |         1 |          d   |
|          5 |     200 |         0 |          e   |
|          6 |     200 |         2 |          f   |
|          7 |     200 |         2 |          g   |
|          8 |     200 |         0 |          h   |
|          9 |     200 |         0 |          i   |
|         10 |     200 |         1 |          k   |
+------------+---------+-----------+--------------+
  • Die Spalte parent_id zeigt uns, dass dieser Kommentar eine Antwort auf einen anderen Kommentar mit dieser ID ist.

  • Angenommen, es gibt nur eine Verschachtelung von Kommentaren auf einer Ebene.

Jetzt muss ich zurückkehren nur die ersten 5 Elternkommentare und alle Kinder, die zu ihnen gehören.

Ich habe eine Anfrage dazu, aber es gibt ein Problem.

    (
     SELECT c.* 
     FROM comments AS c 
     WHERE c.post_id = '200' AND parent_id='0'
     LIMIT 0,5
    )
    UNION 
    (
     SELECT c.*
     FROM comments AS c
     WHERE c.post_id = '200' AND c.parent_id IN 
     (
      SELECT c.comment_id
      FROM comments AS c 
      WHERE c.post_id= '200' AND parent_id='0'
      LIMIT 0,5
     )
    )

Das Problem ist, dass meine aktuelle mySQL-Version kein LIMIT in Unterabfragen unterstützt. Außerdem scheint es, dass ich dieselbe SELECT-Abfrage zweimal ausführe!

Ich bin sicher, dass es eine Möglichkeit gibt, dies auf elegantere Weise zu tun.

1voto

Denis Pshenov Punkte 10719

Am Ende habe ich so etwas wie Andomar vorgeschlagen, aber ohne die Subquery:

        (
        select  *
        from    comments c
        where   c.post_id = '200' 
                and c.parent_id = 0
        order by comment_id DESC limit 0, 4
        )
union
       (
        select  *
        from    comments c
        join    comments p
        on      c.parent_id = p.comment_id
        where   c.post_id = '200' 
                and c.parent_id != '0'
        )
        order by comment_id DESC

EDITAR: Wie niaher richtig bemerkte, ergab diese Abfrage eine viel größere Ergebnismenge als erforderlich. Das macht diese Lösung falsch.

Zum Glück habe ich einen anderen Weg gefunden, der viel eleganter und sauberer ist.

Das ist die richtige Vorgehensweise:

SELECT c.*
    FROM (
        SELECT comment_id
        FROM comments
        WHERE post_id = 200 AND parent_id = 0
        ORDER BY comment_id ASC
        LIMIT 0,5       
    ) AS q
JOIN comments AS c
    ON c.comment_id = q.comment_id OR c.parent_id = q.comment_id

Ich habe diese Abfrage getestet, und sie läuft VIEL schneller als die vorherige.

1voto

Tim Punkte 2273

Dies ist die eleganteste Art, dies zu tun: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ . Das einzige Problem, auf das Sie stoßen werden, ist, dass Einfügungen einige Zeit in Anspruch nehmen, da die anderen Zeilen aktualisiert werden müssen; dies kann jedoch mit etwas Kreativität vermieden werden. Dieser Artikel ist ein guter Anfang.

0voto

Zak Punkte 24386

Können Sie nur 2 Auswahlen treffen?

Select * from comments where parentID = 0 limit 5

select * from comments where parentID IN (*list of the 5 parentIDs)

0voto

Andomar Punkte 224164

In meiner Version von MySQL können Sie Limit in Unterabfragen verwenden:

select  *
from    (
        select  *
        from    comments c
        where   c.post_id = '200' 
                and c.parent_id = 0
        limit    0, 3
        ) s
union
select  c.*
from    (
        select  *
        from    comments c
        where   c.post_id = '200' 
                and c.parent_id = 0
        limit    0, 3
        ) p
join    comments c
on      c.parent_id = p.comment_id;

Die doppelte Auswahl lässt sich ohne den with Anweisung, die von MySQL nicht unterstützt wird. Sie könnten eine temporäre Tabelle erstellen, um sie zu speichern:

drop table if exists top5comments;

create table top5comments
    select  *
    from    comments c
    where   c.post_id = '200' 
            and c.parent_id = 0
    limit    0, 3;

insert  top5comments
select  children.*
from    top5comments parents
join    comments children
on      children.parent_id = parents.comment_id;

select * from top5comments;

Dies würde Änderungen erfordern, wenn die Abfrage von mehreren Verbindungen gleichzeitig ausgeführt werden kann.

P.S. Übrigens, ein limit Anweisung wird in der Regel von einer order by Klausel. Ohne die order by erhalten Sie beliebige Zeilen, und die Ergebnisse können sich von Abfrage zu Abfrage unterscheiden.

0voto

Rudie Punkte 49458

Vielleicht meinen Sie dies:

SELECT
  *,
  (CASE WHEN parent_id = 0 THEN comment_id ELSE parent_id END) AS use_id
FROM
  comments
WHERE
  (CASE WHEN parent_id = 0 THEN comment_id ELSE parent_id END) IN (
    SELECT    comment_id
    FROM      comments
    WHERE     parent_id = 0
    ORDER BY  comment_id ASC
    LIMIT     3)
ORDER BY
  use_id ASC,
  parent_id;

Dabei werden die drei "ersten" (nach comment_id ASC geordneten) Elternkommentare und alle ihre Kinder genommen und die gesamte Liste nach parent-/comment_id sortiert (parent first, children after).

Das CASE WHEN THEN ELSE END ist für SQLite (so teste ich solche Dinge).

-- bearbeiten

Wenn diese Datensätze existieren (4 mehr als Ihre 10):

comment_id  post_id  parent_id  comment
1           200      0          a
2           200      0  b
3           200      1  c
4           200      1  d
5           200      0  e
6           200      2  f
7           200      2  g
8           200      0  h
9           200      0  i
10          200      1  j
11          200      1  k
12          200      2  l
13          200      0  m
14          200      1  n

wäre dies das Ergebnis:

comment_id  post_id parent_id   comment use_id
1        200    0   a   1
3        200    1   c   1
4        200    1   d   1
10       200    1   j   1
11       200    1   k   1
14       200    1   n   1
2        200    0   b   2
6        200    2   f   2
7        200    2   g   2
12       200    2   l   2
5        200    0   e   5

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X