web-dev-qa-db-ja.com

BCP出力にカスタムヘッダー行を挿入する

要するに、ここには、電子メール、姓、名などの一部の従業員データを管理するデータベースがあります。当社は、このSAPベースの経費報告システムを買収しました:|非常に奇妙な形式で従業員ベースのデータをエクスポートする必要があります。多くの詳細を取得することなく、このデータのエクスポートには合計137列が必要です。これらの列の多くは空の値を持っています。

シンプル基本的にこの情報をデータベースから取得するクエリを作成し、いくつかの定数を必要なものに設定しました。この質問でこのクエリが何であるかは関係ありません。単に、いくつかのデータを取得するSELECTステートメントです。

次に、特定のファイル名とパイプ区切り形式でこれを毎日エクスポートする必要がありました。

--employee export
DECLARE @FileName varchar(500)
SET @FileName = (SELECT '\\someFileServer\Public\someFolder\employee_p06010603ace_305_202105_' + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR(19), CONVERT(DATETIME, getdate(), 112), 126), '-', ''), 'T', ''), ':', '') + '.txt')
DECLARE @sql varchar(8000)
DECLARE @header varchar(8000)
SET @sql= 'bcp "exec [MyDBServer].[MyDbName].dbo.ConcurEmployeeExport" queryout ' + @FileName + ' -c -T -t "|"'
Exec master..xp_cmdshell @sql

完璧、私はこのような行を取得します:

305|Jon| |Doe|10217487|10217487@t| 
305|Steve| |Smith|10217522|10217522@t|

列数が約130に増えたため、関連性がないため、これらすべてを表示したくありませんでした。実装コーディネーターが「はい」のように見えるまで、最初の行がいくつかの値を持つこの奇妙な行で始まる必要があることを除いて、私は完了したと思いました。

結果として得られるクエリの意味は、値が1つ追加された行が1つ追加されているはずです。簡単なことですが、必要なものを結合すると思っていました。

UNION ALLを除いて、クエリの列の数と一致させるために、いくつかの追加の空の値を入力する必要がありました。問題は、エクスポートを実行すると、パイプで区切られた空の列の束で終わり、次のようになります。

100|0|SSO|UPDATE|EN|N|N| | | | | | | | | | | | | | | | |

それが約120カラム続くと想像してください。私はこれを彼らに提出しました、そして彼らはあなたの最初の列を除いてすべてがよく見えるとよく言った。最初の7つの値が必要なだけです。つまり、これらの値だけが必要です。

 100|0|SSO|UPDATE|EN|N|N|

ただし、UNION ALLでは両方のクエリから同じ数の列を必要とするため、これを行うことはできません。したがって、このファイルを生成した後、最初の行の|を最後のNの後に置き換えて、どういうわけか私は何とかできると思いました。

これを行うために別のアプリ/インターフェイスを作成する必要なく、これは可能ですか?私は持っているものを保持したいのですが、最初の行のみを変更して、取得した結果が修正されるようにします。現在、3行のサンプルを次に示します。

100|0|SSO|UPDATE|EN|N|N| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 

305|Jon| |Doe|11111|11111@t| |[email protected]|en_US|USA| |TK_Symbolic|USD| |Y|USA|IS|0000202105|0120|CC|371000000| | | |030257| | | | | | | | | | | | |030257| | | |USA0000202105|N|N| | |N|N|N|N|N|N|N| | | | | |10217495| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |EOL

305|Steve| |Smith|22222|22222@t| |[email protected]|en_US|USA| |TK_Symbolic|USD| |Y|USA|IS|0000202105|0120|CC|371000000| | | |030177| | | | | | | | | | | | |030177| | | |USA0000202105|N|N| | |N|N|N|N|N|N|N| | | | | |10217495| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |EOL

これを取得する必要があります。

100|0|SSO|UPDATE|EN|N|N|

305|Jon| |Doe|11111|11111@t| |[email protected]|en_US|USA| |TK_Symbolic|USD| |Y|USA|IS|0000202105|0120|CC|371000000| | | |030257| | | | | | | | | | | | |030257| | | |USA0000202105|N|N| | |N|N|N|N|N|N|N| | | | | |10217495| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |EOL

305|Steve| |Smith|22222|22222@t| |[email protected]|en_US|USA| |TK_Symbolic|USD| |Y|USA|IS|0000202105|0120|CC|371000000| | | |030177| | | | | | | | | | | | |030177| | | |USA0000202105|N|N| | |N|N|N|N|N|N|N| | | | | |10217495| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |EOL

|をすべて削除した最初の行に注目してください。理想的には、ファイルをエクスポートするクエリでこれを実行したいと思います。このすべてを削除する効果への何か| | |エクスポート後の最初の行に?

最初の行は静的で、次を使用して生成されます。

SELECT
    '100' AS [Transaction Type],    --1
    '0' AS [Error Threshold],   --2
    'SSO' AS [Password Generation], --3
    'UPDATE' AS [Existing Record Handling], --4
    'EN' AS [Language Code],    --5
    'N' AS [Validate Expense Group],    --6
    'N' AS [Validate Payment Group],    --7

残念ながら、これは良い考えではありません/不可能だと私の脳は繰り返し言います。私はこれを.netで簡単に実行できることを知っていますが、このようなことを行う別のプログラムをそこに座っておくのは嫌です。

6
JonH

