mcpField
Q
query-generation

query-generation

github.com/happy-technologies-llc数据与数据库4142 次安装2 天前更新
代码仓库

关于 query-generation

query-generation 是一项 ServiceNow 技能,可将自然语言数据需求转为编码查询、GlideRecord 和 GlideAggregate 脚本及报表筛选条件。它面向检索记录或配置报表的用户,涵盖日期逻辑、AND/OR 条件和关联表字段。

主要功能

  1. 01自然语言转编码查询
  2. 02GlideRecord 查询脚本
  3. 03GlideAggregate 汇总
  4. 04报表筛选条件配置
  5. 05日期与相对时间筛选
  6. 06AND/OR 及点遍历查询

适用场景

  1. 01按自然语言需求筛选事件列表
  2. 02按类别或优先级汇总记录
  3. 03配置 ServiceNow 报表条件
  4. 04使用日期范围和关联字段构建查询

MCP 相关

该技能介绍使用 MCP 工具 SN-Create-Record 创建 ServiceNow 报表,也提供通过 MCP 查询 ServiceNow 数据的示例。

安装

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

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

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

详细介绍

从自然语言生成 ServiceNow 查询

概述

此技能涵盖将自然语言描述转换为 ServiceNow 查询、脚本和报表配置:

  • 将简单的英文需求转换为编码查询
  • 使用正确的过滤运算符构建 GlideRecord 查询
  • 生成用于数据汇总的 GlideAggregate 脚本
  • 为仪表板创建报表筛选条件
  • 转换包含 AND/OR 逻辑的复杂多条件查询
  • 生成基于日期、相对时间和动态查询的查询

适用场景: 用户以自然语言描述数据需求、构建报表筛选条件、创建基于脚本的数据检索,或为列表和模块构建复杂编码查询时。

前提条件

  • 角色: itil、report_user、admin,或任何可读取目标表的角色
  • 访问权限: 目标数据表、用于字段验证的 sys_dictionary、用于创建报表的 sys_report
  • 知识: ServiceNow 编码查询语法、GlideRecord API、GlideAggregate API、数据类型
  • 相关技能: 完成 reporting/analytics-generation 以创建仪表板

流程

第 1 步:了解编码查询语法

ServiceNow 编码查询使用特定语法筛选记录:

运算符参考:

运算符语法示例
等于=priority=1
不等于!=state!=6
包含LIKEshort_descriptionLIKEnetwork
不包含NOT LIKEshort_descriptionNOT LIKEtest
以……开头STARTSWITHnumberSTARTSWITHINC
以……结尾ENDSWITH[email protected]
为空ISEMPTYassignment_groupISEMPTY
不为空ISNOTEMPTYresolved_atISNOTEMPTY
大于>priority>2
小于<priority<3
大于或等于>=sys_created_on>=2025-01-01
小于或等于<=sys_created_on<=2025-12-31
在列表中INstate IN 1,2,3
不在列表中NOT INpriority NOT IN 4,5
介于BETWEENsys_created_onBETWEEN2025-01-01@2025-06-30
实例属于INSTANCEOFsys_class_nameINSTANCEOFtask
AND^active=true^priority=1
OR^ORpriority=1^ORpriority=2
新查询(OR 组)^NQpriority=1^NQstate=6

日期运算符:

函数语法含义
今天javascript:gs.daysAgo(0)当前日期
过去 7 天javascript:gs.daysAgo(7)过去一周
过去 30 天javascript:gs.daysAgo(30)过去一个月
本月开始javascript:gs.beginningOfThisMonth()本月第一天
本季度开始javascript:gs.beginningOfThisQuarter()本季度第一天
今年开始javascript:gs.beginningOfThisYear()今年第一天
上月开始javascript:gs.beginningOfLastMonth()上个月第一天
上月结束javascript:gs.endOfLastMonth()上个月最后一天
第 2 步:将自然语言转换为编码查询

自然语言转换为查询的示例:

自然语言编码查询
“所有未关闭的严重事件”active=true^priority=1
“本周打开、分配给 Service Desk 的事件”assignment_group.name=Service Desk^sys_created_on>=javascript:gs.daysAgo(7)
“未解决的 P1 或 P2 事件”active=true^priority=1^ORpriority=2
“已批准但尚未实施的变更”state=authorized^ORstate=scheduled
“没有经理的用户”managerISEMPTY^active=true
“2025 年第一季度创建的事件”sys_created_on>=2025-01-01^sys_created_on<2025-04-01
“3 天内未更新的高优先级事件”priority IN 1,2^sys_updated_on<javascript:gs.daysAgo(3)^active=true
第 3 步:根据表架构验证查询

