セールス: 1-800-867-1380

SSIS の分割と Azure コンポーネント

更新日: 2014年4月

Azure™ SQL データベースで使用される重要なデータセットでは、ある程度の分割が必要です。Microsoft® SQL Server® Integration Services (SSIS) で分割を行うには、条件分割および複数の変換先アダプターを使用します。ただし、この技法にはいくつかの制限事項があります。

  • 設計プロセスは煩雑です。 多くのコンポーネントを使用すると、SSIS のデザイン画面は扱いにくくなります。一部の分割スキームには、数百の分割データベースが含まれる場合があります。SSIS パッケージを設計してこれらすべての変換先を対象にすると、各コンポーネントを開いて設定する必要があるため、時間がかかり、間違いやすくなります。各コンポーネントには、再設計することなく開発環境から運用環境に移行する場合にパラメーター化する必要がある接続マネージャーも必要になります。

    理由と技法などを説明した分割の詳細については、「Sharding with SQL Azure (SQL Azure を使用した分割)」というブログ記事を参照してください。

  • 動的に変化する環境は再設計が必要です。 パラメーターによってサーバー名とデータベース名を変更できますが、変換先の数を変更することはできません。分割データ ストアでは、分割データベースの数は頻繁に変わります。従来のすぐに使用できるコンポーネントの場合、変更には SSIS パッケージの再設計と展開が必要です。SSIS 分割の変換先コンポーネントは、実行のたびに分割カタログを読み取り、分割カタログの内容に基づいて正しい接続を確立することにより、この問題を解消します。

  • すぐに使用できる変換先では、テーブル名は動的ではありません。 Azure SQL データベースにデータを挿入するには、ADO.NET、ODBC、または OLE DB 変換先アダプターを使用できます。ただし、これらのすべてのコンポーネントでテーブルを設定する必要があり、パラメーター化することはできません。一部の論理的な分割シナリオでは、テーブル名は実際には変更できたり、テーブルを追加できたりする場合があります。分割コンポーネントにより、テーブル名を変換先で動的にすることもできます。

  • Azure または圧縮には SSIS コンポーネントは存在しません。 ワイド エリアまたは帯域幅に制限がある接続でデータが移動する場合は、ファイルを圧縮し、大規模なデータ セットを分割して、変換元からデータが抽出されるときに変換先への処理を可能にする必要があります。Blob コピー元およびコピー先コンポーネントは、データの分割、ファイルの圧縮、Azure Blob ストレージとのやり取り (2 段階移動の一部、または HDInsight 使用のための Blob ストレージへの移動の一部として)、およびキューのオプションの使用を有効にして、制限されたネットワークの各終端のパッケージが、パイプラインで分割されたデータを処理できるようにします。

SSIS データベース分割コンポーネントは、これらの課題を解消するうえで役立ちます。

コンポーネントおよび分割カタログ設定スクリプトが、Microsoft ダウンロード センターから個別のダウンロードとして利用できます。コンポーネントを完全に使用するには、次のようにして両方のファイルをダウンロードし、インストールします。

コンポーネントをダウンロードしてインストールするには

  1. ダウンロード サイトから SSIS Database Sharding Components.zip ファイルをダウンロードします。

  2. ファイルを開き、SSIS コンポーネントをインストールする予定のコンピューターのディレクトリにコンテンツを移動します。

    noteメモ
    Microsoft SQL Server 2012 の SSIS は、コンポーネントをインストールする予定のコンピューターにインストールする必要があります。

  3. SSISDatabaseSharding64.msi インストーラーを実行します。

  4. 使用許諾契約書に同意し、[次へ] をクリックします。すべてのコンポーネントと依存関係がインストールされます。

  5. SQL Server Data Tools を開き、新しい SSIS プロジェクトを開始します。

  6. データ フロー タスクをデザイン面にドラッグし、そのタスクをダブルクリックします。

  7. SSIS ツール バーで、Blob コピー先、Blob コピー元、およびデータベース分割コンポーネントが図 1 に示すようにツールボックスに表示されることを確認します。

    図 1図 1. Blob コピー先、Blob コピー元、およびデータベース分割コンポーネントが正しくインストールされると、データ フロー ツールボックスにそれらのコンポーネントが表示されます。