CMDスクリプトを作成して一時ヘッダー行ファイルを作成し、BCPを実行してから、BCP出力を一時ヘッダーファイルに追加できます。これは、現在の設定で[〜#〜] bcp [〜#〜]への既存の呼び出しと同様に、xp_cmdshellを介して呼び出されます。

これがAddHeaderToExportFile.cmdという名前のCMDスクリプトです。次の2つのパラメーターを取ります。

  1. ファイル名。
  2. ヘッダー行。変更された場合は、ストアドプロシージャを更新するだけで済みます。

Windowsエクスプローラーで新しいテキストファイルを作成し、名前(.txt拡張子を含む)をAddHeaderToExportFile.cmdに置き換えます。次にAddHeaderToExportFile.cmdを編集し、次のコードを貼り付けて保存します。

AddHeaderToExportFile.cmd:

@ECHO OFF

SET TempHeaderRowFile="%TEMP%\TempHeader.txt"
SET TempOutputFile="%TEMP%\TempOutput.txt"


BCP "EXEC [MyDBServer].[MyDbName].dbo.ConcurEmployeeExport" queryout %TempOutputFile% -c -C 1252 -T -t "|"

ECHO %~2 >  %TempHeaderRowFile%

REM Concatenate Header + BCP_Output -> @FileName
REM /V = Verifies that new files are written correctly.
REM /Y = Suppresses prompting to confirm you want to overwrite an existing destination file
REM /B = treat files as Binary (else you get an extraneous CHAR(26) at the end)
COPY /V /Y /B %TempHeaderRowFile% + %TempOutputFile% %1

REM Delete the temporary Header and BCP output files
IF EXIST %TempHeaderRowFile% DEL /Q %TempHeaderRowFile%
IF EXIST %TempOutputFile% DEL /Q %TempOutputFile%

元のスクリプトを新しいCMDスクリプトを呼び出すように変更すると、新しいSQLは次のようになります。

ストアドプロシージャ:

--employee export
DECLARE @FileName NVARCHAR(500)
SET @FileName = N'\\someFileServer\Public\someFolder\employee_p06010603ace_305_202105_' +
      REPLACE(REPLACE(REPLACE(
           CONVERT(NVARCHAR(19), CONVERT(DATETIME, GETDATE(), 112), 126),
                              N'-', N''), N'T', N''), N':', N'') +
      N'.txt';

DECLARE @Command NVARCHAR(4000),
        @Header   NVARCHAR(500);

SET @Header = N'100|0|SSO|UPDATE|EN|N|N|';

SET @Command = N'C:\TEMP\BCP\AddHeaderToExportFile.cmd "' +
               @FileName +
               N'", "' +
               REPLACE(@Header, N'|', N'^|') +
               N'"';

EXEC xp_cmdshell @Command; --, NO_OUTPUT;

[〜#〜]または[〜#〜]、ヘッダー行の値を保持するテキストファイルを作成し、CMDスクリプトを完全にスキップできますxp_cmdshellを複数回呼び出して、同じことを実行します。

--employee export
DECLARE @FileName NVARCHAR(500),
        @HeaderFile NVARCHAR(500);
SET @FileName = N'\\someFileServer\Public\someFolder\employee_p06010603ace_305_202105_' +
      REPLACE(REPLACE(REPLACE(
           CONVERT(NVARCHAR(19), CONVERT(DATETIME, GETDATE(), 112), 126),
                              N'-', N''), N'T', N''), N':', N'') +
      N'.txt';
SET @HeaderFile = N'\\someFileServer\public\someFolder\header.txt'; -- static header row

DECLARE @Command NVARCHAR(4000);

SET @Command = N'BCP "EXEC [MyDBServer].[MyDbName].dbo.ConcurEmployeeExport" queryout ' + @FileName + 'tmp -c -C 1252 -T -t "|"';
EXEC xp_cmdshell @Command; --, NO_OUTPUT;

SET @Command = N'COPY /V /Y /B ' + @HeaderFile + N' + ' + @FileName + N'tmp ' + @FileName;
EXEC xp_cmdshell @Command; --, NO_OUTPUT;

SET @Command = N'IF EXIST ' + @FileName + N'tmp DEL /Q ' + @FileName + N'tmp';
EXEC xp_cmdshell @Command; --, NO_OUTPUT;
4
Solomon Rutzky

検討すべきオプションの1つは、2つを分離することです。エクスポートとすべてのフィールドを含む1つのファイルを作成します。他のファイルはあなたのヘッダーを持っています。最後のステップは、この2つを次のようなものと組み合わせることです。

REM create the header file
ECHO 100|0|SSO|UPDATE|EN|N|N| >"MyExport.txt.header"
REM append the bcp export to the header file
TYPE "MyExport.txt">>"MyExport.txt.header"
REM rename the header file back to the export file name
MOVE /y "MyExport.txt.header" "MyExport.txt"

エクスポートは毎日行われたとのことですが、これを行うSQLジョブスケジュールはありますか?これは、オペレーティングシステム(CmdExec)タイプを使用してジョブに追加されたステップである可能性があります。私があなたを正しく理解していれば、それは「外部」プロセスである必要はありません。すべてのコードをジョブステップに入力できます。

慣れていない場合は、SQLCMDを調べてください。結論としては、私はあなたが正しいと信じています。あなたが望むことは、おそらく1ステップで直接bcpを使用して行うことはできません。

4
AWEInCA