使用查询前,请确认目标表中存在相应字段。

MCP 方式:

Tool: SN-Get-Table-Schema
Parameters:
  table_name: incident

REST 替代方案:

curl -u "$SN_USER:$SN_PASS" \
  "$SN_INSTANCE/api/now/table/sys_dictionary?sysparm_query=name=incident^elementISNOTEMPTY^active=true&sysparm_fields=element,column_label,internal_type&sysparm_limit=200" \
  -H "Accept: application/json"
第 4 步:测试生成的查询

MCP 方式:

Tool: SN-Query-Table
Parameters:
  table_name: incident
  query: active=true^priority=1^assignment_groupISEMPTY
  fields: sys_id,number,short_description,priority,state,sys_created_on
  limit: 10

REST 替代方式:

curl -u "$SN_USER:$SN_PASS" \
  "$SN_INSTANCE/api/now/table/incident?sysparm_query=active=true^priority=1^assignment_groupISEMPTY&sysparm_fields=sys_id,number,short_description,priority,state&sysparm_limit=10" \
  -H "Accept: application/json"
第 5 步:生成 GlideRecord 脚本

基本 GlideRecord 查询:

// Natural Language: "Get all active incidents assigned to the Network team"
var gr = new GlideRecord('incident');
gr.addQuery('active', true);
gr.addQuery('assignment_group.name', 'Network');
gr.orderByDesc('sys_created_on');
gr.setLimit(100);
gr.query();

while (gr.next()) {
    gs.info('Incident: {0} - {1} (Priority: {2})',
        gr.getValue('number'),
        gr.getValue('short_description'),
        gr.getDisplayValue('priority'));
}

包含复杂条件的 GlideRecord:

// Natural Language: "Find incidents that are either P1 or P2,
// created in the last 7 days, and not yet assigned"
var gr = new GlideRecord('incident');
var qc = gr.addQuery('priority', '1');
qc.addOrCondition('priority', '2');
gr.addQuery('sys_created_on', '>=', gs.daysAgo(7));
gr.addQuery('assigned_to', '');
gr.addActiveQuery();
gr.query();

gs.info('Found {0} unassigned high-priority incidents', gr.getRowCount());

包含编码查询的 GlideRecord:

// Natural Language: "All resolved incidents from this month
// where the resolution was a workaround"
var gr = new GlideRecord('incident');
gr.addEncodedQuery('state=6^sys_resolved_on>=javascript:gs.beginningOfThisMonth()^close_notesLIKEworkaround');
gr.query();

while (gr.next()) {
    gs.info('{0}: {1}', gr.getValue('number'), gr.getValue('close_notes'));
}
第 6 步:生成 GlideAggregate 脚本

分组计数:

// Natural Language: "How many incidents are open per priority?"
var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.groupBy('priority');
ga.orderBy('priority');
ga.query();

while (ga.next()) {
    gs.info('Priority {0}: {1} incidents',
        ga.getDisplayValue('priority'),
        ga.getAggregate('COUNT'));
}

平均值计算:

// Natural Language: "What is the average resolution time by category?"
var ga = new GlideAggregate('incident');
ga.addQuery('state', 6); // Resolved
ga.addQuery('sys_resolved_on', '>=', gs.daysAgo(90));
ga.addAggregate('AVG', 'business_duration');
ga.groupBy('category');
ga.query();

while (ga.next()) {
    var avgDuration = ga.getAggregate('AVG', 'business_duration');
    var hours = (parseInt(avgDuration, 10) / 3600).toFixed(1);
    gs.info('Category: {0} - Avg Resolution: {1} hours',
        ga.getDisplayValue('category'), hours);
}

带筛选条件的求和:

// Natural Language: "Total cost of all hardware assets purchased this year"
var ga = new GlideAggregate('alm_hardware');
ga.addQuery('purchase_date', '>=', gs.beginningOfThisYear());
ga.addAggregate('SUM', 'cost');
ga.query();

if (ga.next()) {
    gs.info('Total hardware cost this year: ${0}',
        ga.getAggregate('SUM', 'cost'));
}

多个聚合:

// Natural Language: "For each assignment group, show count,
// average priority, and oldest open incident"
var ga = new GlideAggregate('incident');
ga.addQuery('active', true);
ga.addAggregate('COUNT');
ga.addAggregate('AVG', 'priority');
ga.addAggregate('MIN', 'sys_created_on');
ga.groupBy('assignment_group');
ga.orderByAggregate('COUNT');
ga.query();

while (ga.next()) {
    gs.info('Group: {0} | Count: {1} | Avg Priority: {2} | Oldest: {3}',
        ga.getDisplayValue('assignment_group'),
        ga.getAggregate('COUNT'),
        parseFloat(ga.getAggregate('AVG', 'priority')).toFixed(1),
        ga.getAggregate('MIN', 'sys_created_on'));
}
第 7 步:生成报表筛选条件

使用生成的查询创建报表:

MCP 方式:

Tool: SN-Create-Record
Parameters:
  table_name: sys_report
  data:
    title: "Unassigned Critical Incidents"
    table: "incident"
    type: "list"
    filter: "active=true^priority=1^assigned_toISEMPTY"
    is_published: true
    active: true

使用查询创建图表报表:

MCP 方式:

Tool: SN-Create-Record
Parameters:
  table_name: sys_report
  data:
    title: "Monthly Incident Trend by Priority"
    table: "incident"
    type: "line"
    field: "priority"
    filter: "sys_created_on>=javascript:gs.monthsAgo(12)"
    aggregate: "COUNT"
    trend_field: "sys_created_on"
    interval: "month"
    is_published: true
    active: true

REST 替代方式:

curl -u "$SN_USER:$SN_PASS" \
  "$SN_INSTANCE/api/now/table/sys_report" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
    "title": "Monthly Incident Trend by Priority",
    "table": "incident",
    "type": "line",
    "field": "priority",
    "filter": "sys_created_on>=javascript:gs.monthsAgo(12)",
    "aggregate": "COUNT",
    "trend_field": "sys_created_on",
    "interval": "month",
    "is_published": true
  }'
第 8 步:高级查询模式

点遍历(相关表字段):

// Natural Language: "Incidents where the caller's department is IT"
query: caller_id.department.name=IT^active=true

动态引用限定符:

// Natural Language: "Incidents assigned to the current user's group"
query: assignment_group=javascript:getMyGroups()^active=true

相对日期查询:

// Natural Language: "Changes scheduled for next week"
query: start_date>=javascript:gs.daysAgo(-1)^start_date<javascript:gs.daysAgo(-8)

用于层级查询的 INSTANCEOF:

// Natural Language: "All tasks including incidents, changes, and problems"
query: sys_class_nameINSTANCEOFtask^active=true

工具用法

操作MCP 工具REST 端点
测试查询SN-Query-TableGET /api/now/table/{table}?sysparm_query={query}
获取表结构SN-Get-Table-SchemaGET /api/now/table/sys_dictionary
创建报表SN-Create-RecordPOST /api/now/table/sys_report
创建 PA 指标SN-Create-RecordPOST /api/now/table/pa_indicators
验证字段SN-Query-TableGET /api/now/table/sys_dictionary

最佳实践

  • 先验证字段: 构建查询前,务必检查 sys_dictionary,确认字段名称
  • 逐步测试: 一次添加一个条件来构建复杂查询,并测试每次添加的条件
  • 报表优先使用编码查询: 与脚本筛选器相比,编码查询的可移植性更强,也更适合 URL
  • 计数优先使用 GlideAggregate 而非 GlideRecord: GlideAggregate 执行聚合的效率高得多
  • 测试时使用 setLimit: 开发过程中务必限制结果数量,以避免性能问题
  • 谨慎使用点遍历: 跨多个引用进行点遍历可能会影响性能
  • 多个值使用 IN 运算符: priority IN 1,2,3 比串联 OR 条件更简洁
  • 记录复杂查询: 添加注释,说明多条件查询背后的业务逻辑
  • 日期使用 javascript: 函数: 相对日期函数可确保查询保持动态
  • 避免在动态查询中使用 eval(): 切勿根据未经验证的用户输入构造查询
  • 为关键筛选字段建立索引: 确保常用筛选字段具有数据库索引

故障排查

查询未返回结果

现象: 数据应该存在,但编码查询返回空结果集 原因:

  1. 字段名称拼写错误,或该字段在表中不存在
  2. 值不匹配(大小写敏感、显示值与内部值不同)
  3. ACL 限制筛掉了结果 解决方案: 验证字段名称和值:
Tool: SN-Get-Table-Schema
Parameters:
  table_name: [target_table]

然后测试简化版查询:

Tool: SN-Query-Table
Parameters:
  table_name: [target_table]
  query: [single_condition_only]
  fields: sys_id
  limit: 1
查询返回过多结果

现象: 查询范围过宽,返回了过多记录 原因:

  1. 缺少筛选条件
  2. OR 条件范围过宽
  3. 缺少 active=true 筛选条件 解决方案: 逐步添加约束:
Tool: SN-Query-Table
Parameters:
  table_name: incident
  query: active=true^priority=1^sys_created_on>=javascript:gs.daysAgo(30)
  fields: sys_id
  limit: 1
日期查询筛选不正确

现象: 基于日期的筛选条件包含了意外记录 原因:

  1. 日期字段与日期时间比较,或日期时间字段与日期比较
  2. 时区差异
  3. 比较时缺少时间部分 解决方案: 使用 ServiceNow 日期函数以确保可靠性:
// Use gs.daysAgo() instead of hardcoded dates
query: sys_created_on>=javascript:gs.daysAgo(30)

// For specific date ranges, include full datetime
query: sys_created_on>=2025-01-01 00:00:00^sys_created_on<2025-02-01 00:00:00
GlideAggregate 返回的计数异常

现象: 聚合计数与手动计数不一致 原因:

  1. ACL 限制从聚合结果中筛掉了记录
  2. 缺少 addQuery 条件
  3. 按错误的字段分组 解决方案: 与简单计数进行比较:
// Verify with GlideRecord count
var gr = new GlideRecord('incident');
gr.addEncodedQuery('active=true^priority=1');
gr.query();
gs.info('GlideRecord count: ' + gr.getRowCount());
点遍历查询性能

现象: 使用点遍历的查询速度很慢 原因: 多层点遍历会产生开销较大的 JOIN 解决方案: 限制点遍历深度,并优先添加有索引的条件:

// BAD: Deep dot-walk as primary filter
query: caller_id.department.company.name=Acme

// GOOD: Add indexed conditions first, limit dot-walk depth
query: active=true^priority IN 1,2^caller_id.department.name=IT

示例

示例 1:构建复杂的事件查询
Natural Language: "Show me all P1 and P2 incidents from the last 30 days
that are still open, assigned to either the Network or Server team,
and have not been updated in the last 48 hours"

Encoded Query:
active=true^priority IN 1,2^sys_created_on>=javascript:gs.daysAgo(30)^assignment_group.nameLIKENetwork^ORassignment_group.nameLIKEServer^sys_updated_on<javascript:gs.daysAgo(2)

Test:
Tool: SN-Query-Table
Parameters:
  table_name: incident
  query: active=true^priority IN 1,2^sys_created_on>=javascript:gs.daysAgo(30)^sys_updated_on<javascript:gs.daysAgo(2)
  fields: sys_id,number,priority,assignment_group,sys_updated_on
  limit: 20
示例 2:生成聚合报告脚本
Natural Language: "Create a summary showing the number of incidents
created each month this year, broken down by category"

GlideAggregate Script:
var ga = new GlideAggregate('incident');
ga.addQuery('sys_created_on', '>=', gs.beginningOfThisYear());
ga.addAggregate('COUNT');
ga.groupBy('category');
ga.addTrend('sys_created_on', 'month');
ga.query();

while (ga.next()) {
    gs.info('Month: {0} | Category: {1} | Count: {2}',
        ga.getValue('timeref'),
        ga.getDisplayValue('category'),
        ga.getAggregate('COUNT'));
}
示例 3:根据自然语言创建报告
Natural Language: "I need a pie chart showing the distribution
of change requests by risk level for the current quarter"

Tool: SN-Create-Record
Parameters:
  table_name: sys_report
  data:
    title: "Change Requests by Risk Level (Current Quarter)"
    table: "change_request"
    type: "pie"
    field: "risk"
    filter: "sys_created_on>=javascript:gs.beginningOfThisQuarter()"
    aggregate: "COUNT"
    is_published: true
    active: true

相关技能

  • reporting/analytics-generation - 使用 PA 指标构建完整的仪表板
  • development/code-assist - 生成包含查询逻辑的完整脚本
  • itsm/incident-lifecycle - 事件数据模型和字段参考
  • admin/schema-discovery - 将查询应用于列表视图和筛选器

评论

支持 Markdown