Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Conseils d'utilisation de SQL Server avec Salesforce

Table des matières

  1. Présentation
  2. Clause WHERE
  3. Jointures de tables multiples
  4. Table locale attachée à une table distante
  5. Insérer, mettre à jour et supprimer
  6. Mettre à jour
  7. Mettre à jour avec les paramètres
  8. Insérer un nouvel enregistrement et obtenir une erreur BLOB
  9. Obtenir l'identifiant Salesforce pour le dernier enregistrement que vous avez inséré
  10. Mettre à jour les données SQL Server lorsque les données Salesforce changent
  11. Validation de schéma paresseux
  12. Limites du fournisseur OLEDB pour ODBC de Microsoft
  13. Comment puis-je trouver des enregistrements avec un saut de ligne (nouvelle ligne) dans l'adresse de facturation ?
  14. Puis-je voir quels tableaux sont disponibles via le logiciel Easysoft ?
  15. Puis-je voir quelles colonnes sont disponibles via le logiciel Easysoft ?
  16. Puis-je créer un serveur lié par programmation ?

Aperçu

Ce document donne quelques conseils sur l'utilisation de SQL Server avec Salesforce. Les composants utilisés pour connecter SQL Server à Salesforce sont un serveur lié SQL Server et le pilote ODBC Easysoft Salesforce. La manière dont vous connectez SQL Server à Salesforce est décrite dans cet article. Pour les exemples de ce document, le nom du serveur lié (que vous référencez dans vos commandes SQL) utilisé est SF8.

Tout le SQL de ce document a été testé avec SQL Server 2017 et les versions 2.0.0 à 2.0.7 du pilote ODBC Easysoft Salesforce.

Les fonctions SQL Server OPENQUERY et EXEC (EXECUTE ) ont été introduits dans SQL Server 2008 et ces fonctions sont compatibles avec toutes les versions de SQL Server après 2008.

Nous avons rédigé ce document en réponse au nombre de requêtes reçues par notre équipe d'assistance concernant la connexion de SQL Server via Easysoft à Salesforce. Toutefois, les exemples SQL doivent également être utiles pour les connexions de serveur lié qui utilisent un pilote et un backend ODBC différents.

Si vous souhaitez contribuer à ce document, veuillez envoyer votre soumission par e-mail à .

Clause WHERE

Un problème courant qui nous est signalé est "Une simple clause WHERE prend beaucoup de temps pour ne renvoyer qu'une seule ligne". Par exemple :

sélectionnez Id, FirstName, LastName from SF8.SF.DBO.Contact where Id='00346000002I95MAAS'

SQL Server convertit la requête ci-dessus et l'envoie au pilote ODBC Salesforce :

sélectionnez Id, FirstName, LastName de SF.DBO.Contact

La clause WHERE est toujours supprimée, ce qui oblige le pilote ODBC à renvoyer toutes les lignes de cette table. Ensuite, SQL Server les filtre localement pour vous donner la ou les lignes requises. Peu importe la clause WHERE que vous avez spécifiée, elle n'est jamais transmise au pilote ODBC.

La solution simple à cela est d'utiliser SQL Server OPENQUERY fonction à la place. Par exemple :

select * from OPENQUERY(SF8,'select Id, FirstName, LastName from SF.DBO.Contact where Id=''00346000002I95MAAS'' ')

Tout le SQL que vous exécutez dans le OPENQUERY la fonction est transmise directement au pilote, y compris le WHERE clause.

Jointures de tables multiples

Voici une simple jointure de deux tables où les deux tables reviennent du serveur lié.

select a.[Name], BillingStreet, c.[Name] from SF8.SF.DBO.Account a, SF8.SF.DBO.Contact c where a.Id=c.AccountID and a.[Name] like 'Uni%'

SQL Server envoie les requêtes suivantes au pilote ODBC.

sélectionner * depuis le comptesélectionner * depuis le contact

