mcpField
D
data-import

data-import

github.com/happy-technologies-llcデータ・データベース4143 回インストール3 日前に更新
リポジトリ

data-import について

Import SetsとTransform Mapsを使ったServiceNowのデータインポートを解説するスキルです。開発者、管理者、連携担当者、データ移行チーム向けに、データソース設定、フィールドマッピング、重複排除、エラー処理、定期実行を扱います。

主な機能

  1. 01ファイル、JDBC、LDAP、RESTソースの設定
  2. 02ステージングテーブルとTransform Mapの作成
  3. 03フィールドマッピングと変換スクリプトの設定
  4. 04Coalesceによる照合と重複排除
  5. 05エラー処理と失敗行の再試行
  6. 06定期インポートの設定

活用シーン

  1. 01外部システムのデータをServiceNowに取り込む
  2. 02データを移行してターゲットテーブルに対応付ける
  3. 03定期同期を設定し失敗行を処理する

MCP 対応

データソース、インポートテーブル、マッピングの設定や、レコードの照会・一括操作にServiceNowのMCPツールを使う例を示します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/happy-technologies-llc/happy-platform-skills --skill data-import

インストール後、Claude Code で/data-importを入力して読み込まれたか確認します。

詳細

データインポート

概要

このスキルでは、Import Sets と Transform Maps を使用した ServiceNow におけるデータインポートのライフサイクル全体を扱います。

  • Import Set のアーキテクチャを理解する
  • データソース(ファイル、JDBC、LDAP、REST)を構成する
  • Transform Map を作成して構成する
  • フィールドマッピングの方法と変換
  • 変換スクリプト(onBefore、onAfter、onStart、onComplete)
  • 照合と重複排除のための Coalesce フィールド
  • エラー処理とロールバック戦略
  • スケジュールされたインポートと自動化
  • 大規模データセットのパフォーマンス最適化

使用する場面: 外部データを ServiceNow にインポートする場合、ETL 処理を実行する場合、システム間でデータを移行する場合、または定期的なデータ同期を設定する場合です。

対象ユーザー: 開発者、管理者、インテグレーションの専門家、データ移行チームです。

前提条件

  • ロール: import_admin、import_transformer、または admin
  • アクセス権: 対象テーブル、インポートセットテーブル、データソース構成
  • 知識: ServiceNow のデータモデル、GlideRecord API、テーブルのリレーションシップ
  • 関連スキル:
    • admin/generic-crud-operations - 基本的な CRUD 操作
    • admin/batch-operations - 一括データ処理

Import Set のアーキテクチャ

主要コンポーネント
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Data Source   │───>│   Import Set     │───>│  Transform Map  │
│  (File/JDBC/    │    │    (Staging)     │    │   (Mapping)     │
│   LDAP/REST)    │    │                  │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                       │
                                                       v
                                               ┌─────────────────┐
                                               │  Target Table   │
                                               │   (Production)  │
                                               └─────────────────┘
コアテーブル
テーブル用途
sys_import_setインポートセットのヘッダーレコード
sys_import_set_rowインポートデータのステージングテーブル
sys_transform_mapTransform Map の定義
sys_transform_entryフィールドマッピングのエントリ
sys_transform_script変換スクリプト(onBefore など)
sys_data_sourceデータソースの構成
インポートの状態
状態値説明
読み込み済みloadedデータがステージングに読み込まれた状態
変換済みtransformed変換に成功した状態
エラーerror変換に失敗した状態
無視ignored変換ロジックによってスキップされた状態

手順

フェーズ 1:データソースの構成
ステップ 1.1:ファイルデータソースを作成する

CSV、Excel、XML ファイルのインポートに使用します。

MCP を使用:

Tool: SN-Create-Record
Parameters:
  table_name: sys_data_source
  data:
    name: Employee Import - CSV
    type: File
    format: CSV
    header_row: 1
    sheet_number: 1
    import_set_table_name: u_employee_import
    active: true

応答:

{
  "sys_id": "abc123...",
  "name": "Employee Import - CSV",
  "type": "File",
  "import_set_table_name": "u_employee_import"
}
ステップ 1.2:JDBC データソースを作成する

データベース接続(Oracle、MySQL、SQL Server)に使用します。

Tool: SN-Create-Record
Parameters:
  table_name: sys_data_source
  data:
    name: HR Database - JDBC
    type: JDBC
    connection_url: jdbc:mysql://hr-db.company.com:3306/hrms
    user: servicenow_reader
    password: [encrypted_password]
    import_set_table_name: u_hr_import
    query: |
      SELECT employee_id, first_name, last_name, email, department, hire_date
      FROM employees
      WHERE modified_date > ?
    active: true

JDBC 接続 URL のパターン:

データベース接続 URL
MySQLjdbc:mysql://host:3306/database
Oraclejdbc:oracle:thin:@host:1521:sid
SQL Serverjdbc:sqlserver://host:1433;databaseName=db
PostgreSQLjdbc:postgresql://host:5432/database
ステップ 1.3:LDAP データソースを作成する