分割カタログをインストールするには

  • ダウンロード サイトから Sharding Catalog Setup Scripts.zip ファイルをダウンロードします。

  • SQL Server データベース、または設計され、実行されるパッケージで使用できる Azure SQL データベースで、ShardingSchema.sql スクリプトを実行し、次に ShardingStoredProcedures.sql スクリプトを実行します。これら 2 つのスクリプトには、有効な分割カタログの設定を支援するストアド プロシージャと共に、分割カタログのテーブル定義が含まれています。

  • TestAndExamples.sql スクリプトには、ストアド プロシージャを使用して分割カタログを設定する方法の例が含まれています。

コンポーネントと分割カタログをインストールしたら、コンポーネントを使用して、任意の OLE DB 互換データ ストアの分割データの変換先に接続し、データを移動できます。

分割の変換先を使用して、データの変換先を手動で入力できます。ただし、パッケージを再開発せずに変換先の分割スキームを変更する機能の完全な利点を得るには、分割カタログを設定する必要があります。ここでは、ストアド プロシージャを使用して分割カタログを設定する手順について説明します。

完全に設定された分割カタログには、次の要素が含まれています。

  1. 1 つ以上のデータベース サーバー。分割カタログでは、分割スキームで使用するデータベースを含む各サーバーを入力する必要があります。この情報はサーバーの配置方法であり、接続文字列を構築するために分割コンポーネントが使用するサーバー名が含まれます。

  2. セットアップする必要がある 1 つ以上のデータベース。各データベースは、分割カタログで入力したサーバーに存在する必要があります。分割の変換先コンポーネントはデータベース名を使用して必要な接続を確立します。

  3. 作成する必要がある少なくとも 1 つの分割セット。分割セットは、データの配布に使用する分割スキームを識別する方法です。分割セットは分割されたデータ ストアとなることが最適です。

  4. 各分割セットに対して作成する必要がある、少なくとも 1 つの分割ディストリビューション。このディストリビューションは、分割スキームをポイントまたは範囲として定義し、分割キーとして使用される列と、その列のデータ型を定義します。また、FLOATDOUBLE などのデータ型が使用される場合に、列に関する情報を提供します。

  5. 各分割ディストリビューションに対して設定する必要があるメンバー。このメンバーシップは、特定の値を含むどのテーブルとデータベースの行が送信されるかを定義します。各分割メンバーはデータベースに関連している必要があります。

次に完全な設定の例を示します。

-- First, add the server.
DECLARE @srvid INT

EXEC [ShardMeta].[AddServerToShardingScheme] 
@ServerName= 'changed.database.windows.net'
,@Descr= 'Destination Server for the Demo'
,@ServerID = @srvid OUTPUT

-- Next, add the shard databases.

DECLARE @shard0ID INT
DECLARE @shard1ID INT
DECLARE @shard2ID INT
DECLARE @shard3ID INT
DECLARE @shard4ID INT
DECLARE @shard5ID INT
DECLARE @shard6ID INT
DECLARE @shard7ID INT
DECLARE @shard8ID INT
DECLARE @shard9ID INT
DECLARE @shard10ID INT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD000'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard0ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD001'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard1ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD002'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard2ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD003'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard3ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD004'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard4ID OUTPUT


EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD005'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard5ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD006'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard6ID OUTPUT


EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD007'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard7ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD008'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard8ID OUTPUT

EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD009'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard9ID OUTPUT


EXEC [ShardMeta].[AddDatabaseToShardingScheme]
@ServerID= @srvid
,@DatabaseName= 'SHARD010'
,@Descr= 'Destination Database for the Demo'
,@DatabaseID= @shard10ID OUTPUT

-- Next, add the shard set.

DECLARE @ShardSID INT

EXEC [ShardMeta].[AddShardSetToShardingScheme]
@name= 'Point Distro For demo'
,@Descr= 'Point Distribution for Demo'
,@ShardSetID= @ShardSID OUTPUT

-- Add a distribution to the shard set.

