Como retornar pedaços de um nome composto no SQL Server 2005

Boa Madrugada Pessoal,

Acabei de chegar da academia (ralação não é só no trabalho (rs)) e dei uma passada no fórum de SQL Server no MSDN como é de praxe. Achei uma dúvida muito interessante e após uma rápida inspiração encontrei uma resposta que me surpreendi. A dúvida original está no link abaixo:

Como pegar a 1ª Letra de cada de um nome composto ?
http://forums.microsoft.com/MSDN-BR/ShowPost.aspx?PostID=4199702&SiteID=21

Para falar do problema, nada melhor do que um script para demonstrar o problema e as possíveis soluções. Vamos então à criação da tabela:

— Criação da tabela de nomes
CREATE TABLE Nomes (Nome VARCHAR(80))

— Inserção de registros
INSERT INTO Nomes VALUES (‘Joana Maria’)
INSERT INTO Nomes VALUES (‘Ana Paula Silveira’)
INSERT INTO Nomes VALUES (‘Pedro Paulo Almeida’)
INSERT INTO Nomes VALUES (‘Carlos Eduardo da Silva’)
INSERT INTO Nomes VALUES (‘Rodrigo Diógenes Cunha Meira’)
INSERT INTO Nomes VALUES (‘Maria’)

O objetivo era obter a primeira letra de cada nome. No primeiro registro, deveríamos ter então as iniciais JM, no segundo APS, no terceiro PPA e assim por diante.

A tentação natural é pensar em uma solução típica de introdução à algoritmos e lógica de programação. Considerando que o espaço é um delimitador natural de nomes, bastaria capturar o primeiro caractér após o espaço e repetir esse procedimento sucessivas vezes até que a string estivesse terminada e não houvessem mais espaços. De fato essa é uma solução inicial e se eu estivesse usando o SQL Server 2000 faria algo nesse sentido conforme o script abaixo:

CREATE FUNCTION dbo.FnRecuperaPedacosNomeComposto (@Nome VARCHAR(80), @Separador CHAR(1))
RETURNS VARCHAR(80)
AS
BEGIN

DECLARE @Res VARCHAR(100)
SET @Res =

WHILE CHARINDEX(@Separador, @Nome) > 0
BEGIN
    SET
@Res = @Res + LEFT(@Nome,1)
    SET @Nome = SUBSTRING(@Nome, CHARINDEX(@Separador,@Nome)+1,LEN(@Nome)+1)
END

SET @Res = @Res + LEFT(@Nome,1)

RETURN (UPPER(@Res))

END

Posteriormente, bastaria aplicar a function sobre os registros da tabela e obter o retorno desejado. Ex:

SELECT Nome, dbo.FnRecuperaPedacosNomeComposto(Nome,‘ ‘) AS Iniciais FROM Nomes

Essa é uma boa solução, mas considerando as novas funcionalidades disponíveis a partir do SQL Server 2005, acho que os novos recursos devem ser explorados. Não que as soluções antigas não sejam boas, mas novos recursos podem fazer muitas coisas com menos código e muitas vezes um melhor desempenho. Ao invés de pensar na string como um conjunto de pedaços delimitados por um espaço (‘ ‘) podemos pensar na string como um XML composto por vários elementos. Uma solução alternativa baseada no poder do XML poderia ser utilizada se utilizarmos esse raciocínio.

— Declara uma variável para capturar o nome
DECLARE @Nome VARCHAR(100)
SET @Nome = ‘Rodrigo Diógenes Cunha Meira’

— Converter a string em XML
DECLARE @NomeXML XML
SET @NomeXML = ‘<N><n>’ + REPLACE(@Nome,‘ ‘,‘</n><n>’) + ‘</n></N>’
SELECT @NomeXML

O retorno é um documento XML cujo elementos correspondem a cada nome da string.

<N>
  <n>Rodrigo</n>
  <n>Diógenes</n>
  <n>Cunha</n>
  <n>Meira</n>
</N>

O próximo passo é extrair a primeira letra de cada elemento "n" do documento XML. Isso pode ser feito facilmente através de funções XPath. O método mais natural para trabalhar com esse documento XML seria o método Nodes, mas isso retornaria uma linha para elemento "n" e isso não seria factível já que todas a iniciais tem de aparecer na mesma linha. Para retornar todas as iniciais na mesma linha, o método query é mais apropriado.

— Representar o documento XML anterior
DECLARE @NomeXML XML
SET @NomeXML =
‘<N>
  <n>Rodrigo</n>
  <n>Diógenes</n>
  <n>Cunha</n>
  <n>Meira</n>
</N>’

— Aplicar o método query para extrair a primeira letra de cada elemento
SELECT @NomeXML.query(‘for $nome in /N/n/text() return substring($nome,1,1)’) As Iniciais

O retorno está bem próximo, mas ainda há espaços entre as iniciais e o tipo ainda é XML (embora mal formado, pois, está sem as tags e o elemento root). Basta então realizar a conversão para VARCHAR e retirar os espaços.

— Representar o documento XML anterior
DECLARE @NomeXML XML
SET @NomeXML =
‘<N>
  <n>Rodrigo</n>
  <n>Diógenes</n>
  <n>Cunha</n>
  <n>Meira</n>
</N>’

— Aplicar o método query para extrair a primeira letra, realizando as devidas adaptações
SELECT REPLACE(CAST(@NomeXML.query(
    ‘for $nome in /N/n/text() return substring($nome,1,1)’)
    AS VARCHAR(80)),‘ ‘,) As Iniciais

Para que o exemplo fique completo, basta aplicar uma lógica semelhante para todos os registros da tabela.

— Monta uma CTE para fazer algumas substituições
;WITH NomesF (Nome, NomeXML) AS (
SELECT Nome, CAST(
‘<N><n>’ + REPLACE(Nome,‘ ‘,‘</n><n>’) + ‘</n></N>’
AS XML) FROM Nomes)

— Retorna as iniciais
SELECT Nome, UPPER(REPLACE(
CAST(NomeXML.query(‘for $nome in /N/n/text() return substring($nome,1,1)’)
AS VARCHAR(80)),‘ ‘,)) As Iniciais FROM NomesF

Essa foi mais uma dica. Agora é hora de dormir.

[ ]s,

Gustavo

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s