mcpField
I
import-export

import-export

github.com/serac-labs数据与数据库7771 次安装9 周前更新
代码仓库

关于 import-export

面向 ServiceNow 管理者和开发者的数据导入导出技能,涵盖数据迁移、批量操作和传输,并提供 ES5 示例及 MCP 工具工作流。

主要功能

  1. 01从 CSV 导入数据
  2. 02运行转换映射
  3. 03导出为 CSV、JSON 和 XML
  4. 04配置计划导入与导出
  5. 05批量更新或删除记录

适用场景

  1. 01迁移外部数据到 ServiceNow
  2. 02按映射转换导入记录
  3. 03定期导出表数据
  4. 04执行带安全检查的批量变更

MCP 相关

文档列出用于创建导入集和转换映射、测试脚本及查询数据的 MCP 工具,并示范查询导入集的步骤。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/serac-labs/serac --skill import-export

安装后在 Claude Code 中输入/import-export确认已加载。

详细介绍

ServiceNow 导入/导出

导入/导出用于数据迁移、批量操作和数据传输。

导入/导出架构

Data Sources
    ├── Files (CSV, Excel, XML)
    ├── JDBC Connections
    └── REST/SOAP

Import Process
    ├── Import Set Tables
    ├── Transform Maps
    └── Target Tables

Export Process
    ├── Scheduled Exports
    ├── Report Exports
    └── XML Export

关键表

表用途
sys_import_set导入集记录
sys_data_source数据源
sys_transform_map转换映射
sys_export_set导出集

数据导入(ES5)

从 CSV 导入
// Import CSV data (ES5 ONLY!)
function importCSVData(csvContent, importSetTable) {
  var loader = new GlideImportSetLoader()

  // Create import set
  var importSet = new GlideRecord("sys_import_set")
  importSet.initialize()
  importSet.setValue("table_name", importSetTable)
  importSet.setValue("state", "loading")
  var importSetSysId = importSet.insert()

  // Parse CSV
  var lines = csvContent.split("\n")
  var headers = lines[0].split(",")

  // Clean headers
  for (var h = 0; h < headers.length; h++) {
    headers[h] = headers[h]
      .trim()
      .toLowerCase()
      .replace(/[^a-z0-9]/g, "_")
  }

  // Import rows
  var rowCount = 0
  for (var i = 1; i < lines.length; i++) {
    if (!lines[i].trim()) continue

    var values = parseCSVLine(lines[i])

    // Create import set row
    var row = new GlideRecord(importSetTable)
    row.initialize()
    row.setValue("sys_import_set", importSetSysId)

    for (var j = 0; j < headers.length && j < values.length; j++) {
      var fieldName = "u_" + headers[j]
      if (row.isValidField(fieldName)) {
        row.setValue(fieldName, values[j])
      }
    }

    row.insert()
    rowCount++
  }

  // Update import set
  importSet = new GlideRecord("sys_import_set")
  if (importSet.get(importSetSysId)) {
    importSet.setValue("state", "loaded")
    importSet.setValue("row_count", rowCount)
    importSet.update()
  }

  return {
    import_set: importSetSysId,
    rows: rowCount,
  }
}