Active Directory または LDAP ディレクトリのインポートに使用します。

Tool: SN-Create-Record
Parameters:
  table_name: sys_data_source
  data:
    name: Active Directory Users
    type: LDAP
    server_url: ldap://ad.company.com:389
    user: CN=ServiceNow,OU=Service Accounts,DC=company,DC=com
    password: [encrypted_password]
    import_set_table_name: u_ldap_user_import
    ldap_target: OU=Users,DC=company,DC=com
    ldap_filter: (&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
    active: true
ステップ 1.4:REST データソースを作成する

REST API の連携に使用します。

Tool: SN-Create-Record
Parameters:
  table_name: sys_data_source
  data:
    name: External API - REST
    type: REST (IntegrationHub)
    connection_url: https://api.external-system.com/v1/records
    http_method: GET
    authentication_type: basic
    user: api_user
    password: [encrypted_password]
    import_set_table_name: u_api_import
    format: JSON
    active: true
フェーズ 2:インポートセットテーブルの作成
ステップ 2.1:カスタムインポートセットテーブルを作成する

インポートデータを受け取るステージングテーブルを作成します。

MCP を使用:

Tool: SN-Create-Record
Parameters:
  table_name: sys_db_object
  data:
    name: u_employee_import
    label: Employee Import
    extends: sys_import_set_row
    create_access: true
    read_access: true
    update_access: true
    delete_access: true
ステップ 2.2:インポートセットテーブルに列を追加する

ソースデータの構造に合わせて列を定義します。

列を一括作成:

Tool: SN-Batch-Create
Parameters:
  records:
    - table_name: sys_dictionary
      data:
        name: u_employee_import
        element: u_employee_id
        column_label: Employee ID
        internal_type: string
        max_length: 40
    - table_name: sys_dictionary
      data:
        name: u_employee_import
        element: u_first_name
        column_label: First Name
        internal_type: string
        max_length: 100
    - table_name: sys_dictionary
      data:
        name: u_employee_import
        element: u_last_name
        column_label: Last Name
        internal_type: string
        max_length: 100
    - table_name: sys_dictionary
      data:
        name: u_employee_import
        element: u_email
        column_label: Email
        internal_type: string
        max_length: 255
    - table_name: sys_dictionary
      data:
        name: u_employee_import
        element: u_department
        column_label: Department
        internal_type: string
        max_length: 100
    - table_name: sys_dictionary
      data:
        name: u_employee_import
        element: u_hire_date
        column_label: Hire Date
        internal_type: string
        max_length: 40
フェーズ 3:Transform Map の構成
ステップ 3.1:Transform Map を作成する

ステージングデータを対象テーブルに変換する方法を定義します。

MCP を使用:

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_map
  data:
    name: Employee Import Transform
    source_table: u_employee_import
    target_table: sys_user
    active: true
    enforce_mandatory_fields: true
    run_business_rules: true
    run_script: true
    order: 100

Transform Map のオプション:

オプション説明
enforce_mandatory_fields必須フィールドが欠落している場合に失敗する
run_business_rules対象テーブルでビジネスルールを実行する
run_script変換スクリプトを実行する
copy_empty_fields空の値で上書きする
order実行順序(数値が小さいほど先に実行)
ステップ 3.2:フィールドマッピングを作成する

ソース列を対象フィールドにマッピングします。

フィールドマッピングを一括作成:

Tool: SN-Batch-Create
Parameters:
  records:
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_employee_id
        target_field: employee_number
        coalesce: true
        order: 100
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_first_name
        target_field: first_name
        order: 200
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_last_name
        target_field: last_name
        order: 300
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_email
        target_field: email
        coalesce: true
        order: 400
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_department
        target_field: department
        reference_qual_mapping: true
        order: 500
ステップ 3.3:フィールドマッピングの種類
種類用途設定
Directシンプルなコピー変換せずにソースからターゲットへコピー
Mapping値の変換選択肢マップまたはスクリプトを使用
Reference参照関係の検索reference_qual_mapping: true を設定
Script複雑なロジックsource_script フィールドを使用
Derived計算値ソースは不要で、スクリプトのみ

スクリプトマッピングの例:

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_entry
  data:
    map: [transform_map_sys_id]
    source_field: u_status
    target_field: active
    use_source_script: true
    source_script: |
      // Convert status to boolean active flag
      answer = (source.u_status == 'Active' || source.u_status == 'A') ? 'true' : 'false';
    order: 600

ルックアップを使用した参照マッピング:

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_entry
  data:
    map: [transform_map_sys_id]
    source_field: u_manager_email
    target_field: manager
    reference_qual_mapping: true
    reference_qual: email=[u_manager_email]
    order: 700
フェーズ 4:コアレッセンスフィールド(一致判定)
ステップ 4.1:コアレッセンスについて

コアレッセンスフィールドによって、変換で INSERT と UPDATE のどちらを行うかが決まります。

  • 一致なし: 新しいレコードを INSERT
  • 一致が 1 件: 既存のレコードを UPDATE
  • 一致が複数: エラー(別途設定されている場合を除く)
ステップ 4.2:コアレッセンスフィールドを設定する

単一のコアレッセンスフィールド:

Tool: SN-Update-Record
Parameters:
  table_name: sys_transform_entry
  sys_id: [entry_sys_id]
  data:
    coalesce: true

複数のコアレッセンスフィールド(複合キー):

Tool: SN-Batch-Update
Parameters:
  updates:
    - table_name: sys_transform_entry
      sys_id: [employee_id_entry_sys_id]
      data:
        coalesce: true
    - table_name: sys_transform_entry
      sys_id: [company_entry_sys_id]
      data:
        coalesce: true

コアレッセンスの動作マトリックス:

シナリオ動作
コアレッセンスフィールドなし常に新しいレコードを INSERT
コアレッセンスあり、一致なし新しいレコードを INSERT
コアレッセンスあり、一致が 1 件既存のレコードを UPDATE
コアレッセンスあり、一致が複数ERROR(設定可能)
ステップ 4.3:複数一致を処理する

複数一致を処理するように変換マップを設定します。

Tool: SN-Update-Record
Parameters:
  table_name: sys_transform_map
  sys_id: [transform_map_sys_id]
  data:
    multi_coalesce_action: ignore

複数コアレッセンス時のアクション:

アクション動作
create新しいレコードを作成
ignore行をスキップし、無視としてマーク
update_first最初の一致を更新
reject行をエラーとしてマーク
フェーズ 5:変換スクリプト
ステップ 5.1:スクリプトの種類の概要
スクリプトの種類実行タイミング用途
onStart変換開始前カウンターの初期化、検証
onBefore各行の処理前行単位の前処理
onAfter各行の処理後後処理、関連レコードの処理
onComplete変換終了後概要、通知
onChoiceCreate選択肢の作成時カスタム選択肢の作成
onForeignInsert参照の挿入時欠落している参照の処理
ステップ 5.2:onStart スクリプトを作成する

変換の開始時に 1 回実行されます。

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_script
  data:
    map: [transform_map_sys_id]
    script_type: onStart
    script: |
      // onStart: Initialize transform
      // Available: log, source (first row), map, import_set

      log.info('Starting employee import transform');
      log.info('Import Set: ' + import_set.number);
      log.info('Source table: ' + map.source_table);

      // Initialize counters in scratchpad
      var scratchpad = {};
      scratchpad.processed = 0;
      scratchpad.created = 0;
      scratchpad.updated = 0;
      scratchpad.errors = 0;
      scratchpad.startTime = new GlideDateTime();
    order: 100
    active: true
ステップ 5.3:onBefore スクリプトを作成する

各行の変換前に実行されます。

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_script
  data:
    map: [transform_map_sys_id]
    script_type: onBefore
    script: |
      // onBefore: Row-level preprocessing
      // Available: source, target, map, log, action, error, ignore
      // Set ignore=true to skip row, error=true to mark as error

      // Validate required fields
      if (!source.u_employee_id || source.u_employee_id.nil()) {
        error = true;
        error_message = 'Missing employee ID';
        return;
      }

      if (!source.u_email || source.u_email.nil()) {
        error = true;
        error_message = 'Missing email address';
        return;
      }

      // Validate email format
      var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(source.u_email.toString())) {
        error = true;
        error_message = 'Invalid email format: ' + source.u_email;
        return;
      }

      // Normalize data
      source.u_first_name = source.u_first_name.toString().trim();
      source.u_last_name = source.u_last_name.toString().trim();
      source.u_email = source.u_email.toString().toLowerCase().trim();

      // Generate username if not provided
      if (!source.u_user_name || source.u_user_name.nil()) {
        source.u_user_name = source.u_email.toString().split('@')[0];
      }

      // Conditional skip
      if (source.u_status == 'Terminated') {
        ignore = true;
        return;
      }

      scratchpad.processed++;
    order: 100
    active: true
