Tables & Data Model
Note
Understanding Nexthink's data model and available tables - the foundation for writing effective NQL queries.
Overview
Nexthink stores endpoint data in a collection of tables. Understanding which table to query and how tables relate to each other is essential for writing correct NQL queries.
Two main table types: 1. Object Tables - Current state (devices, users, applications, binaries) 2. Event Tables - Historical sampled data (execution.events, connection.events, crashes)
Choosing the Right Table
Ask yourself: "Am I querying current state or historical activity?" - Current state → Use object tables (devices, users, applications) - Historical activity → Use event tables (execution.events, device_performance.events)
Primary Tables Overview
| Table | Type | Time Required | Sample Interval | Use For |
|---|---|---|---|---|
devices | Object | No | N/A | Device inventory, current configuration |
users | Object | No | N/A | User accounts, current properties |
applications | Object | No | N/A | Application catalog |
binaries | Object | No | N/A | Executable files, versions |
execution.events | Event | Yes | 5-15 min | Process execution, CPU, memory usage |
execution.crashes | Event | Yes | On crash | Application crashes, stability |
device_performance.events | Event | Yes | 5-15 min | CPU, memory, disk performance |
device_performance.boots | Event | Yes | On boot | Boot events, uptime |
connection.events | Event | Yes | 5-15 min | Network connections, bandwidth |
web.events | Event | Yes | Per event | Web browsing activity |
web.page_views | Event | Yes | Per page | Page load times, performance |
Object Tables (Current State)
Object tables represent the current state of entities in your environment.
devices
What it contains: All devices monitored by Nexthink
Key characteristics:
- No time selection required
- Shows current device state
- Updated in real-time as devices report
Common use cases:
- Device inventory reports
- Hardware specifications
- Current OS and configuration
- Last seen timestamp
Example:
/* All Windows 11 devices */
devices
| where operating_system.name == "Windows 11"
| list device.name,
device.hardware.memory.as(format = bytes),
device.last_seen
Key fields:
device.name- Device hostnamedevice.operating_system.name- OS namedevice.operating_system.platform- windows/macos/linuxdevice.hardware.memory- RAM sizedevice.volumes.size- Disk sizedevice.last_seen- Last contact timestampdevice.department- Department assignment
users
What it contains: User accounts
Example:
applications
What it contains: Catalog of applications
Example:
binaries
What it contains: Executable files and their metadata
Example:
binaries during past 24h
| where binary.name == "dllhost.exe"
| list name, version, size.as(format = bytes)
Event Tables (Historical Activity)
Event tables contain sampled historical data about device and user activity.
Event Tables Require Time Selection
All event table queries must include during past Xd or past Xh.
execution.events
What it contains: Process execution data (sampled every 5-15 minutes)
Sample interval: 5-15 minutes (high-resolution)
Retention:
- High-res (
past Xh): 8 days - Low-res (
past Xd): 30 days
Common use cases:
- CPU usage analysis
- Memory consumption
- Application usage patterns
- Process monitoring
Example:
/* Outlook CPU usage by device */
execution.events during past 7d
| where binary.name == "outlook.exe"
| summarize
avg_cpu = cpu_time.avg(),
avg_memory = real_memory.avg()
by device.name
| sort avg_cpu desc
Key fields:
binary.name- Process executable namecpu_time- CPU time consumedreal_memory- Memory usageexecution_duration- How long process rannumber_of_started_processes- Process startsdevice.name- Which device (join field)user.name- Which user (join field)
Sampled Data vs Discrete Events
execution.events contains samples taken every 5-15 minutes, not every process start/stop.
Never use count() on execution.events - it returns sample count, not process count!
execution.crashes
What it contains: Application crash events
Sample interval: Per crash (discrete events)
Retention: 30 days
Common use cases:
- Crash analysis
- Application stability
- Affected device identification
Example:
/* Outlook crashes by version */
execution.crashes during past 7d
| where binary.binary.name == "outlook.exe"
| summarize
crash_count = count(),
affected_devices = device.name.count()
by binary.real_binary.version
| sort crash_count desc
Key fields:
binary.binary.name- Crashed applicationbinary.real_binary.version- Application versiondevice.name- Which device crasheduser.name- Which user experienced crash
Crashes are Discrete Events
Unlike execution.events, crashes are discrete events (one per crash).
Using count() on execution.crashes is correct - it counts actual crashes!
device_performance.events
What it contains: System performance metrics (CPU, memory, disk)
Sample interval: 5-15 minutes
Retention: 30 days (both resolutions)
Common use cases:
- CPU usage trending
- Memory pressure
- Disk space monitoring
- Performance baselines
Example:
/* Devices with high CPU usage */
devices during past 7d
| include device_performance.events
| where cpu_usage.avg > 50
| compute
avg_cpu = cpu_usage.avg(),
peak_cpu = cpu_usage.avg.max()
| list device.name, avg_cpu, peak_cpu
Key fields:
cpu_usage.avg- CPU utilization percentagefree_memory- Available RAMfree_disk_space- Available disk space
connection.events
What it contains: Network connection data (sampled)
Sample interval: 5-15 minutes
Retention:
- High-res (
past Xh): 8 days - Low-res (
past Xd): 30 days
Common use cases:
- Network troubleshooting
- Bandwidth analysis
- Connection failure rates
- Destination analysis
Example:
/* Connection failures to specific domain */
connection.events during past 7d
| where event.destination.domain == "*.service.com"
and event.failed_connection_ratio >= 0.15
| summarize
total_connections = event.number_of_connections.sum(),
avg_failure_rate = event.failed_connection_ratio.avg()
by event.destination.domain
Key fields:
event.destination.domain- Destination serverevent.number_of_connections- Connection countevent.failed_connection_ratio- Failure percentageincoming_traffic- Download bandwidthoutgoing_traffic- Upload bandwidthconnection_establishment_time- Round-trip time (latency)
web.events & web.page_views
web.events: Web browsing activity
web.page_views: Page load performance
Retention: 30 days
Example:
/* Confluence page load performance */
web.page_views during past 7d
| where application.name == "Confluence"
| summarize avg_load_time = page_load_time.backend.avg() by device.name
| sort avg_load_time desc
Joining Tables: with vs include
To combine object and event data, use with or include:
with - Filter to Objects WITH Events
Returns only objects that have events.
/* Only devices that crashed */
devices
| with execution.crashes during past 7d
| list device.name
/* Returns: ~15 devices (only those with crashes) */
Use when:
- Finding devices/users that performed an action
- Filtering by event existence
- You don't need objects without events
include - ALL Objects with Metrics
Returns all objects with computed metrics from events.
/* All devices with crash counts */
devices
| include execution.crashes during past 7d
| compute crash_count = count()
| list device.name, crash_count
/* Returns: 142 devices (crash_count = 0 for some) */
Use when:
- Complete inventory with metrics
- Percentage calculations
- You need all objects, including those without events
See with vs include for detailed comparison.
Context Fields vs Device Fields
When querying events, you have access to two types of fields:
context Fields (Historical)
What: Values at the time the event occurred
Use when: You need historical state
/* What OS was running when the event occurred? */
execution.events during past 30d
| list device.name,
context.os_name, # OS at event time
device.operating_system.name # Current OS
Example scenario: Device upgraded from Windows 10 to Windows 11 yesterday. Events from last week show context.os_name = "Windows 10" but device.operating_system.name = "Windows 11".
Common context fields:
context.os_namecontext.device_platformcontext.hardware_modelcontext.departmentcontext.location
device Fields (Current)
What: Current device state
Use when: You need current configuration
/* Current device information */
execution.events during past 7d
| summarize event_count = count() by device.name, device.operating_system.name
/* Shows current OS for each device */
Which to Use?
- Trend analysis by historical state → Use
context.* - Current inventory/configuration → Use
device.* - "What is this device now?" → Use
device.* - "What was the environment when this happened?" → Use
context.*
Data Retention Summary
| Table | High-Res (past Xh) | Low-Res (past Xd) |
|---|---|---|
| execution.events | 8 days | 30 days |
| connection.events | 8 days | 30 days |
| execution.crashes | N/A | 30 days |
| device_performance.events | 30 days | 30 days |
| web.events | 30 days | 30 days |
| devices (object) | N/A (current state) | N/A |
Critical Retention Limits
execution.events and connection.events have 8-day limit for high-resolution queries!
Common Query Patterns
Pattern: Device Inventory
/* Current device state (no time needed) */
devices
| where operating_system.platform == windows
| list device.name,
operating_system.name,
hardware.memory.as(format = bytes)
Pattern: Device + Performance Events
/* Devices with high CPU */
devices during past 7d
| include device_performance.events
| where cpu_usage.avg > 80
| compute avg_cpu = cpu_usage.avg()
| list device.name, avg_cpu
Pattern: Process Activity Analysis
/* Application usage by device */
execution.events during past 7d
| where binary.name == "outlook.exe"
| summarize
avg_cpu = cpu_time.avg(),
avg_memory = real_memory.avg()
by device.name
Pattern: Crash Investigation
/* Application stability analysis */
execution.crashes during past 7d
| summarize
crash_count = count(),
affected_devices = device.name.count()
by application.name
| where crash_count > 10
| sort crash_count desc
Pattern: Network Analysis
/* Connection failures by destination */
connection.events during past 7d
| where event.failed_connection_ratio > 0.10
| summarize
total_connections = event.number_of_connections.sum(),
avg_failure_rate = event.failed_connection_ratio.avg()
by event.destination.domain
| sort avg_failure_rate desc
Decision Tree: Which Table?
What are you querying?
│
├─ Current device configuration/inventory
│ └─ Use: devices
│
├─ Current user information
│ └─ Use: users
│
├─ Process execution, CPU, memory over time
│ └─ Use: execution.events during past Xd
│
├─ Application crashes
│ └─ Use: execution.crashes during past Xd
│
├─ System performance (CPU, memory, disk)
│ └─ Use: device_performance.events during past Xd
│
├─ Network connections, bandwidth
│ └─ Use: connection.events during past Xd
│
└─ Web browsing, page load times
└─ Use: web.events or web.page_views during past Xd
Related Topics
- NQL Basics - Query structure and fundamentals
- Time Selection - Understanding
during pastsyntax - with vs include - Joining tables correctly
- Field Reference - Available fields per table
Additional Resources
- Common Query Templates - Templates for each table type
- NQL Syntax Cheat Sheet - Quick table reference