function parseCSVLine(line) {
  var values = []
  var current = ""
  var inQuotes = false

  for (var i = 0; i < line.length; i++) {
    var char = line[i]

    if (char === '"') {
      inQuotes = !inQuotes
    } else if (char === "," && !inQuotes) {
      values.push(current.trim())
      current = ""
    } else {
      current += char
    }
  }
  values.push(current.trim())

  return values
}
运行转换
// Run transform on import set (ES5 ONLY!)
function runTransform(importSetSysId, transformMapName) {
  var importSet = new GlideRecord("sys_import_set")
  if (!importSet.get(importSetSysId)) {
    return { success: false, message: "Import set not found" }
  }

  // Get transform map
  var transformMap = new GlideRecord("sys_transform_map")
  if (!transformMap.get("name", transformMapName)) {
    return { success: false, message: "Transform map not found" }
  }

  // Run transform
  var transformer = new GlideImportSetTransformer()
  transformer.setImportSetID(importSetSysId)
  transformer.setTransformMapID(transformMap.getUniqueValue())
  transformer.transform()

  // Get results
  var results = {
    success: true,
    inserted: 0,
    updated: 0,
    ignored: 0,
    error: 0,
  }

  // Count results from import set rows
  var ga = new GlideAggregate(importSet.getValue("table_name"))
  ga.addQuery("sys_import_set", importSetSysId)
  ga.addAggregate("COUNT")
  ga.groupBy("sys_import_state")
  ga.query()

  while (ga.next()) {
    var state = ga.getValue("sys_import_state")
    var count = parseInt(ga.getAggregate("COUNT"), 10)

    if (state === "inserted") results.inserted = count
    else if (state === "updated") results.updated = count
    else if (state === "ignored") results.ignored = count
    else if (state === "error") results.error = count
  }

  return results
}

数据导出(ES5)

