data-import について
Import SetsとTransform Mapsを使ったServiceNowのデータインポートを解説するスキルです。開発者、管理者、連携担当者、データ移行チーム向けに、データソース設定、フィールドマッピング、重複排除、エラー処理、定期実行を扱います。
主な機能
- 01ファイル、JDBC、LDAP、RESTソースの設定
- 02ステージングテーブルとTransform Mapの作成
- 03フィールドマッピングと変換スクリプトの設定
- 04Coalesceによる照合と重複排除
- 05エラー処理と失敗行の再試行
- 06定期インポートの設定
活用シーン
- 01外部システムのデータをServiceNowに取り込む
- 02データを移行してターゲットテーブルに対応付ける
- 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_map | Transform 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 |
|---|---|
| MySQL | jdbc:mysql://host:3306/database |
| Oracle | jdbc:oracle:thin:@host:1521:sid |
| SQL Server | jdbc:sqlserver://host:1433;databaseName=db |
| PostgreSQL | jdbc: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 | 手動実行 | なし |
daily | 1 日に 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- データ品質の検証