ステップ 5.4:onAfter スクリプトを作成する

各行の変換後に実行されます。

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_script
  data:
    map: [transform_map_sys_id]
    script_type: onAfter
    script: |
      // onAfter: Post-processing
      // Available: source, target, map, log, action, error, scratchpad
      // action = 'insert', 'update', or 'ignore'

      if (action == 'insert') {
        scratchpad.created++;

        // Add user to default groups
        if (target.sys_id) {
          addUserToGroups(target.sys_id, source.u_department);
        }

        log.info('Created user: ' + target.user_name);

      } else if (action == 'update') {
        scratchpad.updated++;
        log.info('Updated user: ' + target.user_name);
      }

      // Create related records
      if (source.u_manager_email && !source.u_manager_email.nil()) {
        // Store for later processing
        scratchpad.managersToProcess = scratchpad.managersToProcess || [];
        scratchpad.managersToProcess.push({
          userId: target.sys_id.toString(),
          managerEmail: source.u_manager_email.toString()
        });
      }

      function addUserToGroups(userId, department) {
        var deptGroups = {
          'IT': ['IT Support', 'Service Desk'],
          'HR': ['HR Team'],
          'Finance': ['Finance Team']
        };

        var groups = deptGroups[department] || [];

        groups.forEach(function(groupName) {
          var group = new GlideRecord('sys_user_group');
          group.addQuery('name', groupName);
          group.query();

          if (group.next()) {
            var member = new GlideRecord('sys_user_grmember');
            member.addQuery('user', userId);
            member.addQuery('group', group.sys_id);
            member.query();

            if (!member.hasNext()) {
              member.initialize();
              member.user = userId;
              member.group = group.sys_id;
              member.insert();
            }
          }
        });
      }
    order: 100
    active: true
