Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Dicas para usar o SQL Server com o Salesforce

Índice

  1. Visão geral
  2. Cláusula WHERE
  3. Várias junções de tabela
  4. Tabela local anexada a uma tabela remota
  5. Inserir, atualizar e excluir
  6. Atualizar
  7. Atualizar com parâmetros
  8. Inserindo um novo registro e obtendo um erro BLOB
  9. Obter o ID do Salesforce para o último registro que você inseriu
  10. Atualizando dados do SQL Server quando os dados do Salesforce são alterados
  11. Validação de esquema lento
  12. Limitações do provedor OLEDB da Microsoft para ODBC
  13. Como encontro registros com um feed de linha (nova linha) no endereço de faturamento?
  14. Posso ver quais tabelas estão disponíveis por meio do software Easysoft?
  15. Posso ver quais colunas estão disponíveis por meio do software Easysoft?
  16. Posso criar programaticamente um servidor vinculado?

Visão geral


Este documento fornece algumas dicas sobre como usar o SQL Server com o Salesforce. Os componentes usados ​​para conectar o SQL Server ao Salesforce são um SQL Server Linked Server e o Easysoft Salesforce ODBC Driver. Como você conecta o SQL Server ao Salesforce é descrito neste artigo. Para os exemplos neste documento, o nome do Linked Server (que você faz referência em seus comandos SQL) usado é SF8.

Todo o SQL neste documento foi testado no SQL Server 2017 e no driver ODBC do Easysoft Salesforce nas versões 2.0.0 a 2.0.7.

As funções do SQL Server OPENQUERY e EXEC (EXECUTE ) foram introduzidos no SQL Server 2008 e essas funções são compatíveis com todas as versões do SQL Server após 2008.

Escrevemos este documento em resposta a várias consultas recebidas por nossa equipe de suporte sobre a conexão do SQL Server por meio do Easysoft ao Salesforce. No entanto, os exemplos SQL também devem ser úteis para conexões do Linked Server que usam um driver ODBC e um back-end diferentes.

Se você gostaria de contribuir para este documento, envie seu envio por e-mail para .

Cláusula WHERE


Um problema comum relatado para nós é "Uma cláusula WHERE simples leva muito tempo para retornar apenas uma linha". Por exemplo:
selecione Id, FirstName, LastName de SF8.SF.DBO.Contact onde Id='00346000002I95MAAS'

O SQL Server converte a consulta acima e a envia para o driver ODBC do Salesforce:
selecione Id, FirstName, LastName de SF.DBO.Contact

A cláusula WHERE é sempre removida, o que força o driver ODBC a retornar todas as linhas dessa tabela. Em seguida, o SQL Server os filtra localmente para fornecer as linhas necessárias. Não parece importar qual cláusula WHERE você especificou, isso nunca é passado para o driver ODBC.

A solução simples para isso é usar o SQL Server OPENQUERY função em vez disso. Por exemplo:
select * from OPENQUERY(SF8,'select Id, FirstName, LastName from SF.DBO.Contact where Id=''00346000002I95MAAS'' ')

Todo o SQL que você executa dentro do OPENQUERY função é passada diretamente para o driver, incluindo o WHERE cláusula.

Várias junções de tabela


Aqui está uma junção simples de duas tabelas em que ambas as tabelas estão voltando do servidor vinculado.
selecione a.[Nome], BillingStreet, c.[Nome] de SF8.SF.DBO.Conta a, SF8.SF.DBO.Contato c onde a.Id=c.AccountID e a.[Nome] como 'United%'

O SQL Server envia as seguintes consultas ao driver ODBC.
selecione * na conta selecione * no contato