SQL Server procède ainsi pour obtenir une liste des noms de colonnes et des types de données. Il envoie ensuite ces requêtes au pilote ODBC.

SELECT "Tbl1001"."Id" "Col1042","Tbl1001"."Name" "Col1044","Tbl1001"."BillingStreet" "Col1046" FROM "SF".."DBO"."Compte" "Tbl1001" " ORDER BY "Col1042" ASCSELECT "Tbl1003"."AccountId" "Col1057","Tbl1003"."Name" "Col1058" FROM "SF"."DBO"."Contact" "Tbl1003" ORDER BY "Col1057" ASC 

Les données des deux requêtes sont renvoyées aux tables locales, puis la clause WHERE est placée sur la table Account et les données des deux tables sont jointes et renvoyées.

Encore une fois l'utilisation de OPENQUERY garantit que le SQL que vous écrivez est transmis directement au pilote ODBC, donc, à la place, dans SQL Server, vous exécuteriez :

select * from OPENQUERY(SF8,'select a.[Name], BillingStreet, c.[Name] from SF.DBO.Account a, SF.DBO.Contact c where a.Id=c.AccountID and a. [Nom] comme ''United%'' ')

Vous avez besoin d'une légère modification, car SQL Server ne peut pas gérer plusieurs colonnes avec le même "Nom", vous devez donc renommer l'une de ces colonnes. Par exemple :

select * from OPENQUERY(SF8,'select a.[Name], BillingStreet, c.[Name] as FullName from SF.DBO.Account a, SF.DBO.Contact c where a.Id=c.AccountID and a.[Nom] comme ''United%'' ')

Cela oblige le pilote ODBC à traiter l'intégralité du SQL en une seule fois et à ne renvoyer que les résultats requis.

Table locale attachée à une table distante

Dans cet exemple, la table locale a été créée en exécutant.

sélectionnez * dans LocalAccount à partir de SF8.SF.DBO.Account

La jointure des deux tables ressemble maintenant à.

select a.[Name], BillingStreet, c.[Name] as FullName from LocalAccount a, SF8.SF.DBO.Contact c where a.Id=c.AccountID and a.[Name] like 'United%' 

Cela oblige SQL Server à envoyer trois fois la requête suivante au pilote ODBC.

sélectionnez * dans Contact

Dans au moins une de ces requêtes, SQL Server demande toutes les données de la table. Ensuite, SQL Server demande :

SELECT "Tbl1003"."Name" "Col1008" FROM "SF".."DBO"."Contact" "Tbl1003" WHERE ?="Tbl1003"."AccountId"

SQL Server transmet ensuite au pilote ODBC une liste d'AccountIds de la table LocalAccount à la place du "?" paramètre où la colonne LocalAccount.[Name] correspond à la clause LIKE.

Un moyen plus rapide lorsque la table ODBC est la deuxième table de la requête consiste à obtenir uniquement les colonnes dont vous avez besoin à partir de la table ODBC. Cela peut être fait en utilisant le OPENQUERY une fonction. Par exemple :

select a.[Name], BillingStreet, c.[Name] as FullName from LocalAccount a, openquery(SF8,'select [Name], AccountId from SF.DBO.Contact') c where a.Id=c. AccountID et a.[Name] comme 'United%'

Bien que cela obtienne toujours toutes les lignes de la table Contact, il n'obtient que les colonnes nécessaires et est donc plus rapide que la requête standard.

Une autre manière possible serait d'utiliser un curseur et une table temporaire. Par exemple :