ステップ 5.5:onComplete スクリプトを作成する

すべての行の変換後に 1 回実行されます。

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_script
  data:
    map: [transform_map_sys_id]
    script_type: onComplete
    script: |
      // onComplete: Finalize transform
      // Available: log, import_set, map, scratchpad

      var endTime = new GlideDateTime();
      var duration = GlideDateTime.subtract(scratchpad.startTime, endTime);
      var durationSec = duration.getNumericValue() / 1000;

      var summary = {
        processed: scratchpad.processed || 0,
        created: scratchpad.created || 0,
        updated: scratchpad.updated || 0,
        errors: scratchpad.errors || 0,
        duration: durationSec.toFixed(2) + ' seconds'
      };

      log.info('Transform Complete: ' + JSON.stringify(summary));

      // Process manager relationships (deferred to avoid reference issues)
      if (scratchpad.managersToProcess && scratchpad.managersToProcess.length > 0) {
        scratchpad.managersToProcess.forEach(function(item) {
          var manager = new GlideRecord('sys_user');
          manager.addQuery('email', item.managerEmail);
          manager.query();

          if (manager.next()) {
            var user = new GlideRecord('sys_user');
            if (user.get(item.userId)) {
              user.manager = manager.sys_id;
              user.update();
            }
          }
        });
        log.info('Processed ' + scratchpad.managersToProcess.length + ' manager relationships');
      }

      // Send notification if errors occurred
      if (summary.errors > 0) {
        gs.eventQueue('import.transform.errors', import_set, summary.errors, JSON.stringify(summary));
      }

      // Update import set with summary
      import_set.description = 'Summary: ' + JSON.stringify(summary);
      import_set.update();
    order: 100
    active: true
ステップ 5.6:onForeignInsert スクリプト

欠落している参照値を処理します。

Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_script
  data:
    map: [transform_map_sys_id]
    script_type: onForeignInsert
    script: |
      // onForeignInsert: Handle missing references
      // Available: source, target_table, target_field, source_field, source_value, log
      // Return: sys_id of existing/created record, or ignore to skip

      // Handle department lookup - create if not exists
      if (target_table == 'cmn_department' && target_field == 'department') {
        var dept = new GlideRecord('cmn_department');
        dept.addQuery('name', source_value);
        dept.query();

        if (dept.next()) {
          return dept.sys_id;
        } else {
          // Create new department
          dept.initialize();
          dept.name = source_value;
          dept.primary_contact = ''; // Set to admin later
          var newId = dept.insert();
          log.info('Created department: ' + source_value);
          return newId;
        }
      }

      // Handle company lookup - ignore if not found
      if (target_table == 'core_company' && target_field == 'company') {
        log.warn('Company not found: ' + source_value);
        ignore = true;
        return;
      }

      // Default: skip the field
      ignore = true;
    order: 100
    active: true
フェーズ 6:エラー処理
ステップ 6.1:変換レベルのエラー処理

変換マップのエラー動作を設定します。

Tool: SN-Update-Record
Parameters:
  table_name: sys_transform_map
  sys_id: [transform_map_sys_id]
  data:
    abort_on_error: false
    log_transform_messages: true

エラー設定オプション:

オプション説明
abort_on_error最初のエラーで変換を停止
log_transform_messages詳細なログを記録
copy_empty_fields空の値を含める
ステップ 6.2:エラー行を検索する

失敗した行を検索して分析します。

MCP を使用:

Tool: SN-Query-Table
Parameters:
  table_name: sys_import_set_row
  query: sys_import_set=[import_set_sys_id]^sys_import_state=error
  fields: sys_id,sys_row_error,sys_import_state,sys_transform_map
  limit: 100
ステップ 6.3:失敗した行を再試行する

問題を修正した後、エラー行を再処理します。

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Retry failed import set rows
    var importSetId = '[import_set_sys_id]';
    var transformMapId = '[transform_map_sys_id]';

    var gr = new GlideRecord('sys_import_set_row');
    gr.addQuery('sys_import_set', importSetId);
    gr.addQuery('sys_import_state', 'error');
    gr.query();

    var retried = 0;
    var transformer = new GlideImportSetTransformer();

    while (gr.next()) {
      // Reset state
      gr.sys_import_state = 'pending';
      gr.sys_row_error = '';
      gr.update();

      // Retransform single row
      transformer.transformRow(gr, transformMapId);
      retried++;
    }

    gs.info('Retried ' + retried + ' rows');
  description: Retry failed import rows
