Auto-junções recursivas

15

Eu tenho uma commentstabela, que pode ser simplificada para isso:

comments
=======
id
user_id
text
parent_id

onde parent_idé anulável, mas pode ser uma chave para seu comentário pai.


Agora, como posso selecttodos os descendentes de um comentário específico?
Os comentários podem estar vários níveis abaixo ...

Josh
fonte

Respostas:

16

Consultas hierárquicas , como essas consultas recursivas são conhecidas, não são suportadas pelo MySQL.

No entanto, eles são suportados no Oracle, Microsoft SQL Server, DB2 e PostgreSQL, entre outros.

Se você precisar de uma solução alternativa, poderá encontrar um truque dinâmico (e, portanto, potencialmente perigoso) aqui: /programming/8104187/mysql-hierarchical-queries

Você também pode encontrar uma discussão sobre como armazenar dados hierárquicos com outros modelos que não uma Lista de Adjacências (por exemplo, a coluna Pai ) aqui: /programming/192220/what-is-the-most-efficient- maneira elegante de analisar uma mesa plana em uma árvore /

Boa sorte!

Valmoer
fonte
Gostaria de saber como essa solução no seu segundo link pode ser perigosa. Você poderia explicar isso? Caso contrário, seja bem-vindo ao site!
Dezso
3
@dezso: O próprio criador da consulta, Quassnoi " * Não é seguro para atualização *, porque o MySQL não define claramente o comportamento da variável da sessão. No entanto, é a única maneira de lidar com listas de adjacências em tempo hábil na consulta ." Perigoso pode ter sido uma palavra muito forte ( potencialmente instável ?), Mas prefiro errar por precaução (além disso, tenho mais conhecimento em Oracle do que MySQL, por isso queria ser mais cauteloso). Obrigado pelo bem-vindo, a propósito! Há muito tempo que sou espreitador da rede SE e decidi que era hora de pagar um pouco.
Valmoer
2
A sintaxe WITH [RECURSIVE] agora é suportada no mysql 8.0. dev.mysql.com/doc/refman/8.0/en/with.html
ClearCrescendo
6

O design desta tabela é um "antipadrão" SQL antipadrão, conforme descrito por Bill Karwin (a partir do slide 48 em sua apresentação Strike Back do Antipatterns SQL ). O problema desse design especificamente é a dificuldade de obter todos os descendentes (ou pais) de um nó. Como você está usando o MySQL, não é possível usar expressões de tabela comuns (a instrução WITH e seu modificador RECURSIVE) presentes em outros RDBMSes.

O que resta é:

  • use uma implementação alternativa da estrutura hierárquica de dados (as respostas a esta pergunta podem ser uma boa referência)
  • crie consultas de associação automática com um limite de profundidade. Para profundidade = 5, você pode usar algo nas linhas de:

    SELECT *
    FROM comments AS c1
      JOIN comments AS c2 ON (c2.parent_id = c1.id)
      JOIN comments AS c3 ON (c3.parent_id = c2.id)
      JOIN comments AS c4 ON (c4.parent_id = c3.id)
      JOIN comments AS c5 ON (c5.parent_id = c4.id)
  • use um RDBMS que suporte WITH RECURSIVE (embora isso provavelmente não seja uma opção para a maioria das pessoas)

redguy
fonte
2
Eu discordo de Bill Karwin aqui. O modelo de adjacência não é um antipadrão. Com um DBMS moderno que suporta consultas recursivas (a Oracle suporta isso há mais de 20 anos), esse modelo é muito eficiente para recuperar e atualizar.
A_horse_with_no_name
5

O MySQL não suporta consultas recursivas como a que você precisa.

O que fiz um tempo atrás foi escrever Procedimentos armazenados que fornecem o modelo para isso.

Em vez de reinventar a roda, darei a você os links para meus posts anteriores sobre isso:

Resumindo, os Procedimentos armazenados que fiz para fazer o percurso da árvore de pré-encomenda usando o processamento da fila

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

Pai de todos os filhos (como o procedimento armazenado GetFamilyTree)

  • PASSO01) Comece com a parent_idem uma fila
  • PASSO 02) Desfileirar o próximo parent_idcomo o atual
  • PASSO03) Coloque em fila todos os idvalores que possuem a correnteparent_id
  • PASSO04) Imprimir ou coletar o comentário
  • PASSO05) Se a fila não estiver vazia, vá para STEP02
  • PASSO 06) Você terminou !!!

Filho de todos os pais (como o GetAncestry Stored Procedure)

  • STEP01) Comece com um idem uma fila
  • PASSO 02) Desfileirar o próximo idcomo o atual
  • PASSO03) Coloque o parent_idvalor da corrente na filaid
  • PASSO04) Imprimir ou coletar o comentário
  • PASSO05) Se a fila não estiver vazia, vá para STEP02
  • PASSO 06) Você terminou !!!

Consulte os procedimentos armazenados em meus outros posts para ver a implementação.

De uma chance !!!

RolandoMySQLDBA
fonte
2
SELECT  group_concat(@id :=
        (
        SELECT  id
        FROM    comments
        WHERE   parent_id = @id
        )) AS comment
FROM    (
        SELECT  @id := 1
        ) vars
STRAIGHT_JOIN
        comments
WHERE   @id IS NOT NULL

violino

Praveen Prasannan
fonte