-- There are only two types of @Distribution_type:
-- POINT and RANGE
-- POINT means only one value per shard. Example: col1 = 1 or col1 = 2
-- RANGE means a range goes into the shard. Example: -1 < col1 <= 2
-- When @Boundary_value_in_high, the upper bound of a range is included
-- in the shard, but not the lower. So when this is set to 1,
-- a range of -1 to 2 would be: -1 < col1 <= 2
-- Conversely, if it is set to 0, the range from -1 to 2 would be
-- defined as: -1 <= col1 < 2
-- If the sharding column is a system data type, both the
-- @System_Type_Name and @User_type_name parameters contain the system data type name.
-- If the user type is different, name the type name in the @User_Type_Name 
-- parameter.


EXEC [ShardMeta].[AddShardDistributionToShardingScheme]
@ShardSetID= @ShardSID
,@Distribution_Name= 'Point Distro For demo'
,@Distribution_Type= 'POINT'
,@System_Type_Name= 'INT'
,@User_type_name= 'INT'
,@Boundary_value_in_high = 1

-- Add in the members for the “Point Distro For demo” distribution.

-- @MemberID must be unique within each @ShardSetID.
-- The order of MemberID is the order in which the conditions will be
-- evaluated for placement into a shard.
-- @Name is the case-sensitive name of the column as it
-- will appear in the SSIS dataflow. If this is the name
-- of the column in the source, nothing must be done
-- to change this in the SSIS dataflow. However, if it does
-- not exist in the source, use a derived column to create the 
-- column in the dataflow before it reaches the sharding destination.
-- For POINT distributions, @Range_Low and @Range_High are the same.


EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 0
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard0ID
,@TableName= 'dbo.TestData'
,@Range_Low= 0
,@Range_High= 0

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 1
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard1ID
,@TableName= 'dbo.TestData'
,@Range_Low= 1
,@Range_High= 1

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 2
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard2ID
,@TableName= 'dbo.TestData'
,@Range_Low= 2
,@Range_High= 2

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 3
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard3ID
,@TableName= 'dbo.TestData'
,@Range_Low= 3
,@Range_High= 3

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 4
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard4ID
,@TableName= 'dbo.TestData'
,@Range_Low= 4
,@Range_High= 4

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 5
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard5ID
,@TableName= 'dbo.TestData'
,@Range_Low= 5
,@Range_High= 5

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 6
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard6ID
,@TableName= 'dbo.TestData'
,@Range_Low= 6
,@Range_High= 6

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 7
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard7ID
,@TableName= 'dbo.TestData'
,@Range_Low= 7
,@Range_High= 7

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 8
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard8ID
,@TableName= 'dbo.TestData'
,@Range_Low= 8
,@Range_High= 8

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 9
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard9ID
,@TableName= 'dbo.TestData'
,@Range_Low= 9
,@Range_High= 9

EXEC [ShardMeta].[AddShardSetMemberToShardingScheme]
@ShardSetID= @ShardSID
,@MemberID= 10
,@Distribution_Name= 'Point Distro For demo'
,@Name= 'ShardingKey'
,@DatabaseID= @Shard10ID
,@TableName= 'dbo.TestData'
,@Range_Low= 10
,@Range_High= 10

デモのデータセットでは、11 個の分割データベースが設定されています。各分割データベースは別のデータベースにあります。1 つの分割セットと分割ディストリビューションが設定されています。データは ShardingKey で分割されます (大文字と小文字が区別されます)。データのダイレクト先で、各分割に対して 1 つの分割セット メンバーが設定されます。このスキームでは、0 ~ 10 の ShardingKey が変換先に送信されます。

ストアド プロシージャの使用を強くお勧めします。ただし、データを入力するために各ストアド プロシージャが実行する検証のため、ストアド プロシージャによって入力されるデータをカタログ テーブルで見つけ、ストアド プロシージャなしで変更することができます。ストアド プロシージャに入力したデータを見つけるには、ShardMeta スキーマのテーブルを調べます。

分割カタログを設定し、実際のデータベースを指定すると、そのカタログをデータ転送用に使用できます。ここでは、分割コンポーネントと分割カタログを使用するパッケージの作成例を示します。

この例では、変換元テーブルと変換先テーブルには、次に定義するようなスキーマがあります。

