sql >> Database teknologi >  >> RDS >> Sqlserver

Tips til brug af SQL Server med Salesforce

Indholdsfortegnelse

  1. Oversigt
  2. WHERE-klausul
  3. Flere tabelforbindelser
  4. Lokalt bord knyttet til et eksternt bord
  5. Indsæt, opdater og slet
  6. Opdater
  7. Opdater med parametre
  8. Indsættelse af en ny post og få en BLOB-fejl
  9. Hent Salesforce-id'et for den sidste post, du indsatte
  10. Opdatering af SQL Server-data, når Salesforce-data ændres
  11. Dovne skemavalidering
  12. Begrænsninger for Microsofts OLEDB for ODBC-udbyder
  13. Hvordan finder jeg poster med et linjefeed (ny linje) i faktureringsadressen?
  14. Kan jeg se, hvilke tabeller der er tilgængelige via Easysoft-softwaren?
  15. Kan jeg se, hvilke kolonner der er tilgængelige via Easysoft-softwaren?
  16. Kan jeg oprette en linket server programmæssigt?

Oversigt

Dette dokument giver nogle tips om brug af SQL Server med Salesforce. Komponenterne, der bruges til at forbinde SQL Server til Salesforce, er en SQL Server Linked Server og Easysoft Salesforce ODBC Driver. Hvordan du forbinder SQL Server til Salesforce er beskrevet i denne artikel. For eksemplerne i dette dokument er det linkede servernavn (som du refererer til i dine SQL-kommandoer) SF8.

Al SQL i dette dokument blev testet mod SQL Server 2017 og Easysoft Salesforce ODBC-driverversionerne 2.0.0 til 2.0.7.

SQL Server-funktionerne OPENQUERY og EXEC (EXECUTE ) blev introduceret i SQL Server 2008, og disse funktioner er kompatible med alle versioner af SQL Server efter 2008.

Vi har skrevet dette dokument som svar på antallet af forespørgsler modtaget af vores supportteam vedrørende tilslutning af SQL Server via Easysoft til Salesforce. SQL-eksemplerne skulle dog også være nyttige for Linked Server-forbindelser, der bruger en anden ODBC-driver og backend.

Hvis du gerne vil bidrage til dette dokument, bedes du sende en e-mail til .

WHERE-klausul

Et almindeligt problem rapporteret til os er "En simpel WHERE-klausul tager lang tid at returnere kun én række". For eksempel:

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

SQL Server konverterer ovenstående forespørgsel og sender denne til Salesforce ODBC-driveren:

select Id, FirstName, LastName from SF.DBO.Contact

WHERE-sætningen fjernes altid, hvilket tvinger ODBC-driveren til at returnere alle rækkerne for den tabel. Derefter filtrerer SQL Server dem lokalt for at give dig den eller de rækker, der kræves. Det ser ikke ud til at være lige meget, hvilken WHERE-klausul du har angivet, dette videregives aldrig til ODBC-driveren.

Den enkle løsning på dette er at bruge SQL Server OPENQUERY funktion i stedet for. For eksempel:

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

Al den SQL du kører inde i OPENQUERY funktionen sendes direkte til driveren, inklusive WHERE klausul.

Flere tabeltilslutninger

Her er en simpel joinforbindelse med to tabeller, hvor begge tabeller kommer tilbage fra den linkede server.

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 'United%'

SQL Server sender følgende forespørgsler til ODBC-driveren.

select * from Account
select * from Contact

SQL Server gør dette for at få en liste over kolonnenavne og datatyper. Det fortsætter derefter med at sende disse forespørgsler til ODBC-driveren.

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

Dataene fra begge forespørgsler returneres til lokale tabeller, hvorefter WHERE-sætningen placeres på kontotabellen, og dataene fra begge tabeller sammenføjes og returneres.

Igen brugen af ​​OPENQUERY sikrer, at den SQL, du skriver, sendes direkte til ODBC-driveren, så du i stedet for i SQL Server ville køre:

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.[Name] like ''United%'' ')

Du har brug for en lille ændring, fordi SQL Server ikke kan håndtere flere kolonner med det samme "navn", så du skal omdøbe en af ​​disse kolonner. For eksempel:

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.[Name] like ''United%'' ')

Dette tvinger ODBC-driveren til at behandle hele SQL'en på én gang og kun returnere de nødvendige resultater.

Lokalt bord knyttet til et eksternt bord

I dette eksempel blev den lokale tabel oprettet ved at køre.

select * into LocalAccount from SF8.SF.DBO.Account

Sammenføjningen af ​​de to tabeller ser nu ud.

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%'

Dette får SQL Server til at sende følgende forespørgsel tre gange til ODBC-driveren.

select * from Contact