O SQL Server faz isso para obter uma lista de nomes de colunas e tipos de dados. Em seguida, ele envia essas consultas para o driver ODBC.
SELECIONE "Tbl1001",."Id" "Col1042","Tbl1001",."Nome" "Col1044","Tbl1001",."BillingStreet" "Col1046" DE "SF",."DBO",."Conta" "Tbl1001 " ORDER POR "Col1042" ASCSELECT "Tbl1003", "AccountId" "Col1057", "Tbl1003", "Nome" "Col1058" DE "SF", "DBO", "Contato" "Tbl1003" ORDER POR "Col1057" ASC  
Os dados de ambas as consultas são retornados às tabelas locais, então a cláusula WHERE é colocada na tabela Conta e os dados de ambas as tabelas são unidos e retornados.

Novamente o uso de OPENQUERY garante que o SQL que você escreve seja passado diretamente para o driver ODBC, então, em vez disso, no SQL Server você executaria:
selecione * de OPENQUERY(SF8,'selecione a.[Nome], BillingStreet, c.[Nome] de SF.DBO.Conta a, SF.DBO.Contato c onde a.Id=c.AccountID e a. [Nome] como ''United%'' ')

Você precisa de uma pequena modificação, porque o SQL Server não pode lidar com várias colunas com o mesmo "Nome", portanto, você precisa renomear uma dessas colunas. Por exemplo:
selecione * de OPENQUERY(SF8,'selecione a.[Nome], BillingStreet, c.[Nome] como FullName de SF.DBO.Conta a, SF.DBO.Contato c onde a.Id=c.AccountID e a.[Nome] como ''United%'' ')

Isso força o driver ODBC a processar todo o SQL de uma só vez e retornar apenas os resultados necessários.

Tabela Local Anexada a uma Tabela Remota


Neste exemplo, a tabela local foi criada executando.
selecione * em LocalAccount de SF8.SF.DBO.Account

A junção das duas tabelas agora se parece com.
selecione a.[Name], BillingStreet, c.[Name] como FullName de LocalAccount a, SF8.SF.DBO.Contact c onde a.Id=c.AccountID e a.[Name] como 'United%' 

Isso faz com que o SQL Server envie a seguinte consulta três vezes para o driver ODBC.
selecione * em Contato

Em pelo menos uma dessas consultas, o SQL Server solicita todos os dados da tabela. Em seguida, o SQL Server pede:
SELECIONE "Tbl1003".."Nome" "Col1008" DE "SF".."DBO","Contato" "Tbl1003" ONDE ?="Tbl1003",."AccountId"

SQL Server, em seguida, passa para o driver ODBC uma lista de AccountIds da tabela LocalAccount no lugar do "?" parâmetro em que a coluna LocalAccount.[Name] corresponde à cláusula LIKE.

Uma maneira mais rápida em que a tabela ODBC é a segunda tabela na consulta é obter apenas as colunas necessárias da tabela ODBC. Isso pode ser feito usando o OPENQUERY função. Por exemplo:
selecione a.[Nome], BillingStreet, c.[Nome] como FullName de LocalAccount a, openquery(SF8,'select [Name], AccountId de SF.DBO.Contact') c onde a.Id=c. AccountID e a.[Name] como 'United%'

Embora isso ainda obtenha todas as linhas da tabela Contact, ele obtém apenas as colunas necessárias e, portanto, é mais rápido que a consulta padrão.

Outra maneira possível seria usar um cursor e uma tabela temporária. Por exemplo:
Comece a declarar @AccountId como varchar(20) declare @SQL como varchar(1024) -- Crie uma tabela temporária para armazenar as informações da conta. A verificação de Id garante que 0 linhas de dados sejam retornadas selecione * em #LocalContact de openquery(SF8,'select [Name], AccountId de SF.DBO.Contact onde Id=''000000000000000000'' ') -- Configure o cursor declare cursor selcur para selecionar Id distinto de LocalAccount onde [Nome] como 'United%' abrir selcur buscar próximo de selcur em @AccountId enquanto @@FETCH_STATUS=0 Comece selecione @SQL ='inserir em #LocalContact selecione [Nome], ''' +@AccountId+''' de OPENQUERY(SF8,''selecione [Nome] de Contato onde AccountId=''''' + @AccountId + ''''' '')' exec (@SQL) buscar próximo de selcur em @AccountId End close selcur deallocate selcur -- Em seguida, junte suas tabelas e visualize os dados, selecione a.[Name], BillingStreet, c.[Name] como FullName de LocalAccount a, #LocalContact c onde a.Id=c.AccountID e a.[Name] like 'United%' -- Não se esqueça de remover a aba temporária le drop table #LocalContact End