Commencez à déclarer @AccountId en tant que varchar(20) déclarer @SQL en tant que varchar(1024) -- Créez une table temporaire pour stocker les informations de compte. La vérification de l'ID garantit qu'aucune ligne de données n'est renvoyée select * into #LocalContact from openquery(SF8,'select [Name], AccountId from SF.DBO.Contact where Id=''000000000000000000'' ') -- Configurez le curseur declare curseur selcur pour sélectionner l'identifiant distinct de LocalAccount où [Nom] comme 'United%' ouvre selcur récupérer ensuite de selcur dans @AccountId tandis que @@FETCH_STATUS=0 Begin select @SQL ='insert into #LocalContact select [Name], ''' +@AccountId+''' from OPENQUERY(SF8,''select [Name] from Contact where AccountId=''''' + @AccountId + ''''' '')' exec (@SQL) récupère ensuite de selcur dans @AccountId End close selcur deallocate selcur -- Ensuite, joignez vos tables et affichez les données sélectionnez a.[Name], BillingStreet, c.[Name] as FullName from LocalAccount a, #LocalContact c where a.Id=c.AccountID and a.[Name] like 'United%' -- N'oubliez pas de supprimer l'onglet temporaire le drop table #LocalContact End

Cette méthode peut être plusieurs fois plus rapide que le OPENQUERY illustrée dans l'exemple précédent, si la clause WHERE transmise au pilote Easysoft ODBC utilise un index dans Salesforce.

Insérer, mettre à jour et supprimer

Si vous exécutez une requête qui n'est pas une requête SELECT, la meilleure façon de le faire est d'utiliser SQL Server EXEC une fonction. Si votre serveur lié ne peut pas utiliser EXEC , vous obtiendrez un message semblable à :

Le serveur 'SF8' n'est pas configuré pour RPC.

Pour utiliser EXEC , faites un clic droit sur votre serveur lié et choisissez propriétés. Dans la section "Options du serveur", définissez "RPC Out" sur "True". Vous pouvez ensuite utiliser le EXEC fonction.

Mettre à jour

Supposons que vous ayez cette instruction dans SQL Server :

UPDATE SF8.SF.DBO.Contact SET LastName='James' WHERE Id='00346000002I95MAAS'

SQL Server envoie ce SQL au pilote ODBC.

sélectionnez * dans "SF".."DBO"."Contact"

Tous les enregistrements sont récupérés et SQL Server envoie ensuite cette instruction au pilote ODBC.

UPDATE "SF"."DBO"."Contact" SET "LastName"= ? OÙ "Identifiant"= ? AND "LastName"=?

SQL Server le fait pour s'assurer que l'enregistrement ne soit pas modifié entre le moment où vous avez exécuté la requête et le moment où la mise à jour est exécutée. Une méthode plus rapide consiste à utiliser SQL Server EXEC une fonction. Par exemple :

exec ('update SF.DBO.Contact set LastName=''James'' where Id=''00346000002I95MAAS''' ) à SF8 

SQL Server envoie au pilote ODBC la chaîne entière que vous avez entrée, de sorte que la requête est exécutée sans sélectionner la table entière.

Mettre à jour avec les paramètres

Supposons que vous ayez :

Commencez à déclarer @Id varchar(20)='00346000002I95MAAS' declare @LastName varchar(20)='James' update SF8.SF.DBO.Contact set LastName=@LastName where Id=@IdEnd

Cela fonctionne exactement de la même manière que celle décrite dans les notes de mise à jour. Cependant, la syntaxe lors de l'utilisation de EXEC changement de fonction :

Commencez declare @Id varchar(20)='00346000002I95MAAS' declare @LastName varchar(20)='James' exec ('update SF.DBO.Contact set LastName=? where Id=?', @LastName, @Id ) à SF8End

Où vous avez une colonne telle que LastName= tu mets un ? à la place de @LastName pour représenter ce que vous allez passer dans le paramètre. Les paramètres sont ensuite répertoriés après l'instruction UPDATE dans l'ordre dans lequel ils doivent être lus.

Insérer un nouvel enregistrement et obtenir une erreur BLOB

Supposons que vous essayez de courir :

insérer dans les valeurs SF8.SF.DBO.Contact ( FirstName, LastName ) ('Easysoft','Test')

SQL Server envoie ceci au pilote ODBC :

