Skip to content

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 hostname
  • device.operating_system.name - OS name
  • device.operating_system.platform - windows/macos/linux
  • device.hardware.memory - RAM size
  • device.volumes.size - Disk size
  • device.last_seen - Last contact timestamp
  • device.department - Department assignment

users

What it contains: User accounts

Example:

users
| list username, type, department

applications

What it contains: Catalog of applications

Example:

applications
| where publisher == "Microsoft"
| list name, publisher, version

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.

/* ❌ WRONG */
execution.events
| summarize count = count()

/* ✅ CORRECT */
execution.events during past 7d
| summarize count = count()

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 name
  • cpu_time - CPU time consumed
  • real_memory - Memory usage
  • execution_duration - How long process ran
  • number_of_started_processes - Process starts
  • device.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!

/* ❌ WRONG - Returns sample count */
execution.events during past 7d
| summarize total = count()

/* ✅ CORRECT - Count unique devices/apps */
execution.events during past 7d
| summarize
devices = device.count(),
apps = application.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 application
  • binary.real_binary.version - Application version
  • device.name - Which device crashed
  • user.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!

/* ✅ CORRECT - Counts actual crashes */
execution.crashes during past 7d
| summarize total_crashes = count()

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 percentage
  • free_memory - Available RAM
  • free_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 server
  • event.number_of_connections - Connection count
  • event.failed_connection_ratio - Failure percentage
  • incoming_traffic - Download bandwidth
  • outgoing_traffic - Upload bandwidth
  • connection_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_name
  • context.device_platform
  • context.hardware_model
  • context.department
  • context.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!

/* ❌ May fail - beyond 8-day high-res limit */
execution.events during past 15d

/* ✅ Works - within 8-day limit */
execution.events during past 7d

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

Additional Resources