Este método pode ser várias vezes mais rápido que o OPENQUERY método mostrado no exemplo anterior, se a cláusula WHERE que está sendo passada para o driver ODBC do Easysoft usar um índice no Salesforce.

Inserir, atualizar e excluir


Se você estiver executando uma consulta que não é uma consulta SELECT, a melhor maneira de fazer isso é usar o SQL Server EXEC função. Se o seu servidor vinculado não puder usar EXEC , você receberá uma mensagem semelhante a:
O servidor 'SF8' não está configurado para RPC.

Para usar EXEC , clique com o botão direito do mouse no servidor vinculado e escolha propriedades. Na seção "Opções do servidor", defina "RPC Out" como "True". Você pode então usar o EXEC função.

Atualizar


Digamos que você tenha esta instrução no SQL Server:
UPDATE SF8.SF.DBO.Contact SET LastName='James' WHERE Id='00346000002I95MAAS'

O SQL Server envia esse SQL para o driver ODBC.
selecione * de "SF",."DBO","Contato"

Todos os registros são recuperados e o SQL Server envia essa instrução para o driver ODBC.
UPDATE "SF",."DBO","Contato" SET "LastName"=? ONDE "Id" =? E "Sobrenome"=?

O SQL Server está fazendo isso para garantir que o registro não seja alterado entre o momento em que você executou a consulta e o momento em que o UPDATE é executado. Um método mais rápido é usar o SQL Server EXEC função. Por exemplo:
exec ('update SF.DBO.Contact set LastName=''James'' where Id=''00346000002I95MAAS''' ) em SF8 

O SQL Server envia ao driver ODBC a string inteira que você inseriu, de modo que a consulta seja executada sem selecionar a tabela inteira.

Atualizar com parâmetros


Diga que você tem:
Comece a declarar @Id varchar(20)='00346000002I95MAAS' declare @LastName varchar(20)='James' atualização SF8.SF.DBO.Contact set LastName=@LastName onde Id=@IdEnd

Isso funciona exatamente da mesma maneira descrita nas notas de atualização. No entanto, a sintaxe ao usar o EXEC mudanças de função:
Comece a declarar @Id varchar(20)='00346000002I95MAAS' declare @LastName varchar(20)='James' exec ('update SF.DBO.Contact set LastName=? where Id=?', @LastName, @Id ) em SF8End

Onde você tem uma coluna como LastName= você colocou um ? no lugar de @LastName para representar o que você vai passar para o parâmetro. Os parâmetros são listados após a instrução UPDATE na ordem em que precisam ser lidos.

Inserindo um novo registro e obtendo um erro BLOB


Digamos que você esteja tentando executar:
inserir nos valores SF8.SF.DBO.Contact ( FirstName, LastName ) ('Easysoft','Test')

O SQL Server envia isso para o driver ODBC:
selecione * de "SF",."DBO","Contato"

Isso é feito duas vezes. Na primeira vez que isso é executado, o SQL Server verifica se o conjunto de resultados é atualizável. Na segunda vez que isso é enviado, o SQL Server move para um registro vazio após o último registro retornado e tenta fazer um INSERT posicional, o que dá um erro.
O provedor OLE DB "MSDASQL" para o servidor vinculado "SF8" retornou a mensagem "Não há suporte para inserção ou atualização baseada em consulta de valores BLOB.".