ステップ 6.4:包括的なエラーログ記録

エラー追跡テーブルを作成し、ログを記録します。

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Enhanced error tracking for imports
    function logImportError(source, target, errorMsg, context) {
      var log = new GlideRecord('u_import_error_log');
      log.initialize();
      log.u_import_set = context.importSetId;
      log.u_transform_map = context.transformMapId;
      log.u_source_row = source.sys_id;
      log.u_source_data = JSON.stringify({
        employee_id: source.u_employee_id.toString(),
        email: source.u_email.toString(),
        name: source.u_first_name + ' ' + source.u_last_name
      });
      log.u_error_message = errorMsg;
      log.u_timestamp = new GlideDateTime();
      log.insert();

      return log.sys_id;
    }

    // Usage in onBefore script:
    // if (validationFailed) {
    //   logImportError(source, target, 'Validation failed: missing email', {
    //     importSetId: import_set.sys_id,
    //     transformMapId: map.sys_id
    //   });
    //   error = true;
    // }

    gs.info('Error logging function defined');
  description: Define import error logging function
フェーズ 7:スケジュールされたインポート
ステップ 7.1:スケジュールされたインポートを作成する

定期的なデータインポートを設定します。

Tool: SN-Create-Record
Parameters:
  table_name: scheduled_import_set
  data:
    name: Daily Employee Sync
    data_source: [data_source_sys_id]
    transform_map: [transform_map_sys_id]
    run_type: daily
    run_time: "02:00:00"
    run_dayofweek: "*"
    active: true
    delete_on_success: true
    email_on_error: [email protected]

実行タイプのオプション:

種類説明追加フィールド
on_demand手動実行なし
daily1 日に 1 回run_time
weekly週に 1 回run_time、run_dayofweek
monthly月に 1 回run_time、run_dayofmonth
periodically固定間隔run_period(分)
ステップ 7.2:インポートセット実行スクリプトを作成する

カスタムスケジュールや複雑なワークフローに使用します。

Tool: SN-Create-Record
Parameters:
  table_name: sysauto_script
  data:
    name: Employee Import - Custom Schedule
    script: |
      // Custom scheduled import with pre/post processing
      var startTime = new GlideDateTime();
      gs.info('Starting scheduled employee import');

      try {
        // Pre-import validation
        if (!validateSourceConnection()) {
          gs.error('Source connection validation failed - aborting import');
          return;
        }

        // Run import
        var dataSourceId = '[data_source_sys_id]';
        var transformMapId = '[transform_map_sys_id]';

        // Load data into import set
        var loader = new GlideImportSetLoader();
        var importSetId = loader.loadAttachment(dataSourceId);

        if (!importSetId) {
          gs.error('Failed to load import set');
          return;
        }

        // Transform data
        var transformer = new GlideImportSetTransformer();
        transformer.setImportSetID(importSetId);
        transformer.setTransformMapID(transformMapId);
        transformer.transform();

        // Post-import processing
        var stats = getImportStats(importSetId);
        gs.info('Import complete: ' + JSON.stringify(stats));

        // Send summary notification
        if (stats.errors > 0) {
          sendErrorNotification(stats);
        }

        // Cleanup old import sets (keep last 7 days)
        cleanupOldImportSets(7);

      } catch (e) {
        gs.error('Scheduled import failed: ' + e.message);
      }

      function validateSourceConnection() {
        // Add connection validation logic
        return true;
      }

      function getImportStats(importSetId) {
        var stats = { total: 0, inserted: 0, updated: 0, errors: 0, ignored: 0 };

        var ga = new GlideAggregate('sys_import_set_row');
        ga.addQuery('sys_import_set', importSetId);
        ga.addAggregate('COUNT');
        ga.groupBy('sys_import_state');
        ga.query();

        while (ga.next()) {
          var state = ga.sys_import_state.toString();
          var count = parseInt(ga.getAggregate('COUNT'));
          stats.total += count;

          if (state == 'inserted') stats.inserted = count;
          else if (state == 'updated') stats.updated = count;
          else if (state == 'error') stats.errors = count;
          else if (state == 'ignored') stats.ignored = count;
        }

        return stats;
      }

      function sendErrorNotification(stats) {
        gs.eventQueue('import.scheduled.errors', null, stats.errors, JSON.stringify(stats));
      }

      function cleanupOldImportSets(daysToKeep) {
        var gr = new GlideRecord('sys_import_set');
        gr.addQuery('sys_created_on', '<', gs.daysAgo(daysToKeep));
        gr.addQuery('state', 'processed');
        gr.query();

        var deleted = 0;
        while (gr.next()) {
          gr.deleteRecord();
          deleted++;
        }

        gs.info('Cleaned up ' + deleted + ' old import sets');
      }
    run_type: daily
    run_time: "02:00:00"
    active: true
フェーズ 8:パフォーマンスの最適化
ステップ 8.1:大規模インポートのベストプラクティス