sélectionnez * dans "SF".."DBO"."Contact"

Ceci est fait deux fois. La première fois qu'il est exécuté, SQL Server vérifie si le jeu de résultats peut être mis à jour. La deuxième fois que cela est envoyé, SQL Server se déplace vers un enregistrement vide après le dernier enregistrement retourné et essaie de faire un INSERT positionnel, ce qui donne une erreur.

Le fournisseur OLE DB "MSDASQL" pour le serveur lié "SF8" a renvoyé le message "L'insertion ou la mise à jour basée sur une requête des valeurs BLOB n'est pas prise en charge.".

Ce message est renvoyé car une insertion positionnelle tente d'insérer toutes les colonnes avec des valeurs NULL à l'exception de celles que vous avez spécifiées dans votre instruction INSERT, et dans le cas de la table Contact, il existe un BLOB (Long Text Area in Salesforce ), que le fournisseur OLE DB de Microsoft ne prend pas en charge. Le pilote ODBC Easysoft Salesforce prend en charge l'insertion de tous les champs dans Salesforce où vous êtes autorisé à insérer des données. Pour contourner ce problème, il vous suffit d'utiliser EXEC.

exec ('insert into SF.DBO.Contact ( FirstName, LastName ) values ​​(''Easysoft'',''Test'')') à SF8

Cela envoie simplement l'INSERT directement au pilote ODBC.

Obtenir l'identifiant Salesforce pour le dernier enregistrement que vous avez inséré

Quelques-uns de nos clients nous ont demandé quelle est la méthode la plus simple pour obtenir l'ID de la ligne qui vient d'être insérée. Cet exemple montre comment vous pouvez obtenir l'ID du dernier enregistrement que vous avez inséré dans la table "Contact".

Begin declare @Id varchar(20)='00346000002I95MAAS' declare @FirstName varchar(20)='Easysoft' declare @LastName varchar(20)='Test' declare @FindTS varchar(22)=convert(varchar(22) ),GETUTCDATE(),120) déclarer @SQL comme varchar(1024) exec ('insert into SF.DBO.Contact (FirstName, LastName) values ​​(?, ?)', @FirstName, @LastName ) à SF8 sélectionner @SQL ='select Id from openquery(SF8, ''select top 1 c.Id from [User] u, Contact c where u.Username=CURRENT_USER and c.CreatedDate>={ts '''''+@FindTS+''' ''} et c.CreatedById=u.Id trier par c.CreatedDate desc'')' exec (@SQL) End

Lorsqu'un enregistrement est créé dans Salesforce, la colonne "CreatedDate" contient un horodatage qui est l'UTC (Coordinated Universal Time) auquel l'enregistrement a été créé et pas nécessairement votre date/heure actuelle. Les @FindTs la chaîne est définie sur l'UTC avant que l'INSERT n'ait lieu, donc lorsque le SELECT pour obtenir l'ID est appelé, il ne regarde que les lignes insérées après le @FindTS a été défini.

Lors du SELECT, l'Easysoft CURRENT_USER est également utilisée pour limiter les lignes renvoyées par Salesforce uniquement à l'utilisateur qui a inséré les données.

Mettre à jour les données SQL Server lorsque les données Salesforce changent

Cette section vous montre comment créer une nouvelle table SQL Server basée sur la structure d'une table Salesforce et mettre à jour cette table lorsque des modifications sont apportées à cette table Salesforce.

create procedure SFMakeLocal( @Link varchar(50), @Remote varchar(50), @Local varchar(50), @DropLocal int) as declare @SQL as nvarchar(max) begin /* Importe les données dans un local table */ /* Définissez DropLocal sur 1 pour supprimer la table locale si elle existe */ si OBJECT_ID(@Local, 'U') N'EST PAS NULL begin if (@DropLocal=1) begin set @SQL='DROP TABLE dbo. '+@Local exec ( @SQL) end else RAISERROR(15600,1,1, 'La table locale existe déjà') RETURN end set @SQL='select * into dbo.'+@Local+' from OPENQUERY('+@Link+ ',''select * from '+@Remote+''')' exec(@SQL) select 'Local Table :'+@Local+' créé.' end -- @Link Votre serveur lié SQL Server -- @Remote Le nom de la table dans Salesforce -- @Local La table locale dans laquelle vous voulez que les données soient stockées -- @DropLocal Définir sur 1 si la table existe et que vous voulez le laisser tomber