Esta mensagem é retornada porque um insert posicional tenta inserir todas as colunas com valores NULL exceto aquelas que você especificou em sua instrução INSERT, e no caso da tabela Contact, existe um BLOB (Long Text Area in Salesforce), que o provedor OLE DB da Microsoft não oferece suporte. O driver ODBC do Easysoft Salesforce suporta a inserção de todos os campos no Salesforce nos quais você tem permissão para inserir dados. Para contornar isso, tudo que você precisa fazer é usar EXEC.
exec ('inserir nos valores SF.DBO.Contact ( FirstName, LastName ) (''Easysoft'',''Test'')') em SF8

Isso apenas envia o INSERT direto para o driver ODBC.

Obter o ID do Salesforce para o último registro que você inseriu


Alguns de nossos clientes nos perguntaram qual é o método mais fácil de obter o ID da linha que acabou de ser inserida. Este exemplo mostra como você pode obter o ID do último registro inserido na tabela "Contato".
Comece a declarar @Id varchar(20)='00346000002I95MAAS' declare @FirstName varchar(20)='Easysoft' declare @LastName varchar(20)='Teste' declare @FindTS varchar(22)=convert(varchar(22) ),GETUTCDATE(),120) declare @SQL como varchar(1024) exec ('insert into SF.DBO.Contact (FirstName, LastName ) values ​​(?, ?)', @FirstName, @LastName ) em SF8 selecione @SQL ='selecione Id de openquery(SF8, ''selecione top 1 c.Id de [User] u, Contact c onde u.Username=CURRENT_USER e c.CreatedDate>={ts '''''+@FindTS+''' ''} e c.CreatedById=u.Id ordem por c.CreatedDate desc'')' exec (@SQL) Fim

Quando um registro é criado no Salesforce, a coluna "CreatedDate" contém um timestamp que é o UTC (Coordinated Universal Time) em que o registro foi criado e não necessariamente sua data/hora atual. O @FindTs string é definida como UTC antes que o INSERT ocorra, então quando o SELECT para obter o Id é chamado, ele está apenas olhando para as linhas inseridas após o @FindTS foi configurado.

Durante o SELECT, o Easysoft CURRENT_USER A função também é usada para limitar as linhas retornadas do Salesforce apenas ao usuário que inseriu os dados.

Atualizando dados do SQL Server quando os dados do Salesforce são alterados


