sql >> データベース >  >> RDS >> Sqlserver

SalesforceでSQLServerを使用するためのヒント

    目次

    1. 概要
    2. WHERE条項
    3. 複数のテーブル結合
    4. リモートテーブルに接続されたローカルテーブル
    5. 挿入、更新、削除
    6. 更新
    7. パラメータで更新
    8. 新しいレコードの挿入とBLOBエラーの取得
    9. 最後に挿入したレコードのSalesforceIDを取得する
    10. Salesforceデータの変更時にSQLServerデータを更新する
    11. レイジースキーマ検証
    12. MicrosoftのOLEDBforODBCプロバイダーの制限
    13. 請求先住所に改行(改行)が含まれるレコードを見つけるにはどうすればよいですか?
    14. Easysoftソフトウェアで利用できるテーブルを確認できますか?
    15. Easysoftソフトウェアで利用できる列を確認できますか?
    16. プログラムでリンクサーバーを作成できますか?
    概要

    このドキュメントでは、SalesforceでSQLServerを使用するためのヒントをいくつか紹介します。 SQL ServerをSalesforceに接続するために使用されるコンポーネントは、SQLServerリンクサーバーとEasysoftSalesforceODBCドライバーです。この記事では、SQLServerをSalesforceに接続する方法について説明します。このドキュメントの例では、使用されているリンクサーバー名(SQLコマンドで参照)はSF8です。

    このドキュメントのすべてのSQLは、SQLServer2017およびEasysoftSalesforceODBCドライバーバージョン2.0.0から2.0.7に対してテストされています。

    SQLServerの機能OPENQUERY およびEXECEXECUTE )はSQL Server 2008に導入され、これらの関数は2008以降のすべてのバージョンのSQLServerと互換性があります。

    このドキュメントは、Easysoftを介したSQLServerのSalesforceへの接続に関してサポートチームが受け取った多数の問い合わせに応えて作成されました。ただし、SQLの例は、別のODBCドライバーとバックエンドを使用するリンクサーバー接続にも役立つはずです。

    このドキュメントに貢献したい場合は、提出物をに電子メールで送信してください。

    どこの条項

    私たちに報告される一般的な問題は、「単純なWHERE句が1行だけを返すのに長い時間がかかる」です。例:

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

    SQL Serverは上記のクエリを変換し、これをSalesforceODBCドライバーに送信します。

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

    WHERE句は常に削除されます。これにより、ODBCドライバーはそのテーブルのすべての行を返すように強制されます。次に、SQL Serverはそれらをローカルでフィルタリングして、必要な行を提供します。指定したWHERE句は重要ではないようです。これは、ODBCドライバーに渡されることはありません。

    これに対する簡単な解決策は、SQLServerのOPENQUERYを使用することです。 代わりに機能します。例:

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

    OPENQUERY内で実行するすべてのSQL WHEREを含む関数はドライバーに直接渡されます 条項。

    複数のテーブル結合

    これは、両方のテーブルがリンクサーバーから戻ってくる単純な2つのテーブル結合です。

    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は、次のクエリをODBCドライバーに送信します。

    select * from Account
    select * from Contact

    SQL Serverはこれを実行して、列名とデータ型のリストを取得します。次に、これらのクエリをODBCドライバーに送信します。

    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

    両方のクエリからのデータがローカルテーブルに返され、WHERE句がAccountテーブルに配置され、両方のテーブルからのデータが結合されて返されます。

    ここでもOPENQUERYを使用します 作成したSQLがODBCドライバーに直接渡されるようにするため、代わりにSQLServerで次のコマンドを実行します。

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

    SQL Serverは同じ「名前」を持つ複数の列を処理できないため、わずかな変更が必要です。そのため、これらの列の1つを名前変更する必要があります。例:

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

    これにより、ODBCドライバーはSQL全体を一度に処理し、必要な結果のみを返します。

    リモートテーブルに接続されたローカルテーブル

    この例では、ローカルテーブルはを実行して作成されました。

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

    2つのテーブルの結合は次のようになります。

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

    これにより、SQLServerは次のクエリをODBCドライバーに3回送信します。

    select * from Contact

    これらのクエリの少なくとも1つで、SQLServerはテーブル内のすべてのデータを要求します。次に、SQLServerは次のことを要求します。

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

    次に、SQL Serverは、「?」の代わりにLocalAccountテーブルのAccountIdのリストをODBCドライバーに渡します。 LocalAccount。[Name]列がLIKE句と一致するパラメータ。

    ODBCテーブルがクエリの2番目のテーブルである場合のより高速な方法は、ODBCテーブルから必要な列のみを取得することです。これは、OPENQUERYを使用して実行できます 働き。例:

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

    これでもContactテーブルからすべての行を取得しますが、必要な列のみを取得するため、標準のクエリよりも高速です。

    別の可能な方法は、カーソルと一時テーブルを使用することです。例:

    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

    このメソッドは、OPENQUERYよりも数倍高速になる可能性があります 前の例で示したメソッド。EasysoftODBCドライバーに渡されるWHERE句がSalesforceのインデックスを使用する場合。

    挿入、更新、削除

    SELECTクエリではないクエリを実行している場合、これを行うための最良の方法は、SQL Server EXECを使用することです。 働き。リンクサーバーがEXECを使用できない場合 、次のようなメッセージが表示されます:

    Server 'SF8' is not configured for RPC.

    EXECを使用するには 、リンクサーバーを右クリックして、プロパティを選択します。 [サーバーオプション]セクションで、[RPC出力]を[True]に設定します。その後、EXECを使用できます 機能。

    更新

    SQLServerに次のステートメントがあるとします。

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

    SQLServerはこのSQLをODBCドライバーに送信します。

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

    すべてのレコードが取得され、SQLServerはこのステートメントをODBCドライバーに送信します。

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

    SQL Serverは、クエリを実行してからUPDATEを実行するまでの間にレコードが変更されないようにするためにこれを実行しています。より高速な方法は、SQLServerのEXECを使用することです。 働き。例:

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

    SQL Serverは入力した文字列全体をODBCドライバーに送信するため、テーブル全体を選択せず​​にクエリが実行されます。

    パラメータで更新

    あなたが持っていると言う:

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

    これは、アップデートノートで説明されているのとまったく同じように機能します。ただし、EXECを使用する場合の構文 機能の変更:

    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

    LastName=などの列がある場合 ?を入れます @LastNameの代わりに パラメータに渡すものを表します。次に、パラメーターは、UPDATEステートメントの後に、読み取る必要のある順序でリストされます。

    新しいレコードの挿入とBLOBエラーの取得

    実行しようとしているとしましょう:

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

    SQL ServerはこれをODBCドライバーに送信します:

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

    これは2回行われます。これを初めて実行するとき、SQLServerは結果セットが更新可能かどうかを確認しています。これが2回送信されると、SQL Serverは最後のレコードが返された後、空のレコードに移動し、位置INSERTを実行しようとします。これにより、エラーが発生します。

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

    このメッセージが返されるのは、位置挿入がINSERTステートメントで指定した列を除くすべての列をNULL値で挿入しようとし、Contactテーブルの場合はBLOB(Salesforceの長いテキスト領域)があるためです。 MicrosoftのOLEDBプロバイダーはサポートしていません。 Easysoft Salesforce ODBCドライバーは、データを挿入する権限があるSalesforce内のすべてのフィールドの挿入をサポートします。これを回避するには、EXECを使用するだけです。

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

    これにより、INSERTがODBCドライバーに直接送信されます。

    最後に挿入したレコードのSalesforceIDを取得する

    数人のお客様から、挿入されたばかりの行のIDを取得する最も簡単な方法を尋ねられました。この例は、「連絡先」テーブルに挿入した最後のレコードのIDを取得する方法を示しています。

    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

    Salesforceでレコードが作成されると、[CreatedDate]列には、レコードが作成されたUTC(協定世界時)であるタイムスタンプが含まれますが、必ずしも現在の日付/時刻である必要はありません。 @FindTs 文字列はINSERTが実行される前にUTCに設定されるため、IDを取得するためのSELECTが呼び出されると、@FindTSの後に挿入された行のみが参照されます。 設定されました。

    SELECT中に、Easysoft CURRENT_USER この関数は、Salesforceから返される行をデータを挿入したユーザーのみに制限するためにも使用されます。

    Salesforceデータの変更時にSQLServerデータを更新する

    このセクションでは、Salesforceテーブルの構造に基づいて新しいSQL Serverテーブルを作成し、そのSalesforceテーブルに変更があった場合にそのテーブルを更新する方法を示します。

    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

    手順を実行して、Salesforceテーブルからローカルテーブルにレコード構造をコピーしてから、すべてのSalesforceデータを転送します。このコマンド例では、Accountテーブルを使用しています。 Salesforceテーブルにあるデータの量によっては、このプロセスにかなりの時間がかかる場合があります。

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

    引数は次のとおりです。

    引数
    SF8 SQLServerリンクサーバー名。
    アカウント 構造とデータの読み取りに使用するSalesforceテーブル名。
    LocalAccount SQLServerのテーブルの名前。
    0 Salesforceにカスタム列を追加し、ローカルテーブルを削除して新しい列で再度作成する場合は、このデフォルト値を1に変更できます。

    次のステップは、データが更新された場合、またはSalesforceテーブルに挿入された場合に、ローカルテーブルを更新する2つのプロシージャを作成することです。

    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.

    これをテストするには、次を実行します:

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

    この例は、ユーザーがアクセスできるすべてのSalesforceテーブルで使用できます。

    レイジースキーマ検証

    SQL Serverにリンクされたサーバーのプロパティの[サーバーオプション]セクションには、[遅延スキーマ検証]のオプションがあります。デフォルトでは、これはFALSEに設定されています。これにより、SQLServerはSELECTステートメントを2回送信します。クエリが初めて送信されるとき、SQL Serverは、返された詳細を使用して、結果セットに関するメタデータを構築します。その後、クエリが再度送信されます。これは非常にコストのかかるオーバーヘッドであるため、Easysoftは「LazySchema Validation」をTRUEに設定することをお勧めします。これは、1つのクエリのみが送信され、メタデータと結果セットの両方を一度に取得することを意味します。これにより、SalesforceAPI呼び出しの数も節約できます。

    MicrosoftのOLEDBforODBCプロバイダーの制限

    OLEDB for ODBCプロバイダーの制限の詳細については、次のURLを参照してください。

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

    請求先住所に改行(改行)が含まれるレコードを見つけるにはどうすればよいですか?

    Easysoftドライバーの内部機能のいくつかを使用することにより、請求先住所にレコード内のラインフィードがあるレコードを簡単に見つけることができます。例:

    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) この関数は、xの位置を検索します 指定された列内。

    CHAR(X) この関数は、ASCII値がxの文字を返します。 。

    Salesforce ODBCドライバーで使用可能な関数の詳細については、こちらをご覧ください

    Easysoftソフトウェアで利用できるテーブルを確認できますか?

    アクセスできるテーブルのリストを取得するには、次のコマンドを実行します。

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

    Easysoftソフトウェアで利用できる列を確認できますか?

    次のコマンドを実行すると、テーブルにある列のリストを取得できます。

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

    このメソッドを使用すると、TABLE_NAMEWHERE句で指定したテーブルに属する列のリストのみを取得できます。すべてのテーブルの列の完全なリストを表示するには、次のコマンドを実行します。

    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
    プログラムでリンクサーバーを作成できますか?

    はい。この例はウェブ上にたくさんあります。例:

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


    1. Django開発データベースをデフォルトのSQLiteからPostgreSQLに変更する

    2. MariaDBでのROUND()のしくみ

    3. SQL * Plus/SQLcl出力グリッドに垂直方向の境界線を追加する方法

    4. 主キーと一意キーの違い