Respondendo a esta pergunta ,
Dada esta tabela (construída a partir da pergunta):
CREATE TABLE wordcount (id SERIAL NOT NULL PRIMARY KEY, description TEXT NOT NULL);
INSERT INTO wordcount (description) VALUES ('What a great day');
INSERT INTO wordcount (description) VALUES ('This is a product. It is useful');
produza este resultado:
id | word_count | unique_word_count | Description
---------+------------+-------------------+---------------
1 | 4 | 4 | What a great day
2 | 7 | 6 | This is a product. It is useful
Eu dei a resposta (correta), que você pode encontrar aqui .
No entanto, em um comentário, o OP fez outra pergunta - e se a string em questão fosse ['a', ' ', ' ', 'b']
e minha solução quebrasse completamente - para começar, a string nem entraria INSERT
na tabela.
Então, a questão agora é, como se lida com strings como esta - ou seja, com apóstrofos, colchetes etc. Vou dar minha própria resposta e também oferecer um bônus por uma solução mais elegante.
Soluções com múltiplas opções serão altamente valorizadas, assim como aquelas que mostrarem evidências de "pensar fora da caixa" (desculpe o clichê - mas cabe aqui! :-) ). Eu também vou dar uma explicação detalhada do meu raciocínio - que vai ganhar elogios também! As opções que mencionam outros servidores também ganharão mérito. Obviamente, só posso conceder o bônus a uma pessoa, mas votarei positivamente em todas as respostas decentes.
Só posso oferecer um bônus em dois dias - então postarei minha resposta e oferecerei o bônus (+100) quando puder. Além disso, quaisquer soluções que lidem com strings com as quais eu não possa lidar - ainda não foram exaustivamente testadas.
O primeiro passo obviamente é criar a tabela e os dados (conforme a pergunta mencionada ):
O primeiro "salva-vidas" foi a cotação do dólar (
$$
) - um recurso realmente interessante do PostgreSQL. Eu estava realmente me debatendo antes de me deparar com isso - não conseguia nem colocar os dados na tabela (tentando barras invertidas, aspas duplas etc.)Meu SQL final se parece com isso (fiddle aqui ):
Resultado:
Lógica explicada:
Eu decidi não me preocupar com capitalização - ou seja, "It" e "it" são palavras diferentes neste caso - se isso for um problema, a simples adição de uma
UPPER()
função resolveria isso - não é o núcleo da questão.Passo 1:
Resultado:
Etapa 2 (remover todos os não-espaços, não-alfa)
Resultado:
Passo 3 (coloque as strings em um array):
Resultado:
Por fim, a resposta em si -
UNNEST
e, em seguida, selecione essas palavrasLENGTH > 0
agrupadas por id e descrição.ou seja,
SELECT
o necessário do seguinte cte (Common Table Expression) - o cte não é estritamente necessário - eu poderia ter usado oUNNEST...
em toda a minha consulta final, mas isso teria sido horrível para ler e depurar. É a razão pela qual as Expressões de Tabela Comuns foram inventadas!Quanto à sua solução: inteligente e com uma explicação sólida. Mas e esses casos:
''
,NULL
,'"§$%'
,'-'
? Sem palavras. A contagem deve ser0
- mas sua solução descarta essas linhas completamente.Além disso, qualquer solução depende em primeiro lugar da definição exata de "palavra" , que pode variar muito ...
Processamento de string baseado em expressão regular
Semelhante à sua solução, com algumas sugestões alternativas:
db<>fiddle aqui (caso de teste estendido)
O núcleo é
regexp_replace(description, '\W+', ' ', 'g')
substituir todas as substrings de caracteres que não sejam palavras por um único espaço. Consulte Escapes abreviados de classe de expressão regular . Isso remove todo o ruído no início do jogo.Seguido de barato
trim()
para remover espaços à esquerda/à direita estring_to_array()
converter a string preparada em uma matriz.Obter
word_count
da matriz diretamente. Mais uma vez: barato.O
unique_word_count
de umaLATERAL
subconsulta comcount(DISTINCT ...)
. Essa parte pode ou não ser mais lenta do que um desaninhamento/agregação total. É um pouco mais simples.O
COALESCE
no exteriorSELECT
cuida daNULL
entrada (a pergunta original não mencionou umaNOT NULL
restrição). Opcional, caso você precise0
em vez deNULL
.Ou (mais rápido em um teste rápido com strings curtas):
Isso descarta linhas com 0 palavras, como sua resposta.
(Ab-) usando analisador de pesquisa de texto
Usar a função de pesquisa de texto
ts_parse()
é mais simples. Pode ou não ser mais rápido. Mas primeiro estude os vários tokens identificados pelo analisador de pesquisa de texto e veja o que corresponde à sua definição de "palavra":Para apenas "Palavras ASCII" : (Ao contrário de acima, o sublinhado (
_
) não é tratado como caractere de palavra aqui):Para evitar
_
separar palavras, use simplesreplace()
primeiro:Novamente, para manter todas as linhas:
db<>fique aqui
Relacionado: