毎週金曜日、2、3(時には300以上)のXMLファイルを2つのテーブルにインポートする必要があります。
テーブルの1つの構造R000000
、次のようになります:
R00000010 | R00000020 | R00000030 | R00000040 | R00000050 | R00000060
---------- ------------ ---------- ----------- ----------- ----------
R000000 | I | 0002 | 1 | 2 | 0026
R000000 | I | 0003 | 1 | 2 | 0025
R000000 | I | 0004 | 1 | 2 | 0021
R000000 | I | 0006 | 1 | 2 | 0023
R000000 | I | 0001 | 1 | 2 | 0022
^各行はXMLファイルに対応しています。
構造は変更されず、データのみが変更されます(この場合、ランダムなものを配置しました)。
XMLファイルは次のようになります。
<?xml version="1.0" encoding="ISO-8859-1"?>
<ns0:P4131 xmlns:ns0="http://switching/xi">
<R000000>
<R00000010>R000000</R00000010>
<R00000020>I</R00000020>
<R00000030>0002</R00000030>
<R00000040>1</R00000040>
<R00000050>0026</R00000050>
<R00000060>2</R00000060>
</R000000>
</ns0:P4131>
これを行う最良の方法は何ですか?私は現在、これをAccessで行っています。
これは、SQLCLRを介して行うのが実際には非常に簡単です。ストアドプロシージャは、特定のディレクトリにあるxml
ファイルを読み取るように(またはすべてのサブディレクトリを簡単にチェックするように)設定して、すべての内容を含む単一の結果セットを出力できます。これを行うと、次のクエリをテーブルに入力できます。
_INSERT INTO dbo.R000000 (R00000010, R00000020, R00000030, R00000040, R00000050, R00000060)
EXEC dbo.GetXmlDataFromFiles(N'C:\Path\To\XML\Files');
_
そして、それはそれです。
次のコードは、_.xml
_入力パラメーターで指定されたディレクトリ内の_@FilePath
_ファイルを読み取り、オプションでサブディレクトリを走査して、各ファイルの内容の単一の結果セットを返します。
ご注意ください:
<R000000>
_ノードが含まれている可能性がある場合、このコードを変更してそれを処理するのは非常に簡単です。_using System;
using System.Data;
using System.Data.SqlTypes;
using System.IO;
using System.Xml;
using Microsoft.SqlServer.Server;
public class ImportXmlFiles
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void ReadXmlFiles([SqlFacet(MaxSize = 500)] SqlString FilePath,
SqlBoolean Recursive)
{
XmlDocument _FileContents = new XmlDocument();
SqlDataRecord _ResultRow = new SqlDataRecord(new SqlMetaData[]{
new SqlMetaData("R00000010", SqlDbType.VarChar, 10),
new SqlMetaData("R00000020", SqlDbType.VarChar, 10),
new SqlMetaData("R00000030", SqlDbType.VarChar, 10),
new SqlMetaData("R00000040", SqlDbType.Int),
new SqlMetaData("R00000050", SqlDbType.VarChar, 10),
new SqlMetaData("R00000060", SqlDbType.Int)
});
SqlContext.Pipe.SendResultsStart(_ResultRow);
foreach (string _FileName in Directory.GetFiles(FilePath.Value, "*.xml",
(Recursive.IsTrue) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
)
{
_FileContents.Load(_FileName);
XmlElement _Row = (XmlElement)_FileContents.SelectSingleNode("//R000000");
_ResultRow.SetString(0, _Row.SelectSingleNode("./R00000010").InnerText);
_ResultRow.SetString(1, _Row.SelectSingleNode("./R00000020").InnerText);
_ResultRow.SetString(2, _Row.SelectSingleNode("./R00000030").InnerText);
_ResultRow.SetInt32(3,
Convert.ToInt32(_Row.SelectSingleNode("./R00000040").InnerText));
_ResultRow.SetString(4, _Row.SelectSingleNode("./R00000050").InnerText);
_ResultRow.SetInt32(5,
Convert.ToInt32(_Row.SelectSingleNode("./R00000060").InnerText));
SqlContext.Pipe.SendResultsRow(_ResultRow);
}
SqlContext.Pipe.SendResultsEnd();
return;
}
}
_
上記のSQLCLRストアドプロシージャの簡単にインストールできる実際の例は、Pastebinの次の場所にあります。
SQLCLR Stored Procは多数のXMLファイルの1つの結果セットを返します
!!アセンブリは_EXTERNAL_ACCESS
_に設定されていますが、TRUSTWORTHY
のデータベースプロパティはnotがON
に設定されていることに注意してください。これは、ほとんどのSQLCLRで行われますあなたがインターウェブでここで見つける例。アセンブリはコンパイル時に署名され(厳密な名前が付けられます)、インストールスクリプトは_[master]
_に非対称キーを作成し、その非対称キーに基づくログインを作成して、そのログインに_EXTERNAL ACCESS Assembly
_権限を付与します。これにより、_EXTERNAL_ACCESS
_を必要とせずにアセンブリを_TRUSTWORTHY ON
_に設定できるだけでなく、notを使用してアセンブリにUNSAFE
がTRUSTWORTHY
に設定されている場合に許可されるON
に設定される!!
より一般的で、さまざまなXML構造をインポートできる別のアプローチは、ストアドプロシージャの代わりにテーブル値関数を使用することです。ここに示されているストアドプロシージャよりも、各ファイルの内容を単に読み取り、XMLデータ型の1つのフィールドである結果セットの各ファイルに対して1行を返す方が簡単です。次に、T-SQL .nodes()
および.value()
関数を使用して、必要に応じてさまざまな構造を解析できます。
以下のようなものを試してみてください...
環境に合わせて変数をプラグインし、データ型をチェックする必要があります(先行ゼロを維持するためにロジックを追加する必要があるかもしれません)、最終的な一時テーブルから通常のテーブルに変更するなど。
後でファイルを削除せずにXMLファイルから一時テーブルにインポートする場合は問題なく動作しますが、UNCパスからファイルを削除するロジックを追加することは、別のxp_cmdshellコマンドでそれほど難しくないはずです。
DECLARE @folder AS VARCHAR(1000) = '\\servername\sharename\folder\subfolder1\'
DECLARE @command VARCHAR(500) = 'DIR /B "' + @folder + '*.xml"'
DECLARE @file VARCHAR(100)
DECLARE @filesinafolder TABLE (filenameswithfolder VARCHAR(500))
DECLARE @sql NVARCHAR(4000)
-- create global temp table
IF OBJECT_ID('tempdb..##XMLImport') IS NOT NULL
DROP TABLE ##XMLImport
CREATE TABLE ##XMLImport (
R00000010 VARCHAR(7)
,R00000020 VARCHAR(1)
,R00000030 INT
,R00000040 INT
,R00000050 INT
,R00000060 INT
)
INSERT INTO @filesinafolder
EXEC master..xp_cmdshell @command
-- create cursor
DECLARE filecurs CURSOR
FOR
SELECT REPLACE(filenameswithfolder, @folder, '') AS filenames
FROM @filesinafolder
WHERE filenameswithfolder IS NOT NULL
OPEN filecurs
FETCH NEXT
FROM filecurs
INTO @file
IF @file = 'FILE NOT FOUND'
GOTO exitprocessing
WHILE @@fetch_status != - 1
BEGIN
SET @sql = 'DECLARE @X XML
SELECT @X = P
FROM OPENROWSET(BULK ''' + @folder + '' + @file + ''', SINGLE_BLOB) AS Products(P)
DECLARE @iX INT
EXEC sp_xml_preparedocument @iX OUTPUT
,@X
SELECT *
INTO #XMLResults
FROM OPENXML(@iX, ''/*/*'', 2) WITH (
R00000010 VARCHAR(7)
,R00000020 VARCHAR(1)
,R00000030 INT
,R00000040 INT
,R00000050 INT
,R00000060 INT
)
EXEC sp_xml_removedocument @iX
INSERT INTO ##XMLImport
SELECT R00000010
,R00000020
,R00000030
,R00000040
,R00000050
,R00000060
FROM #XMLResults'
--PRINT @sql
EXEC sp_executesql @sql
-- process next file
FETCH NEXT
FROM filecurs
INTO @file
END
exitprocessing:
-- clean up
CLOSE filecurs
DEALLOCATE filecurs
SELECT *
FROM ##XMLImport