Eu tenho uma tabela de artigos onde quero que o slug seja único.
CREATE TABLE article (
title char(50) NOT NULL,
slug char(50) NOT NULL
);
Quando o usuário insere um título, por exemplo News on Apple
, quero verificar o banco de dados para ver se existe um slug correspondente, por exemplo news-on-apple
. Em caso afirmativo, colocarei um sufixo em um valor numérico até encontrar um único, por exemplo news-on-apple-1
. Isso pode ser alcançado com uma consulta CTE recursiva em vez de fazer recursão em meu ORM. Existe um bom número aproximado onde devo parar de recorrer e errar. Posso imaginar pessoas usando o mesmo título 1.000 vezes, o que resultaria em 1.000 consultas apenas para criar 1 artigo.
É possível que meu entendimento de CTE recursivo esteja incorreto e não haja maneira melhor de encontrar um slug exclusivo. Por favor, sugira quaisquer alternativas.
Primeiro, você não quer usar
. Usechar(50)
varchar(50)
ou apenastext
. Consulte Mais informação:Supondo as seguintes regras:
-123
).Observe que todos os métodos a seguir estão sujeitos a condições de corrida : operações simultâneas podem identificar o mesmo nome "livre" para o próximo slug.
Para se defender contra isso, você pode impor uma restrição UNIQUE
slug
e estar preparado para repetir um INSERT em caso de violação de chave duplicada ou para obter um bloqueio de gravação na tabela no início da transação.Se você colar o sufixo ao nome do slug básico com um traço e permitir que os slugs básicos terminem em números separados, a especificação será um pouco ambígua (veja os comentários). Em vez disso, sugiro um delimitador exclusivo de sua escolha (o que não é permitido).
rCTE eficiente
Melhor desempenho sem rCTE
Se você se preocupa com milhares de slugs competindo pelo mesmo slug ou geralmente deseja otimizar o desempenho, considero uma abordagem diferente e mais rápida.
Se o slug básico ainda não foi executado, o segundo mais caro nunca
SELECT
é executado - o mesmo que acima, mas muito mais importante aqui. Verifique com , Postgres é inteligente dessa forma com consultas. Relacionado:EXPLAIN ANALYZE
LIMIT
Verifique a string inicial e o sufixo separadamente, para que a
LIKE
expressão possa usar um índice btree básico comtext_pattern_ops
comoExplicação detalhada:
Converta o sufixo para inteiro antes de aplicar
max()
. Os números na representação de texto não funcionam.Otimize o desempenho
Para obter o ideal, considere armazenar o sufixo separado do slug básico e concatenar o slug conforme necessário:
concat_ws('-' , slug, suffix::text) AS slug
A consulta para um novo slug torna-se então:
Idealmente suportado com um índice exclusivo em
(slug, suffix)
.Consulta para lista de slugs
Em qualquer versão do Postgres, você pode fornecer linhas em uma
VALUES
expressão.Você também pode usar
IN
com um conjunto de expressões de tipo de linha que é mais curta:Detalhes nesta questão relacionada (conforme comentado abaixo):
Para listas longas, o
JOIN
para umaVALUES
expressão é normalmente mais rápido.No Postgres 9.4 (lançado hoje!), você também pode usar a nova variante
unnest()
para desaninhar vários arrays em paralelo.Dado um array de slugs básicos e um array correspondente de sufixos (conforme comentário):