インポートセットのチャンク化:

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Process large imports in chunks
    var CONFIG = {
      dataSourceId: '[data_source_sys_id]',
      transformMapId: '[transform_map_sys_id]',
      chunkSize: 5000,
      pauseBetweenChunks: 5000  // milliseconds
    };

    var totalProcessed = 0;
    var chunkNumber = 0;

    while (true) {
      chunkNumber++;
      gs.info('Processing chunk ' + chunkNumber);

      // Load chunk
      var loader = new GlideImportSetLoader();
      loader.setLimit(CONFIG.chunkSize);
      loader.setOffset((chunkNumber - 1) * CONFIG.chunkSize);

      var importSetId = loader.loadAttachment(CONFIG.dataSourceId);

      if (!importSetId) {
        gs.info('No more data to process');
        break;
      }

      // Count rows loaded
      var ga = new GlideAggregate('sys_import_set_row');
      ga.addQuery('sys_import_set', importSetId);
      ga.addAggregate('COUNT');
      ga.query();

      var rowCount = 0;
      if (ga.next()) {
        rowCount = parseInt(ga.getAggregate('COUNT'));
      }

      if (rowCount == 0) {
        gs.info('Empty chunk - import complete');
        break;
      }

      // Transform chunk
      var transformer = new GlideImportSetTransformer();
      transformer.setImportSetID(importSetId);
      transformer.setTransformMapID(CONFIG.transformMapId);
      transformer.transform();

      totalProcessed += rowCount;
      gs.info('Chunk ' + chunkNumber + ' complete: ' + rowCount + ' rows (Total: ' + totalProcessed + ')');

      // Pause between chunks
      gs.sleep(CONFIG.pauseBetweenChunks);
    }

    gs.info('Import complete: ' + totalProcessed + ' total rows processed');
  description: Chunked large import processing
ステップ 8.2:インポート中のビジネスルールを無効化する

大規模なインポートで最大限のパフォーマンスを得るためです。

Tool: SN-Update-Record
Parameters:
  table_name: sys_transform_map
  sys_id: [transform_map_sys_id]
  data:
    run_business_rules: false

警告: その影響を理解している場合にのみ無効化してください。必要に応じて、インポート後に再度有効化してください。

ステップ 8.3:コアレスフィールドにインデックスを設定する

パフォーマンス向上のため、コアレスフィールドにインデックスが設定されていることを確認してください。

Tool: SN-Create-Record
Parameters:
  table_name: sys_index
  data:
    table: sys_user
    field_list: employee_number,email
    unique: true
    active: true
ステップ 8.4:パフォーマンス監視スクリプト
Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Monitor import performance
    var importSetId = '[import_set_sys_id]';

    var importSet = new GlideRecord('sys_import_set');
    if (importSet.get(importSetId)) {
      var stats = {
        number: importSet.number.toString(),
        state: importSet.state.toString(),
        created: importSet.sys_created_on.toString(),
        updated: importSet.sys_updated_on.toString()
      };

      // Calculate duration
      var created = new GlideDateTime(importSet.sys_created_on);
      var updated = new GlideDateTime(importSet.sys_updated_on);
      var duration = GlideDateTime.subtract(created, updated);
      stats.duration_seconds = duration.getNumericValue() / 1000;

      // Get row counts by state
      var ga = new GlideAggregate('sys_import_set_row');
      ga.addQuery('sys_import_set', importSetId);
      ga.addAggregate('COUNT');
      ga.groupBy('sys_import_state');
      ga.query();

      stats.rows = {};
      while (ga.next()) {
        stats.rows[ga.sys_import_state.toString()] = parseInt(ga.getAggregate('COUNT'));
      }

      // Calculate throughput
      var totalRows = 0;
      for (var state in stats.rows) {
        totalRows += stats.rows[state];
      }
      stats.total_rows = totalRows;
      stats.rows_per_second = (totalRows / stats.duration_seconds).toFixed(2);

      gs.info('Import Performance:\n' + JSON.stringify(stats, null, 2));
    }
  description: Monitor import set performance
フェーズ 9:ロールバックとクリーンアップ
ステップ 9.1:挿入したレコードをロールバックする

変換によって作成されたレコードを取り消します。

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Rollback inserted records from import
    var importSetId = '[import_set_sys_id]';
    var targetTable = 'sys_user';
    var DRY_RUN = true;  // Set to false to actually delete

    // Get inserted records
    var gr = new GlideRecord('sys_import_set_row');
    gr.addQuery('sys_import_set', importSetId);
    gr.addQuery('sys_import_state', 'inserted');
    gr.query();

    var toDelete = [];
    while (gr.next()) {
      toDelete.push(gr.sys_target_sys_id.toString());
    }

    gs.info('Found ' + toDelete.length + ' records to rollback');

    if (DRY_RUN) {
      gs.info('[DRY RUN] Would delete: ' + toDelete.join(', '));
    } else {
      var deleted = 0;
      toDelete.forEach(function(sysId) {
        var target = new GlideRecord(targetTable);
        if (target.get(sysId)) {
          target.deleteRecord();
          deleted++;
        }
      });
      gs.info('Deleted ' + deleted + ' records');

      // Update import set rows
      var updateGr = new GlideRecord('sys_import_set_row');
      updateGr.addQuery('sys_import_set', importSetId);
      updateGr.addQuery('sys_import_state', 'inserted');
      updateGr.query();

      while (updateGr.next()) {
        updateGr.sys_import_state = 'rolled_back';
        updateGr.update();
      }
    }
  description: Rollback imported records