Exécutez la procédure pour copier la structure d'enregistrement de la table Salesforce dans la table locale, puis transférez toutes les données Salesforce. Cet exemple de commande utilise la table Account. Ce processus peut prendre un certain temps en fonction de la quantité de données dont vous disposez dans le tableau Salesforce.

SFMakeLocal 'SF8','Compte','CompteLocal', 0

Les arguments sont :

Argument Valeur
SF8 Le nom du serveur lié SQL Server.
Compte Le nom de la table Salesforce que vous souhaitez utiliser pour lire la structure et les données depuis.
Compte local Le nom de votre table dans SQL Server.
0 Cette valeur par défaut peut être changée en 1 si vous ajoutez plus de colonnes personnalisées dans Salesforce et que vous souhaitez supprimer la table locale pour la recréer avec les nouvelles colonnes.

L'étape suivante consiste à créer deux autres procédures qui mettront à jour la table locale si des données sont mises à jour ou insérées dans la table Salesforce :

create procedure SFUpdateTable ( @Link varchar(50), @Remote varchar(50), create procedure SFUpdateTable @Link varchar(50), @Remote varchar(50), @LocalTable varchar(50) as begin -- Met à jour le données dans une table locale en fonction des modifications apportées à Salesforce. declare @TempDef as varchar(50)='##EasyTMP_' declare @TempName as varchar(50) declare @TempNumber as decimal declare @CTS as datetime=current_timestamp declare @TTLimit int =100 déclarer @MaxCreated comme datetime déclarer @MaxModified comme datetime déclarer @SQL comme nvarchar(max) déclarer @RC comme int -- La première étape consiste à créer une table temporaire globale. 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)) tandis que OBJECT_ID(@TempName, 'U') N'EST PAS NULL begin RAISERROR (15600,1,1, 'Temp nom déjà utilisé.') RETURN end set @SQL='select * into '+@TempName+' from '+@ LocalTable+' où 1=0' crée une table #LocalDates ( ColName varchar(20), DTS datetime) set @sql='insert into #LocalDates select ''Created'', max(CreatedDate) from '+@LocalTable exec (@sql ) définissez @sql='insert into #LocalDates select ''Modified'', max(LastModifiedDate) from '+@LocalTable exec (@sql) select @MaxCreated=DTS from #LocalDates where ColName='Created' select @MaxModified=DTS from #LocalDates where 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 SFAppe ndFromTemp @LocalTable, @TempName set @SQL='drop table '+@TempName exec (@SQL) set @SQL='select * into '+@TempName+' from openquery('+@Link+',''select * from ' +@Remote+' où LastModifiedDate>{ts'''''+convert(varchar(22),@MaxModified,120)+'''''} et 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 /* Utilise la table temporaire pour importer les données dans la table locale en s'assurant que tous les doublons sont d'abord supprimés */ declare @Columns nvarchar(max) declare @ ColName varchar(50) declare @SQL nvarchar(max) set @sql='delete from '+@Local+' where Id in ( select Id from '+@TempName+')' exec (@SQL) set @Columns='' déclarer le curseur col_cursor pour sélectionner syscolumns.name à partir de sysobjects jointure interne syscolumns sur sysobjects.id =syscolumns.id où sysobjects.xtype ='u' et sysobjects.name =@Local open col_cursor récupérer ensuite de col_cursor dans @ ColName while @@FETCH_STATUS=0 Begin set @Columns=@Columns+'['+@ColName+']' récupérer le suivant de col_cursor dans @ColName if (@@FETCH_STATUS=0) set @Columns=@Columns+', ' End close col_cursor deallocate col_cursor set @sql='insert into '+@Local+' (' +@Columns+') select '+@Columns+' from '+@TempName exec (@sql) end -- Deux procédures sont utilisées pour obtenir les données d'un tableau distant. 1) SFUpdateTable, qui -- copie les données dans une table temporaire. 2) SFAppendFromTemp, qui ajoute -- les données de la table temporaire dans la table locale. -- @Link Le nom de votre serveur lié SQL Server -- @Remote Le nom de la table dans Salesforce -- @Local La table locale dans laquelle vous souhaitez stocker les données -- @TempName Le nom d'une table qui peut être utilisée pour stocker temporairement des données. Ne -- utilisez pas un vrai nom de table temporaire tel que #temp, cela ne fonctionnera pas.