I mindst én af disse forespørgsler beder SQL Server om alle dataene i tabellen. Så fortsætter SQL Server med at bede om:

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

SQL Server sender derefter en liste over AccountId'er fra LocalAccount-tabellen til ODBC-driveren i stedet for "?" parameter, hvor kolonnen LocalAccount.[Name] matcher LIKE-udtrykket.

En hurtigere måde, hvor ODBC-tabellen er den anden tabel i forespørgslen, er kun at hente de kolonner, du har brug for, fra ODBC-tabellen. Dette kan gøres ved at bruge OPENQUERY fungere. For eksempel:

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 and a.[Name] like 'United%'

Selvom dette stadig henter alle rækkerne fra kontakttabellen, får det kun de nødvendige kolonner og er derfor hurtigere end standardforespørgslen.

En anden mulig måde ville være at bruge en markør og en midlertidig tabel. For eksempel:

Begin
      declare @AccountId as varchar(20)
      declare @SQL as varchar(1024)

      -- Create a temporary table to store the Account information. The Id check ensures 0 rows of data are returned
      select * into #LocalContact from openquery(SF8,'select [Name], AccountId from SF.DBO.Contact where Id=''000000000000000000'' ')
      
      -- Set up the cursor      
      declare selcur cursor for
            select distinct Id from LocalAccount where [Name] like 'United%'
      
      	open selcur
      	fetch next from selcur into @AccountId
      	while @@FETCH_STATUS=0
      	Begin
      		select @SQL ='insert into #LocalContact select [Name], '''+@AccountId+''' from OPENQUERY(SF8,''select [Name] from Contact where AccountId=''''' + @AccountId + ''''' '')'
      		exec (@SQL)
      		
      		fetch next from selcur into @AccountId
      	End
      	close selcur
      	deallocate selcur
	
      	-- Next, join your tables and view the data      	
      	select a.[Name], BillingStreet, c.[Name] as FullName from LocalAccount a, #LocalContact c where a.Id=c.AccountID and a.[Name] like 'United%'
      
      	-- Don't forget to remove the temporary table
      	drop table #LocalContact
      
      End

Denne metode kan være flere gange hurtigere end OPENQUERY metode vist i det foregående eksempel, hvis WHERE-sætningen, der overføres til Easysoft ODBC-driveren, bruger et indeks i Salesforce.

Indsæt, opdater og slet

Hvis du kører en forespørgsel, der ikke er en SELECT-forespørgsel, er den bedste måde at gøre dette på at bruge SQL Server EXEC fungere. Hvis din sammenkædede server ikke kan bruge EXEC , vil du få en besked, der ligner:

Server 'SF8' is not configured for RPC.

For at bruge EXEC , højreklik på din linkede server og vælg egenskaber. I afsnittet "Serverindstillinger" skal du indstille "RPC Out" til "True". Du kan derefter bruge EXEC funktion.

Opdatering

Lad os sige, at du har denne sætning i SQL Server:

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

SQL Server sender denne SQL til ODBC-driveren.

select * from "SF"."DBO"."Contact"

Alle posterne hentes, og SQL Server sender derefter denne erklæring til ODBC-driveren.

UPDATE "SF"."DBO"."Contact" SET "LastName"=? WHERE "Id"=? AND "LastName"=?

SQL Server gør det for at sikre, at posten ikke bliver ændret mellem det tidspunkt, hvor du kørte forespørgslen, og det tidspunkt, hvor OPDATERINGER udføres. En hurtigere metode er at bruge SQL Server EXEC fungere. For eksempel:

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

SQL Server sender ODBC-driveren hele den streng, du har indtastet, så forespørgslen udføres uden at vælge hele tabellen.

Opdater med parametre

Sig, at du har:

Begin
	declare @Id varchar(20)='00346000002I95MAAS'
	declare @LastName varchar(20)='James'
	update SF8.SF.DBO.Contact set LastName=@LastName where Id=@Id
End

Dette fungerer på nøjagtig samme måde som beskrevet i opdateringsbemærkningerne. Syntaksen, når du bruger EXEC funktionsændringer:

Begin
      	declare @Id varchar(20)='00346000002I95MAAS'
      	declare @LastName varchar(20)='James'
	exec ('update SF.DBO.Contact set LastName=? where Id=?', @LastName, @Id)
      		at SF8
End

Hvor du har en kolonne såsom LastName= du indsætter en ? i stedet for @LastName at repræsentere, hvad du vil overføre til parameteren. Parametrene vises derefter efter UPDATE-sætningen i den rækkefølge, de skal læses i.

Indsættelse af en ny post og få en BLOB-fejl

Lad os sige, at du prøver at køre:

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

SQL Server sender dette til ODBC-driveren:

select * from "SF"."DBO"."Contact"

Dette gøres to gange. Første gang dette køres, tjekker SQL Server for at se, om resultatsættet kan opdateres. Anden gang dette sendes, flytter SQL Server til en tom post efter den sidste post returnerede og forsøger at lave en positionel INSERT, hvilket giver en fejl.

OLE DB provider "MSDASQL" for linked server "SF8" returned message "Query-based insertion or updating of BLOB values is not supported.".

Denne meddelelse returneres, fordi en positionel indsættelse forsøger at indsætte alle kolonnerne med NULL-værdier undtagen dem, du har angivet i din INSERT-sætning, og i tilfælde af kontakttabellen er der en BLOB (Long Text Area i Salesforce ), som OLE DB-udbyderen fra Microsoft ikke understøtter. Easysoft Salesforce ODBC-driveren understøtter indsættelse af alle felter i Salesforce, hvor du har tilladelse til at indsætte data. For at komme uden om dette, skal du blot bruge EXEC.

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

Dette sender bare INSERT direkte til ODBC-driveren.

Få Salesforce-id'et for den sidste post, du indsatte

Vi er blevet spurgt af et par af vores kunder, hvad der er den nemmeste metode til at få id'et for den række, der lige blev indsat. Dette eksempel viser, hvordan du kan få id'et for den sidste post, du indsatte i "Kontakt"-tabellen.

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)
      	declare @SQL as varchar(1024)
      
      	exec ('insert into SF.DBO.Contact (FirstName, LastName ) values (?, ?)', @FirstName, @LastName ) at SF8

      	select @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+'''''} and c.CreatedById=u.Id order by c.CreatedDate desc'')'

      	exec (@SQL) 