ステップ 9.2:インポートセットデータをクリーンアップする

古いインポートセットとステージングデータを削除します。

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Cleanup old import sets
    var DAYS_TO_KEEP = 30;
    var DRY_RUN = true;

    var cutoffDate = gs.daysAgo(DAYS_TO_KEEP);

    // Find old import sets
    var gr = new GlideRecord('sys_import_set');
    gr.addQuery('sys_created_on', '<', cutoffDate);
    gr.addQuery('state', 'processed');
    gr.query();

    var stats = { sets: 0, rows: 0 };

    while (gr.next()) {
      // Count rows
      var ga = new GlideAggregate('sys_import_set_row');
      ga.addQuery('sys_import_set', gr.sys_id);
      ga.addAggregate('COUNT');
      ga.query();

      var rowCount = 0;
      if (ga.next()) {
        rowCount = parseInt(ga.getAggregate('COUNT'));
      }

      if (DRY_RUN) {
        gs.info('[DRY RUN] Would delete: ' + gr.number + ' (' + rowCount + ' rows)');
      } else {
        // Delete rows first
        var rowGr = new GlideRecord('sys_import_set_row');
        rowGr.addQuery('sys_import_set', gr.sys_id);
        rowGr.deleteMultiple();

        // Delete import set
        gr.deleteRecord();
        stats.rows += rowCount;
      }

      stats.sets++;
    }

    gs.info('Cleanup complete: ' + stats.sets + ' import sets, ' + stats.rows + ' rows');
  description: Cleanup old import sets
ステップ 9.3:インポート履歴をアーカイブする

クリーンアップの前にインポート履歴を保存します。

Tool: SN-Execute-Background-Script
Parameters:
  script: |
    // Archive import history before cleanup
    var importSetId = '[import_set_sys_id]';

    var importSet = new GlideRecord('sys_import_set');
    if (importSet.get(importSetId)) {

      // Get statistics
      var stats = {
        number: importSet.number.toString(),
        table_name: importSet.table_name.toString(),
        state: importSet.state.toString(),
        sys_created_on: importSet.sys_created_on.toString(),
        sys_created_by: importSet.sys_created_by.toString()
      };

      // Count by state
      var ga = new GlideAggregate('sys_import_set_row');
      ga.addQuery('sys_import_set', importSetId);
      ga.addAggregate('COUNT');
      ga.groupBy('sys_import_state');
      ga.query();

      stats.results = {};
      while (ga.next()) {
        stats.results[ga.sys_import_state.toString()] = parseInt(ga.getAggregate('COUNT'));
      }

      // Create archive record
      var archive = new GlideRecord('u_import_archive');
      archive.initialize();
      archive.u_import_set_number = stats.number;
      archive.u_source_table = stats.table_name;
      archive.u_import_date = stats.sys_created_on;
      archive.u_imported_by = stats.sys_created_by;
      archive.u_statistics = JSON.stringify(stats);
      archive.insert();

      gs.info('Archived import set: ' + stats.number);
    }
  description: Archive import set history

ツールの使用概要

操作MCP ツール目的
データソースの作成SN-Create-Recordインポート元を設定する
変換マップの作成SN-Create-Recordフィールドマッピングを定義する
フィールドマッピングの作成SN-Batch-Createソースからターゲットへのマッピング
スクリプトの作成SN-Create-Record変換ロジックを追加する
インポート行のクエリSN-Query-Tableインポートの進行状況を監視する
エラーの再試行SN-Execute-Background-Script失敗した行を再処理する
パフォーマンスの確認SN-Execute-Background-Scriptスループットを監視する
ロールバックSN-Execute-Background-Scriptインポートを取り消す

パフォーマンスガイドライン

データ量推奨される方法想定時間
< 1,000標準インポート< 1 分
1,000 - 10,000標準インポート1~10 分
10,000 - 100,000チャンク分割インポート10~60 分
100,000+バックグラウンドジョブ+チャンク分割数時間

パフォーマンスに関するヒント:

  • コアレスフィールドにインデックスを設定する
  • 一括インポートではビジネスルールを無効化する
  • 大規模なデータセットにはチャンク分割処理を使用する
  • オフピーク時間にインポートをスケジュールする
  • インポート中はシステムのパフォーマンスを監視する

