Reconstruindo conversas do Salesforce Data Cloud com SQL
Como relacionar sessões, participantes e mensagens em uma única consulta
Em projetos envolvendo atendimento digital, uma necessidade bastante comum é conseguir responder perguntas simples:
Qual foi a conversa daquele cliente? Quando a sessão começou? Quem enviou cada mensagem? Foi o cliente, um bot ou um atendente? Qual era o conteúdo da mensagem?
Recentemente trabalhei em uma consulta relativamente simples no Salesforce Data Cloud para justamente reconstruir esse histórico.
O objetivo era transformar diferentes objetos relacionados a Messaging em uma visão única da conversa.
O cenário
No Salesforce, uma conversa não necessariamente está armazenada inteira dentro de uma única tabela.
As informações podem estar distribuídas entre estruturas responsáveis por:
- sessão de atendimento;
- relacionamento entre sessão e mensagens;
- entradas da conversa;
- participantes;
- usuários;
- conteúdo das mensagens.
Para reconstruir a conversa, precisamos relacionar esses dados.
A lógica ficou aproximadamente assim:
Messaging Session
↓
Related Record
↓
Conversation Entry
↓
Conversation Participant
↓
Salesforce User
O resultado é uma estrutura semelhante a:
Sessão
Cliente
Canal
CPF/CNPJ
Classificação
Status
↓
Mensagem 1 - Cliente
Mensagem 2 - Atendente
Mensagem 3 - Cliente
Mensagem 4 - Atendente
1. Começando pela sessão
A consulta parte da sessão de Messaging:
FROM MessagingSession_Home__dlm s
A partir dela recuperamos informações importantes sobre o atendimento:
s.Name__c AS nome_sessao,
s.CreatedDate__c AS data_criacao,
s.ChannelName__c AS nome_canal,
s.telefoneCliente_c__c AS telefone_cliente,
s.CPF_CNPJ_c__c AS cpf_cnpj,
s.Classificacao_c__c AS classificacao,
s.Status__c AS status
Com isso conseguimos trazer o contexto da conversa junto com cada mensagem.
Por exemplo:
Sessão: MS-001245
Canal: WhatsApp Safra
Telefone: 14999999999
Classificação: Negociação
Status: Encerrado
2. Relacionando a sessão às mensagens
O próximo passo é descobrir quais mensagens pertencem àquela sessão.
Para isso utilizamos uma tabela intermediária:
JOIN RELATED_RECORD ... r
ON LEFT(s.Id__c, 15) = r.record_id__c
Nesse ponto existe um detalhe interessante.
Utilizamos:
LEFT(s.Id__c, 15)
porque alguns relacionamentos no ecossistema Salesforce podem aparecer utilizando versões diferentes do identificador.
O Salesforce normalmente trabalha com IDs de 15 ou 18 caracteres.
Então normalizar o identificador durante o JOIN pode ser necessário dependendo da estrutura do Data Cloud.
3. Recuperando as mensagens
Depois relacionamos o registro encontrado com as entradas da conversa:
JOIN CONVERSATION_ENTRY ... e
ON r.conversation_entry_id__c = e.id__c
Assim conseguimos acessar:
e.id__c AS mensagem_id,
e.created_at__c AS data_mensagem,
e.conversation_participant_role__c AS tipo_remetente,
e.entry_payload__c AS conteudo_json
Agora já temos algo parecido com:
10:21 - Cliente
"Gostaria de negociar minha dívida"
10:22 - Atendente
"Olá, vou verificar as condições disponíveis."
10:24 - Cliente
"Obrigado."
4. Mas quem realmente enviou a mensagem?
Essa foi uma das partes mais interessantes.
A própria mensagem possui:
e.conversation_participant_display_name__c
Mas nem sempre esse campo é suficiente para identificar o participante corretamente.
Por isso relacionamos também:
ssot__ConversationEntry__dlm
com:
ConversationParticipant_Home__dll
e posteriormente:
ssot__User__dlm
A lógica permite tentar identificar o nome do usuário Salesforce responsável pela mensagem.
5. Utilizando COALESCE para encontrar o remetente
Para aumentar a confiabilidade do resultado utilizei:
COALESCE(
u_sender.ssot__FullName__c,
cp.ParticipantDisplayName__c,
e.conversation_participant_display_name__c
) AS remetente
O COALESCE retorna o primeiro campo que não estiver nulo.
Na prática, a prioridade fica:
1. Nome do usuário Salesforce
↓
2. Nome do participante
↓
3. Nome informado na própria mensagem
Isso evita depender exclusivamente de uma única fonte.
O resultado pode ficar assim:
Remetente: João Silva
Tipo: Agent
Remetente: Cliente
Tipo: EndUser
Remetente: Bot Atendimento
Tipo: Bot
6. Filtrando apenas mensagens reais
Outro filtro importante foi:
AND e.entry_type__c = 'Message'
Uma conversa pode possuir outros tipos de eventos além de mensagens.
Por exemplo:
entrada na sessão
mudança de participante
evento de sistema
status
mensagem
Como o objetivo era reconstruir o transcript, mantivemos apenas:
Message
7. Trabalhando com período
Também delimitamos o período analisado:
WHERE s.CreatedDate__c >=
TIMESTAMP WITH TIME ZONE '2026-09-01 00:00:00-03:00'
AND s.CreatedDate__c <
TIMESTAMP WITH TIME ZONE '2026-10-01 00:00:00-03:00'
Prefiro trabalhar utilizando:
>= início
< próximo período
em vez de tentar definir manualmente:
23:59:59
Isso deixa o filtro mais previsível.
8. Filtrando uma operação específica
Nesse exemplo também queremos analisar somente sessões relacionadas a um determinado canal.
AND LOWER(s.ChannelName__c) LIKE '%safra%'
O LOWER() ajuda a evitar problemas com diferenças de maiúsculas e minúsculas.
Assim valores como:
SAFRA
Safra
safra_whatsapp
WhatsApp Safra
podem ser encontrados pelo mesmo filtro.
9. Colocando a conversa na ordem correta
Por fim:
ORDER BY
s.CreatedDate__c DESC,
e.created_at__c ASC
Aqui temos duas ordenações.
As sessões mais recentes aparecem primeiro:
s.CreatedDate__c DESC
Mas dentro de cada sessão as mensagens aparecem cronologicamente:
e.created_at__c ASC
Assim conseguimos ler a conversa na mesma sequência em que ela aconteceu.
A consulta
Para uma publicação pública, recomendo substituir identificadores específicos do ambiente por nomes genéricos.
A estrutura principal fica assim:
SELECT
s.Name__c AS nome_sessao,
s.CreatedDate__c AS data_criacao,
s.ChannelName__c AS nome_canal,
s.telefoneCliente_c__c AS telefone_cliente,
s.CPF_CNPJ_c__c AS cpf_cnpj,
s.Classificacao_c__c AS classificacao,
s.Status__c AS status,
r.record_id__c AS sessao_vinculada,
e.id__c AS mensagem_id,
e.created_at__c AS data_mensagem,
COALESCE(
u_sender.ssot__FullName__c,
cp.ParticipantDisplayName__c,
e.conversation_participant_display_name__c
) AS remetente,
e.conversation_participant_role__c AS tipo_remetente,
e.entry_payload__c AS conteudo_json
FROM MessagingSession_Home__dlm s
JOIN RELATED_RECORD_<DATA_SOURCE>__dll r
ON LEFT(s.Id__c, 15) = r.record_id__c
JOIN CONVERSATION_ENTRY_<DATA_SOURCE>__dll e
ON r.conversation_entry_id__c = e.id__c
LEFT JOIN ssot__ConversationEntry__dlm ce
ON e.id__c = ce.ssot__Id__c
LEFT JOIN ConversationParticipant_Home__dll cp
ON ce.ssot__EngagementParticipantId__c = cp.Uuid__c
LEFT JOIN ssot__User__dlm u_sender
ON LEFT(cp.ParticipantEntityId__c, 15)
= LEFT(u_sender.ssot__Id__c, 15)
WHERE
s.CreatedDate__c >=
TIMESTAMP WITH TIME ZONE '2026-09-01 00:00:00-03:00'
AND s.CreatedDate__c <
TIMESTAMP WITH TIME ZONE '2026-10-01 00:00:00-03:00'
AND e.entry_type__c = 'Message'
AND LOWER(s.ChannelName__c) LIKE '%canal%'
ORDER BY
s.CreatedDate__c DESC,
e.created_at__c ASC;
Resultado
Apesar de ser uma consulta relativamente pequena, ela permite transformar diversos objetos do Salesforce em uma visão muito mais simples:
SESSÃO
│
├── Cliente
├── Telefone
├── CPF/CNPJ
├── Canal
├── Classificação
└── Status
↓
CONVERSA
│
├── 09:01 Cliente: Olá
├── 09:02 Bot: Como posso ajudar?
├── 09:03 Cliente: Quero negociar
├── 09:05 João Silva: Vou verificar
└── 09:07 Cliente: Obrigado
E isso abre diversas possibilidades.
Podemos utilizar esse resultado para:
- geração de transcripts;
- auditoria de atendimentos;
- análises de qualidade;
- classificação de conversas;
- indicadores operacionais;
- integrações externas;
- alimentação de bases internas;
- utilização futura com IA e análise conversacional.
O que esse pequeno SQL me ensinou
Uma consulta não precisa ter centenas de linhas para resolver um problema relevante.
Nesse caso utilizamos principalmente:
JOIN para conectar os objetos.
LEFT JOIN para manter mensagens mesmo quando determinada informação complementar não existir.
LEFT() para normalizar IDs Salesforce.
COALESCE() para melhorar a identificação do remetente.
TIMESTAMP WITH TIME ZONE para trabalhar corretamente com períodos.
LOWER() + LIKE para filtros mais flexíveis.
ORDER BY para reconstruir a sequência cronológica da conversa.
No final, o SQL apenas conecta os pontos.
O verdadeiro valor está em transformar dados distribuídos em uma informação que a operação consegue utilizar.
Conclusão
Esse foi um exemplo simples de como utilizei SQL dentro do Salesforce Data Cloud para reconstruir conversas de atendimento.
Partimos de objetos separados de Messaging e chegamos a uma visão contendo:
sessão + cliente + atendente + mensagem + horário + conteúdo.
É uma consulta pequena, mas que pode se tornar a base para projetos muito maiores de dados conversacionais, automação, analytics e inteligência artificial.
Tecnologias: Salesforce • Data Cloud • Messaging • SQL • WhatsApp • Dados Conversacionais