导出为 CSV
// Export table data to CSV (ES5 ONLY!)
function exportToCSV(tableName, encodedQuery, fields) {
  var fieldList = fields.split(",")
  var csv = ""

  // Header row
  csv += fieldList.join(",") + "\n"

  // Data rows
  var gr = new GlideRecord(tableName)
  if (encodedQuery) {
    gr.addEncodedQuery(encodedQuery)
  }
  gr.query()

  while (gr.next()) {
    var row = []
    for (var i = 0; i < fieldList.length; i++) {
      var field = fieldList[i].trim()
      var value = gr.getDisplayValue(field) || ""

      // Escape for CSV
      if (value.indexOf(",") !== -1 || value.indexOf('"') !== -1 || value.indexOf("\n") !== -1) {
        value = '"' + value.replace(/"/g, '""') + '"'
      }
      row.push(value)
    }
    csv += row.join(",") + "\n"
  }

  return csv
}

// Example
var csvData = exportToCSV("incident", "active=true^priority<=2", "number,short_description,priority,state,assigned_to")
导出为 JSON
// Export to JSON (ES5 ONLY!)
function exportToJSON(tableName, encodedQuery, fields) {
  var fieldList = fields.split(",")
  var records = []

  var gr = new GlideRecord(tableName)
  if (encodedQuery) {
    gr.addEncodedQuery(encodedQuery)
  }
  gr.query()

  while (gr.next()) {
    var record = {}
    for (var i = 0; i < fieldList.length; i++) {
      var field = fieldList[i].trim()
      record[field] = {
        value: gr.getValue(field),
        display_value: gr.getDisplayValue(field),
      }
    }
    record.sys_id = gr.getUniqueValue()
    records.push(record)
  }

  return JSON.stringify(records, null, 2)
}
导出为 XML
// Export records to XML (ES5 ONLY!)
function exportToXML(tableName, encodedQuery) {
  var exporter = new GlideRecordXMLSerializer()

  var gr = new GlideRecord(tableName)
  if (encodedQuery) {
    gr.addEncodedQuery(encodedQuery)
  }
  gr.query()

  var xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
  xml += "<records>\n"

  while (gr.next()) {
    xml += exporter.serialize(gr) + "\n"
  }

  xml += "</records>"

  return xml
}

计划导入(ES5)

创建计划导入
// Create scheduled data import (ES5 ONLY!)
var dataSource = new GlideRecord("sys_data_source")
dataSource.initialize()

// Data source config
dataSource.setValue("name", "Daily Employee Sync")
dataSource.setValue("type", "File")
dataSource.setValue("format", "CSV")

// File location
dataSource.setValue("file_path", "/import/employees.csv")

// Import set table
dataSource.setValue("import_set_table_name", "u_employee_import")

// Schedule
dataSource.setValue("schedule", scheduleId) // Reference to scheduled job

// Active
dataSource.setValue("active", true)

dataSource.insert()
计划导出
// Scheduled export job (ES5 ONLY!)
;(function executeScheduledJob() {
  var LOG_PREFIX = "[ScheduledExport] "

  // Export data
  var csvData = exportToCSV(
    "incident",
    "closed_at>=javascript:gs.daysAgoStart(1)^closed_at<javascript:gs.daysAgoStart(0)",
    "number,short_description,resolved_at,resolution_code,resolved_by",
  )

  // Create attachment on export record
  var exportRecord = new GlideRecord("sys_export_set")
  exportRecord.initialize()
  exportRecord.setValue("name", "Daily Incident Export - " + new GlideDateTime().getLocalDate())
  exportRecord.setValue("table", "incident")
  var exportSysId = exportRecord.insert()

  // Attach CSV
  var attachment = new GlideSysAttachment()
  attachment.write(
    "sys_export_set",
    exportSysId,
    "incident_export_" + new GlideDateTime().getLocalDate() + ".csv",
    "text/csv",
    csvData,
  )

  gs.info(LOG_PREFIX + "Export completed")

  // Notify
  gs.eventQueue("export.complete", exportRecord, "", "")
})()

批量操作(ES5)

批量更新
// Bulk update records (ES5 ONLY!)
function bulkUpdate(tableName, encodedQuery, updates) {
  var updateCount = 0
  var errors = []

  var gr = new GlideRecord(tableName)
  if (encodedQuery) {
    gr.addEncodedQuery(encodedQuery)
  }
  gr.query()

  while (gr.next()) {
    try {
      for (var field in updates) {
        if (updates.hasOwnProperty(field) && gr.isValidField(field)) {
          gr.setValue(field, updates[field])
        }
      }
      gr.update()
      updateCount++
    } catch (e) {
      errors.push({
        sys_id: gr.getUniqueValue(),
        error: e.message,
      })
    }
  }

  return {
    updated: updateCount,
    errors: errors,
  }
}

// Example: Close old incidents
var result = bulkUpdate("incident", "active=true^sys_updated_on<javascript:gs.daysAgo(90)", {
  state: 7,
  close_code: "Closed/Resolved by Caller",
  close_notes: "Auto-closed due to inactivity",
})
批量删除
// Bulk delete with safety checks (ES5 ONLY!)
function bulkDelete(tableName, encodedQuery, maxRecords) {
  maxRecords = maxRecords || 1000

  var gr = new GlideRecord(tableName)
  if (encodedQuery) {
    gr.addEncodedQuery(encodedQuery)
  }
  gr.setLimit(maxRecords)
  gr.query()

  var count = gr.getRowCount()

  if (count > maxRecords) {
    return {
      success: false,
      message: "Too many records (" + count + "). Max allowed: " + maxRecords,
    }
  }

  // Use deleteMultiple for efficiency
  gr = new GlideRecord(tableName)
  gr.addEncodedQuery(encodedQuery)
  gr.setLimit(maxRecords)
  gr.deleteMultiple()

  return {
    success: true,
    deleted: count,
  }
}

MCP 工具集成

可用工具
工具用途
snow_create_import_set创建导入集
snow_create_transform_map创建转换
snow_execute_script测试导入/导出
snow_query_table查询数据
工作流示例
// 1. Query import sets
await snow_query_table({
  table: "sys_import_set",
  query: "state=loaded",
  fields: "table_name,row_count,state,sys_created_on",
})

// 2. Export data
await snow_execute_script({
  script: `
        var csv = exportToCSV('incident', 'active=true', 'number,short_description,state');
        gs.info('Exported ' + csv.split('\\n').length + ' rows');
    `,
})

// 3. Check transform maps
await snow_query_table({
  table: "sys_transform_map",
  query: "active=true",
  fields: "name,source_table,target_table",
})

最佳实践

  1. 验证 - 导入前验证数据
  2. 合并字段 - 使用合并字段进行更新
  3. 批量大小 - 限制批量操作的规模
  4. 日志记录 - 跟踪导入/导出活动
  5. 错误处理 - 处理行级错误
  6. 计划安排 - 大规模操作应安排在非高峰时段
  7. 备份 - 批量更改前进行备份
  8. 仅限 ES5 - 不使用现代 JavaScript 语法

评论

支持 Markdown