Esta seção mostra como criar uma nova tabela do SQL Server com base na estrutura de uma tabela do Salesforce e atualizar essa tabela quando houver alterações nessa tabela do Salesforce.
criar procedimento SFMakeLocal( @Link varchar(50), @Remote varchar(50), @Local varchar(50), @DropLocal int) as declarar @SQL como nvarchar(max) begin /* Importa os dados para um local table */ /* Defina DropLocal como 1 para eliminar a tabela local se ela existir */ if OBJECT_ID(@Local, 'U') IS NOT NULL begin if (@DropLocal=1) begin set @SQL='DROP TABLE dbo. '+@Local exec ( @SQL) end else RAISERROR(15600,1,1, 'A tabela local já existe') RETURN end set @SQL='select * into dbo.'+@Local+' from OPENQUERY('+@Link+ ',''selecione * de '+@Remote+''')' exec(@SQL) selecione 'Tabela Local:'+@Local+' criada.' end -- @Link Seu servidor vinculado ao SQL Server -- @Remote O nome da tabela no Salesforce -- @Local A tabela local na qual você deseja que os dados sejam armazenados -- @DropLocal Defina como 1 se a tabela existir e você desejar para soltá-lo

Execute o procedimento para copiar a estrutura de registro da tabela do Salesforce para a tabela local e, em seguida, transfira todos os dados do Salesforce. Este comando de exemplo usa a tabela Conta. Esse processo pode demorar um pouco dependendo da quantidade de dados que você tem na tabela do Salesforce.
SFMakeLocal 'SF8','Conta','ContaLocal', 0

Os argumentos são:
Argumento Valor
SF8 O nome do servidor vinculado do SQL Server.
Conta O nome da tabela do Salesforce que você deseja usar para ler a estrutura e os dados.
Conta Local O nome da sua tabela no SQL Server.
0 Esse valor padrão pode ser alterado para 1 se você adicionar mais colunas personalizadas ao Salesforce e desejar descartar a tabela local para criá-la novamente com as novas colunas.

A próxima etapa é criar mais dois procedimentos que atualizarão a tabela local se algum dado for atualizado ou inserido na tabela do Salesforce:
criar o procedimento SFUpdateTable ( @Link varchar(50), @Remote varchar(50), criar o procedimento SFUpdateTable @Link varchar(50), @Remote varchar(50), @LocalTable varchar(50) as begin -- Atualiza o dados em uma tabela local com base nas alterações no Salesforce. declare @TempDef como varchar(50)='##EasyTMP_' declare @TempName como varchar(50) declare @TempNumber como decimal declare @CTS como datetime=current_timestamp declare @TTLimit int =100 declare @MaxCreated como datetime declare @MaxModified como datetime declare @SQL como nvarchar(max) declare @RC as int -- O primeiro passo é criar uma tabela temporária global. set @TempNumber=datepart(yyyy,@CTS)*10000000000 +datepart(mm,@CTS)*100000000+datepart(dd,@CTS)*1000000+datepart(hh,@CTS)*10000+datepart(mi,@CTS)*100+datepart(ss,@CTS) set @ TempName=@TempDef+cast(@TempNumber as varchar(14)) while OBJECT_ID(@TempName, 'U') IS NOT NULL begin RAISERROR (15600,1,1, 'Nome temporário já em uso.') RETURN end set @SQL='select * into '+@TempName+' de '+@ LocalTable+' onde 1=0' cria a tabela #LocalDates ( ColName varchar(20), DTS datetime) set @sql='insert into #LocalDates selecione ''Created'', max(CreatedDate) de '+@LocalTable exec (@sql ) set @sql='inserir em #LocalDates selecione ''Modified'', max(LastModifiedDate) de '+@LocalTable exec (@sql) selecione @MaxCreated=DTS de #LocalDates onde ColName='Created' selecione @MaxModified=DTS from #LocalDates onde ColName='Modified' drop table #LocalDates set @SQL='select * into '+@TempName+' from openquery('+@Link+',''select * from '+@Remote+' where CreatedDate>{ts '''''+convert(varchar(22),@MaxCreated,120)+'''''}'')' exec(@SQL) exec SAppe ndFromTemp @LocalTable, @TempName set @SQL='soltar tabela '+@TempName exec (@SQL) set @SQL='select * em '+@TempName+' de openquery('+@Link+',''select * from ' +@Remote+' onde LastModifiedDate>{ts'''''+convert(varchar(22),@MaxModified,120)+'''''} e CreatedDate<={ts'''''+convert(varchar( 22),@MaxCreated,120)+'''''}'')' exec (@SQL) exec SFAppendFromTemp @LocalTable, @TempName set @SQL='drop table '+@TempName exec (@SQL) end create procedure SFAppendFromTemp(@Local varchar(50), @TempName varchar(50)) as begin /* Usa a tabela temporária para importar os dados para a tabela local certificando-se de que quaisquer duplicatas sejam removidas primeiro */ declare @Columns nvarchar(max) declare @ ColName varchar(50) declare @SQL nvarchar(max) set @sql='delete from '+@Local+' onde Id in (selecione Id de '+@TempName+')' exec (@SQL) set @Columns='' declare o cursor col_cursor para selecionar syscolumns.name de sysobjects inner join syscolumns em sysobjects.id =syscolumns.id onde sysobjects.xtype ='u' e sysobjects.name =@Local abra col_cursor buscar próximo de col_cursor em @ ColName while @@FETCH_STATUS=0 Iniciar conjunto @Columns=@Columns+'['+@ColName+']' buscar próximo de col_cursor para @ColName if (@@FETCH_STATUS=0) set @Columns=@Columns+', ' Final fechar col_cursor desalocar col_cursor set @sql='insert into '+@Local+' (' +@Columns+') select '+@Columns+' from '+@TempName exec (@sql) end -- Dois procedimentos são usados ​​para obter os dados de um mesa remota. 1) SFUpdateTable, que -- copia os dados em uma tabela temporária. 2) SFAppendFromTemp, que anexa -- os dados da tabela temporária na tabela local. -- @Link O nome do servidor vinculado ao SQL Server -- @Remote O nome da tabela no Salesforce -- @Local A tabela local na qual você deseja que os dados sejam armazenados -- @TempName Um nome de uma tabela que pode ser usada para armazenar dados temporários. Não -- use um nome de tabela temporária real, como #temp, isso não funcionará.

Para testar isso, execute:
SFUpdateTable 'SF8','Conta','LocalAccount'

Este exemplo pode ser usado com qualquer tabela do Salesforce à qual um usuário tenha acesso.

Validação de esquema lento


Nas propriedades do servidor vinculado do SQL Server, na seção "Opções do servidor", há uma opção para "Validação de esquema lento". Por padrão, isso é definido como FALSE, o que faz com que o SQL Server envie instruções SELECT duas vezes. Na primeira vez que a consulta é enviada, o SQL Server usa os detalhes passados ​​de volta para criar metadados sobre seu conjunto de resultados. Em seguida, a consulta é enviada novamente. Esta é uma sobrecarga bastante cara, então a Easysoft recomendaria que você defina "Lazy Schema Validation" como TRUE, o que significa que apenas uma consulta é enviada, recuperando metadados e conjunto de resultados de uma só vez. Isso também economiza o número de chamadas de API do Salesforce que estão sendo feitas.

Limitações do OLEDB da Microsoft para Provedor ODBC


Detalhes sobre as limitações do OLEDB for ODBC Provider podem ser encontrados em:

https://msdn.microsoft.com/en-us/library/ms719628(v=vs.85).aspx

Como encontro registros com um feed de linha (nova linha) no endereço de cobrança?


Usando algumas das funções internas do driver Easysoft, você pode encontrar facilmente registros onde o endereço de cobrança tem uma linha de alimentação dentro do registro. Por exemplo:

select * from openquery(sf8,'select Id, Name, {fn POSITION({fn CHAR(10)} IN BillingStreet)} LinePos from Account where {fn POSITION({fn CHAR(10)} IN BillingStreet)} >0')

POSITION(x) Esta função procura a posição de x dentro da coluna especificada.

CHAR(X) Esta função retorna o caractere com o valor ASCII de x .

Mais informações sobre as funções disponíveis em nosso driver ODBC Salesforce podem ser encontradas aqui

Posso ver quais tabelas estão disponíveis através do software Easysoft?


Para obter uma lista de tabelas que você pode acessar, execute:

select * from openquery(SF8,'select TABLE_NAME from INFO_SCHEMA.TABLES')

Posso ver quais colunas estão disponíveis por meio do software Easysoft?


Você pode obter uma lista de colunas que estão na tabela executando:

select * from openquery(SF8,'select * from INFO_SCHEMA.COLUMNS where TABLE_NAME=''Account'' ')

Usando este método, você pode obter apenas uma lista das colunas que pertencem à tabela especificada na cláusula TABLE_NAME WHERE. Se você quiser ver uma lista completa de colunas para todas as tabelas, execute:
começar a declarar @Table nvarchar(max) declarar table_cursor cursor para selecionar TABLE_NAME de openquery(SF8,'selecionar TABLE_NAME de INFO_SCHEMA.TABLES') open table_cursor buscar próximo de table_cursor em @Table while @@FETCH_STATUS=0 Begin exec (' selecione * de INFO_SCHEMA.COLUMNS onde TABLE_NAME=?', @Table) em SF8 busca próximo de table_cursor em @Table End close table_cursor desaloque table_cursorend

Posso criar programaticamente um servidor vinculado?


Sim. Existem muitos exemplos disso na web, por exemplo:

http://www.sqlservercentral.com/articles/Linked+Servers/142270/?utm_source=SSC