CREATE TABLE [dbo].[TestData](
[KeyCol] [bigint] NOT NULL,
[IntCol1] [int] NOT NULL,
[IntCol2] [int] NOT NULL,
[IntCol3] [int] NOT NULL,
[CharCol1] [nvarchar](50) NOT NULL,
[CharCol2] [nvarchar](50) NOT NULL,
[CharCol3] [nvarchar](50) NOT NULL,
[CharCol4] [nvarchar](50) NOT NULL,
PRIMARY KEY CLUSTERED 
(
[KeyCol] ASC
)
)

このテーブルを作成し、ランダムなデータを入力して、以前に設定した分割カタログ スキームを使用して例の設定を行うことができます。

  1. SQL Server Data Tools を開き、新しい SSIS プロジェクトを作成します。

  2. プロジェクトでパッケージを作成します。

  3. パッケージにデータ フロー タスクを追加し、[データ フロー] タブをクリックします。

  4. ADO.NET ソースを、データ フローのデザイン面にドラッグします。

  5. 接続マネージャーを作成する手順に従って、変換元データ テーブルを指定します。完全に設定したら、接続マネージャーを閉じます。

    noteメモ
    前のセクションでの分割スキームの設定では、分割キーは ShardingKey という名前の列です。このキーは変換元データには存在しませんが、データがデータベース分割変換先に接続する前に、SSIS データ フローに存在する必要があります。派生列を使用して、SSIS データ フローでこの列を作成します。分割キーがデータそのものに存在する場合、この手順は必要ありません。

  6. 派生列の変換を SSIS データ フローのデザイン面にドラッグし、出力を ADO.NET ソースからこのコンポーネントに接続します。

  7. 次のように派生列の変換を構成します。

    1. ShardingKey を派生列の名前として使用します。この名前では大文字と小文字が区別され、分割カタログの分割キー列の名前と一致する必要があります。

    2. 式には、[KeyCol] % 11 を使用します。これにより、分割キーに 0 ~ 10 の可能な値が与えられ、これにより分割カタログが直接行に対して設定されます。

    派生列を設定すると、図 2 のようになります。

    図 2図 2. データ フローに ShardingKey 列を追加し、KeyCol で 11 の剰余演算としてその列を定義する、完全に設定された派生列。

  8. データベース分割変換先をデータ フローのデザイン面にドラッグし、派生列の変換の出力を接続します。

  9. ダブルクリックしてデザインを開き、次の手順を実行します。

    1. [分割カタログに接続してください][新規] をクリックし、手順に従って、以前に作成した分割カタログ データベースを指定する接続マネージャーを定義します。

    2. [分割セットを選択してください] の一覧で、このデモに対して作成した分割セット (Point Distro for demo) を選択します。

    3. [変換先接続マネージャー][新規] をクリックします。新しい分割接続マネージャー (SHARDINGCM) が表示されます。

    4. 変換先データベースへの接続に使用するユーザー名とパスワードを指定します。Azure データベースの場合、セキュリティ サポート プロバイダー インターフェイス (SSPI) を使用することはできません。

      noteメモ
      分割スキームに参加するすべてのサーバーとデータベースに接続するには、1 つのユーザー名とパスワードのみを定義するだけで済みます。ユーザー名とパスワードが存在し、各データベースに十分な特権があることを確認します。

      noteメモ
      このデモではユーザー名とパスワードを直接設定しますが、それらはパラメーター化可能です。SSIS の他の接続マネージャーのユーザー名とパスワードも同様です。ユーザー名とパスワードはパラメーター化可能ですが、開発後にパッケージを移動するか、実行時にログイン情報を変更できます。

      図 3 に、上記の手順を実行後の分割変換先アダプターのエディターの外観を示します。

      図 3図 3. 分割変換先アダプターのページ 1 の例。drxufmq90b.database.windows.net.ShardCatalog.AzureCat 接続マネージャーは分割カタログを指定しています。使用されている分割セットの名前は Point Distro For Azure CAT Summit です。SQL Sharding Connection Manager という名前の接続マネージャーには、分割スキームのすべての変換先データベースへの接続に使用されるユーザー名とパスワードが含まれます。

    5. [OK] をクリックして分割変換先接続マネージャーの設定を終了します。

    6. 分割変換先アダプターのエディターの左ペインで、[接続] をクリックします。この時点で、図 4 に示すようにエディターは分割カタログに接続し、分割カタログで現在定義されている変換先を表示します。これは分割カタログに現在存在する唯一のデータです。このデータは設計後に変更できます。実行ごとに、コンポーネントは分割カタログに接続して、カタログの現在の状態を判断するためです。ほとんどの場合は、[一括挿入を使用する] チェック ボックスをオンにしますが、個別に行を挿入して、可能性のあるプライマリ キーの違反を処理することをお勧めします。

      図 4図 4. 分割変換先アダプターの接続ページ。式とテーブルの情報は、すべてページ 1 で定義された分割カタログから取得されています。

    7. 左ペインで、[マッピング] をクリックします。コンポーネントは、図 5 で示すように、同じ名前の列を自動的にマップします。ただし、列に等しくない名前がある場合は、手動でマップできます。この場合、ShardingKey を除くすべての列がマップされます。ShardingKey は変換先テーブルに存在せず、分割変換先コンポーネントが正しい分割データベースに行をダイレクトできるようにするためだけに、データ フローに存在します。

      図 5図 5. 分割変換先アダプターの [マッピング] ページ。

    8. [OK] をクリックしてエディターを終了します。

  10. データベース分割変換先アダプターを選択し、SQL Server Data Tools の [プロパティ] タブをクリックします。カスタム プロパティで、特に FaultToleranceRetryStrategy に注目します。一時的なエラー処理が分割変換先に組み込まれます。ここでプロパティを設定し、再試行方法、試行する再試行の数、および再試行の間隔を変更できます。これらはすべてカスタム プロパティとして設定されるので、実行時に変更できるようにパラメーター化可能です。