End

Når en post oprettes i Salesforce, indeholder kolonnen "CreatedDate" et tidsstempel, der er den UTC (Coordinated Universal Time), som posten blev oprettet og ikke nødvendigvis din nuværende dato/tid. @FindTs strengen er sat til UTC før INSERT finder sted, så når SELECT for at få Id kaldes, ser den kun på rækkerne indsat efter @FindTS blev indstillet.

Under SELECT, Easysoft CURRENT_USER Funktionen bruges også til at begrænse de rækker, der returneres fra Salesforce, til kun den bruger, der har indsat dataene.

Opdatering af SQL Server-data, når Salesforce-data ændres

Dette afsnit viser dig, hvordan du opretter en ny SQL Server-tabel baseret på strukturen af ​​en Salesforce-tabel og opdaterer den tabel, når der er ændringer i den pågældende Salesforce-tabel.

create procedure SFMakeLocal( @Link varchar(50), @Remote varchar(50), @Local varchar(50), @DropLocal int) as
          declare @SQL as nvarchar(max)
          begin
              /* Imports the data into a local table */
              /* Set DropLocal to 1 to drop the local table if it exists */
      
              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, 'Local table already exists')
                  RETURN  
              end
      
              set @SQL='select * into dbo.'+@Local+' from OPENQUERY('+@Link+',''select * from '+@Remote+''')'
              
              exec(@SQL)
      		select 'Local Table :'+@Local+' created.'
          end
      
      -- @Link Your SQL Server linked server
      -- @Remote The name of the table within Salesforce
      -- @Local The local table you want the data to be stored in
      -- @DropLocal Set to 1 if the table exists and you want to drop it

Kør proceduren for at kopiere poststrukturen fra Salesforce-tabellen til den lokale tabel, og overfør derefter alle Salesforce-dataene. Denne eksempelkommando bruger kontotabellen. Denne proces kan tage et stykke tid afhængigt af mængden af ​​data, du har i Salesforce-tabellen.

SFMakeLocal 'SF8','Account','LocalAccount', 0

Argumenterne er:

Argument Værdi
SF8 SQL Server Linked Server-navnet.
Konto Det Salesforce-tabelnavn, du ønsker at bruge til at læse strukturen og dataene fra.
Lokal konto Navnet på din tabel i SQL Server.
0 Denne standardværdi kan ændres til 1, hvis du tilføjer flere tilpassede kolonner i Salesforce, og du ønsker at droppe den lokale tabel for at oprette den igen med de nye kolonner.

Det næste trin er at oprette yderligere to procedurer, der opdaterer den lokale tabel, hvis nogen data opdateres eller indsættes i Salesforce-tabellen:

create procedure SFUpdateTable ( @Link varchar(50), @Remote varchar(50),
      create procedure SFUpdateTable  
      @Link varchar(50), @Remote varchar(50), @LocalTable varchar(50) 
      as
          begin
              -- Updates the data into a local table based on changes in 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
              declare @MaxCreated as datetime
              declare @MaxModified as datetime
              declare @SQL as nvarchar(max)
      		declare @RC as int
      
      		-- The first step is to create a global temporary table.
      
      		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, 'Temp name already in use.')
                  RETURN  
              end
      
      		set @SQL='select * into '+@TempName+' from '+@LocalTable+' where 1=0'
      
      		create table #LocalDates ( ColName varchar(20), DTS datetime)
              set @sql='insert into #LocalDates select ''Created'', max(CreatedDate) from '+@LocalTable
      		exec (@sql)
              set @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 SFAppendFromTemp @LocalTable, @TempName
      
      		set @SQL='drop table '+@TempName
      		exec (@SQL)
      
              set @SQL='select * into '+@TempName+' from openquery('+@Link+',''select * from '+@Remote+' where LastModifiedDate>{ts'''''+convert(varchar(22),@MaxModified,120)+'''''} and 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
      
      
          /* Uses the temp table to import the data into the local table making sure any duplicates are removed first */
      
          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=''
      
          declare col_cursor cursor for 
              select syscolumns.name from sysobjects inner join syscolumns on sysobjects.id = syscolumns.id where sysobjects.xtype = 'u' and  sysobjects.name = @Local
      
          open col_cursor
          fetch next from col_cursor into @ColName
          while @@FETCH_STATUS=0
          Begin
              set @Columns=@Columns+'['+@ColName+']'
              fetch next from col_cursor into @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
      
      -- Two procedures are used to get the data from a remote table. 1) SFUpdateTable, which
      -- copies the data into a temporary table. 2) SFAppendFromTemp, which appends
      -- the data from the temporary table into the local table.
      
      -- @Link Your SQL Server linked server name
      -- @Remote The name of the table within Salesforce
      -- @Local The local table where you want the data to be stored in
      -- @TempName A name of a table that can be used to temporary store data. Do not
      -- use an actual temporary table name such as #temp, this will not work.

For at teste dette, kør:

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

Dette eksempel kan bruges med enhver Salesforce-tabel, som en bruger har adgang til.

Dovne skemavalidering

I dine SQL Server-linkede serveregenskaber, under "Serverindstillinger" sektionen, er en mulighed for "Lazy Schema Validation". Som standard er dette sat til FALSE, hvilket får SQL Server til at sende SELECT-sætninger to gange. Første gang forespørgslen sendes, bruger SQL Server de detaljer, der sendes tilbage til at opbygge metadata om dit resultatsæt. Så sendes forespørgslen igen. Dette er ret dyrt overhead, så Easysoft vil anbefale, at du indstiller "Lazy Schema Validation" til TRUE, hvilket betyder, at der kun sendes én forespørgsel, der henter både metadata og resultatsæt på én gang. Dette sparer også på antallet af Salesforce API-kald, der foretages.

Begrænsninger for Microsofts OLEDB for ODBC-udbyder

Detaljer om begrænsningerne af OLEDB for ODBC-udbyderen kan findes på:

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

Hvordan finder jeg poster med et linjefeed (ny linje) i faktureringsadressen?

Ved at bruge nogle af Easysoft-chaufførens interne funktioner, kan du nemt finde poster, hvor faktureringsadressen har et linjeskift i posten. For eksempel:

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) Denne funktion leder efter positionen for x i den angivne kolonne.

CHAR(X) Denne funktion returnerer tegnet med ASCII-værdien x .

Flere oplysninger om de tilgængelige funktioner i vores Salesforce ODBC-driver kan findes her

Kan jeg se, hvilke tabeller der er tilgængelige via Easysoft-softwaren?

For at få en liste over tabeller, som du kan få adgang til, skal du køre:

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

Kan jeg se, hvilke kolonner der er tilgængelige via Easysoft-softwaren?

Du kan få en liste over kolonner, der er i tabellen, ved at køre:

vælg * fra openquery(SF8,'vælg * fra INFO_SCHEMA.COLUMNS hvor TABLE_NAME=''Konto'' ')

Ved at bruge denne metode kan du kun få en liste over de kolonner, der hører til den tabel, du angiver i TABLE_NAME WHERE-sætningen. Hvis du vil se en komplet liste over kolonner for alle tabeller, skal du køre:

begin
    declare @Table nvarchar(max)
	
	declare table_cursor cursor 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
		fetch next from table_cursor into @Table
	End

    close table_cursor
    deallocate table_cursor

end

Kan jeg programmæssigt oprette en linket server?

Ja. Der er masser af eksempler på dette på nettet, for eksempel:

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


  1. Oracle pivot med underforespørgsel

  2. Hvordan starter man MySQL med --skip-grant-tables?

  3. Hvad betyder PÅ [PRIMÆR]?

  4. Android-rum + vinduesfunktioner