ベストプラクティス

  • 必ず最初にテストする: サンプルデータを使い、本番環境以外でインポートを実行します
  • コアレスフィールドを使用する: 適切な照合により重複レコードを防ぎます
  • データを検証する: 変換前に検証するため、onBefore スクリプトを使用します
  • エラーを適切に処理する: 最初のエラーで中断せず、ログに記録して続行します
  • 進行状況を監視する: 行数と処理時間を追跡します
  • クリーンアップする: パフォーマンスを維持するため、古いインポートセットを削除します
  • マッピングを文書化する: フィールドマッピングと変換の記録を保持します
  • バージョン管理する: 変換スクリプトをソース管理に保存します
  • 監査証跡: コンプライアンスのため、すべてのインポート操作をログに記録します

トラブルシューティング

変換が「No Coalesce」で失敗する

症状: すべての行がエラーとしてマークされ、「Multiple records found」と表示されます
原因: コアレスフィールドが複数のレコードと一致しています
解決策:

  • 一意に照合できるよう、コアレスフィールドを追加します
  • multi_coalesce_action を設定して重複を処理します
  • インポート前にターゲットテーブルの重複を削除します
インポートのタイムアウト

症状: インポートが処理の途中で停止します
原因: 1 回のインポートに含まれるレコードが多すぎます
解決策:

  • チャンク分割処理を使用します
  • トランザクションのタイムアウトを延長します(システムプロパティ)
  • ビジネスルールを一時的に無効化します
参照フィールドがマッピングされない

症状: 変換後、参照フィールドが空のままです
原因: 参照先のレコードが見つかりません
解決策:

  • onForeignInsert スクリプトを使用して、見つからない参照先を作成します
  • 参照フィールドのクエリがソースデータと一致することを確認します
  • 参照先テーブルに一致するレコードがあるか確認します
パフォーマンスの低下

症状: インポートの速度が時間とともに大幅に低下します
原因: テーブルの断片化、インデックスの不足
解決策:

  • コアレスフィールドにインデックスを追加します
  • テーブルのメンテナンスを実行します
  • 古いインポートセットをアーカイブします
データの切り捨て

症状: フィールド値が途中で切れています
原因: ソースデータがフィールドの長さを超えています
解決策:

  • スキーマ内の field max_length を確認します
  • onBefore スクリプトでデータを切り捨てるか変換します
  • 適切であればフィールドの長さを増やします

例

例 1:基本的なユーザーインポート
# 1. Create transform map
Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_map
  data:
    name: Basic User Import
    source_table: u_user_import
    target_table: sys_user
    active: true

# 2. Create field mappings
Tool: SN-Batch-Create
Parameters:
  records:
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_email
        target_field: email
        coalesce: true
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_first_name
        target_field: first_name
    - table_name: sys_transform_entry
      data:
        map: [transform_map_sys_id]
        source_field: u_last_name
        target_field: last_name
例 2:関連付けを含む CI インポート
# Transform map for CI import with relationship creation
Tool: SN-Create-Record
Parameters:
  table_name: sys_transform_script
  data:
    map: [ci_transform_map_sys_id]
    script_type: onAfter
    script: |
      // Create CI relationships after import
      if (action == 'insert' && source.u_parent_ci) {
        var rel = new GlideRecord('cmdb_rel_ci');
        rel.initialize();
        rel.parent = lookupCI(source.u_parent_ci);
        rel.child = target.sys_id;
        rel.type = getRelationType('Depends on::Used by');
        rel.insert();
      }

      function lookupCI(name) {
        var ci = new GlideRecord('cmdb_ci');
        ci.addQuery('name', name);
        ci.query();
        return ci.next() ? ci.sys_id : '';
      }

      function getRelationType(name) {
        var type = new GlideRecord('cmdb_rel_type');
        type.addQuery('name', name);
        type.query();
        return type.next() ? type.sys_id : '';
      }
例 3:増分データ同期
# Scheduled import for incremental sync
Tool: SN-Create-Record
Parameters:
  table_name: sysauto_script
  data:
    name: Incremental User Sync
    script: |
      // Get last sync timestamp
      var prop = gs.getProperty('user.import.last_sync', '1970-01-01 00:00:00');
      var lastSync = new GlideDateTime();
      lastSync.setDisplayValue(prop);

      // Update data source query with timestamp
      var ds = new GlideRecord('sys_data_source');
      ds.get('[data_source_sys_id]');
      ds.query = "SELECT * FROM employees WHERE modified_date > '" + lastSync.getValue() + "'";
      ds.update();

      // Run import
      var loader = new GlideImportSetLoader();
      var importSetId = loader.loadAttachment('[data_source_sys_id]');

      var transformer = new GlideImportSetTransformer();
      transformer.setImportSetID(importSetId);
      transformer.setTransformMapID('[transform_map_sys_id]');
      transformer.transform();

      // Update last sync timestamp
      gs.setProperty('user.import.last_sync', new GlideDateTime().getDisplayValue());
    run_type: periodically
    run_period: 60
    active: true

関連スキル

  • admin/batch-operations - データの一括操作
  • admin/generic-crud-operations - 基本的なテーブル操作
  • admin/script-execution - バックグラウンドでのスクリプト実行
  • admin/schema-discovery - テーブル構造の確認
  • cmdb/data-quality - データ品質の検証

参考資料

コメント