この時点で、パッケージは実行または展開準備が整います。分割データベースを追加または削除する場合、または実行ごとに分割データベースを変更する場合、次回にパッケージを実行するときに、新しい分割構成が読み取られます。データは、実行開始時にカタログに存在するデータベースとテーブルに送信されます。

データベース分割変換先に加えて、コンポーネントを使用して Azure Blob ストレージにデータを送信したり、Azure Blob ストレージからデータを取得したりできます。これらは Blob コピー元および Blob コピー先コンポーネントです。

Blob 変換先アダプターの情報の入力を完了するには、Azure ストレージ アカウントおよびキー、非圧縮データの変換先分割サイズ、およびファイルを保存する基本ファイル名を指定します。コンテナー リストを使用して変換先コンテナーを選択します。Blob 存在リストでは、名前の競合を避けるため、選択されたコンテナーに既に格納されているファイルが表示されます。エディターに情報を入力すると、図 6 のようになります。

図 6

図 6. SSIS の Azure Blob コピー先の例。

SSIS パッケージを実行すると、Blob コピー先はデータを分割し、指定された Blob コンテナーで GNU zip (gzip) ファイルを作成します。また、指定されたファイル名と同じ名前を持つストレージ アカウントでキューを作成します。Blob コピー先は、圧縮されたファイルをさらに処理する必要があるときに、作成された各ファイルの名前をこのキューに配置します。Neudesic Azure ストレージ エクスプローラーなどのツールを使用して、キューとそのコンテンツを表示するか、最初からやり直す必要がある場合はキューをクリアします。

Blob コピー元コンポーネントは、Blob コピー先コンポーネントが作成したデータ ファイル用のソースとなります。Blob コピー元コンポーネントを使用するには、[ストレージ アカウント名] ボックスおよび [ストレージ アカウント キー] ボックスに情報を入力します。次に、コンテナーの一覧が自動的に入力されます。コンテナーを選択し、キューからの取得が必要かどうかを選択します (すべての場合に、このオプションをお勧めします。このオプションは、同じデータ ファイルのセットに対して複数のインスタンスのパッケージを実行する場合に、必ず必要になります)。図 7 に例を示します。

図 7

図 7:

Blob ストレージのコピー先は圧縮され、区切られたファイルを Blob ストレージに保存するため、HDInsight は作成されたファイルを直接使用できます。各行は行区切りタグで区切られ、各列は列の区切りタグで区切られます。列ヘッダーは、ファイルが格納されるコンテナーに読み込まれる列ヘッダー ファイルに格納されます。

この情報は役に立ちましたか。
(残り 1500 文字)
フィードバックをいただき、ありがとうございました
表示:
© 2015 Microsoft