Pour tester cela, exécutez :

SFUpdateTable 'SF8','Account','LocalAccount'

Cet exemple peut être utilisé avec n'importe quelle table Salesforce à laquelle un utilisateur a accès.

Validation de schéma paresseux

Dans les propriétés de votre serveur lié SQL Server, sous la section "Options du serveur", se trouve une option pour "Lazy Schema Validation". Par défaut, cette valeur est définie sur FALSE, ce qui oblige SQL Server à envoyer deux fois des instructions SELECT. La première fois que la requête est envoyée, SQL Server utilise les détails renvoyés pour créer des métadonnées sur votre jeu de résultats. Ensuite, la requête est à nouveau envoyée. C'est une surcharge assez coûteuse, donc Easysoft vous recommande de définir "Lazy Schema Validation" sur TRUE, ce qui signifie qu'une seule requête est envoyée, récupérant à la fois les métadonnées et le jeu de résultats en une seule fois. Cela permet également d'économiser sur le nombre d'appels d'API Salesforce effectués.

Limitations du fournisseur OLEDB de Microsoft pour ODBC

Vous trouverez des détails sur les limites du fournisseur OLEDB pour ODBC à :

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

Comment puis-je trouver des enregistrements avec un saut de ligne (nouvelle ligne) dans l'adresse de facturation ?

En utilisant certaines des fonctions internes du pilote Easysoft, vous pouvez facilement trouver des enregistrements où l'adresse de facturation a un saut de ligne dans l'enregistrement. Par exemple :

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) Cette fonction recherche la position de x dans la colonne spécifiée.

CHAR(X) Cette fonction renvoie le caractère avec la valeur ASCII de x .

Vous trouverez plus d'informations sur les fonctions disponibles dans notre pilote ODBC Salesforce ici

Puis-je voir quelles tables sont disponibles via le logiciel Easysoft ?

Pour obtenir une liste des tables auxquelles vous pouvez accéder, exécutez :

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

Puis-je voir quelles colonnes sont disponibles via le logiciel Easysoft ?

Vous pouvez obtenir une liste des colonnes qui sont dans le tableau en exécutant :

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

En utilisant cette méthode, vous ne pouvez obtenir qu'une liste des colonnes qui appartiennent à la table que vous spécifiez dans la clause TABLE_NAME WHERE. Si vous souhaitez voir une liste complète des colonnes pour toutes les tables, exécutez :

begin declare @Table nvarchar(max) declare table_cursorcursor for select TABLE_NAME from openquery(SF8,'select TABLE_NAME from INFO_SCHEMA.TABLES') open table_cursor fetch next from table_cursor into @Table while @@FETCH_STATUS=0 Begin exec (' select * from INFO_SCHEMA.COLUMNS where TABLE_NAME=?', @Table) at SF8 extrayez ensuite de table_cursor dans @Table End close table_cursor deallocate table_cursorend

Puis-je créer par programmation un serveur lié ?

Oui. Il existe de nombreux exemples de cela sur le Web, par exemple :

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