Example usecases

These examples illustrate how Unified Data Access data can be used to answer real operational questions. Each use case describes a scenario, the tables involved, and a sample query.

Notes

⚠️

In all examples tables: users, tickets, interactions, and events need to be replaced by the name of customer's tables.


⚠️

Data Delay Notice

The data in BigQuery is delayed by approximately 1 day compared to what is visible in the Supervisor Dashboard/Table views

Because of this delay, mutable data — such as interactions that change status (e.g., from ongoing to deflected or transferred) — may not reflect the current state visible in the admin panel.

What this means:

  • Ticket counts, statuses, and metrics shown in these queries represent historical snapshots, not real-time data
  • If you see a discrepancy between BigQuery results and the Supervisor, the Supervisor shows the most current state

Monitoring daily chatbot performance over the past week

A need to track how AI Agent is performing day-by-day — how many conversations it's handling, how many it's deflecting versus transferring to human agents, and whether the transfer rate is trending up or down.

Tables used: interactions

By filtering interactions to a specific chatbot and grouping by date, it is possible to see daily trends in conversation volume and deflection success. The timezone adjustment (TIMESTAMP_ADD(created_at, INTERVAL 2 HOUR)) ensures dates align with customer's local time.

-- Step 1: Aggregate daily interaction metrics by status
WITH daily_metrics AS (
  SELECT
    -- Convert UTC timestamp to local timezone (UTC+2) before extracting date
    -- This ensures dates align with business hours in the local timezone
    DATE(TIMESTAMP_ADD(created_at, INTERVAL 2 HOUR)) AS interaction_date,
    
    -- Count all interactions for the day
    COUNT(*) AS total_interactions,
    
    -- BigQuery COUNTIF: counts rows where condition is true (more efficient than SUM(CASE))
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred,
    
    -- Calculate transfer rate as percentage
    -- Division in BigQuery automatically handles float conversion
    ROUND(COUNTIF(status = 'transferred') / COUNT(*) * 100, 1) AS transfer_rate_pct
    
  FROM interactions
  WHERE 
    owner_user_id = 'enter chatbotID here'  -- Filter to specific chatbot ID
    -- DATE_SUB: BigQuery function to subtract days from current date
    -- CURRENT_DATE() returns today's date (compared at midnight UTC when used with timestamps)
    AND created_at >= TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
  
  GROUP BY interaction_date
)

-- Step 2: Return final results ordered by most recent first
SELECT
  interaction_date,
  total_interactions,
  deflected,
  transferred,
  transfer_rate_pct
FROM daily_metrics
ORDER BY interaction_date DESC

This returns one row per day showing the total number of interactions, how many were successfully deflected (resolved by AI), how many were transferred to human agents, and the transfer rate as a percentage. Days are sorted from most recent to oldest, making it easy to spot recent changes in performance.


Monitoring daily chatbot performance by channel over the past week

A needs to track how AI Agent is performing day-by-day across different channels (chat, email, etc.) — understanding whether deflection rates vary by channel and which channels are driving the most volume.

Tables used: interactions

By filtering interactions to a specific chatbot and grouping by both date and channel, it is possible to see daily trends broken down by communication channel. The timezone adjustment (TIMESTAMP_ADD(created_at, INTERVAL 2 HOUR)) ensures dates align with the customer's local time.

-- Step 1: Aggregate daily interaction metrics by date and channel
WITH daily_channel_metrics AS (
  SELECT
    -- Convert UTC timestamp to local timezone (UTC+2) before extracting date
    -- This ensures dates align with business hours in the local timezone
    DATE(TIMESTAMP_ADD(created_at, INTERVAL 2 HOUR)) AS interaction_date,
    
    -- Group by channel type to compare performance across channels
    channel_id,
    
    -- Count all interactions for this date/channel combination
    COUNT(*) AS total_interactions,
    
    -- BigQuery COUNTIF: counts rows where condition is true (more efficient than SUM(CASE))
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred,
    
    -- Calculate transfer rate as percentage
    -- Division in BigQuery automatically handles float conversion
    ROUND(COUNTIF(status = 'transferred') / COUNT(*) * 100, 1) AS transfer_rate_pct
    
  FROM interactions
  WHERE 
    owner_user_id = 'enter chatbotID here'  -- Filter to specific chatbot
    -- DATE_SUB: BigQuery function to subtract days from current date
    AND created_at >= TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
  
  -- GROUP BY both dimensions to get one row per date/channel combination
  GROUP BY interaction_date, channel_id
)

-- Step 2: Return final results ordered by date (newest first), then channel
SELECT
  interaction_date,
  channel_id,
  total_interactions,
  deflected,
  transferred,
  transfer_rate_pct
FROM daily_channel_metrics
ORDER BY 
  interaction_date DESC,  -- Most recent dates first
  channel_id            -- Alphabetical channel order within each date

This returns one row per day per channel showing the total number of interactions, how many were successfully deflected (resolved by AI), how many were transferred to human agents, and the transfer rate as a percentage. Each day will have multiple rows — one for each channel type — making it easy to compare performance across chat, email, and other channels.


Tracking AI Agent performance across key dimensions

A need to understand which Flows or Knowledge Bases are driving the most interactions and which ones have the highest deflection rates — helping identify what's working well and where improvements are needed.

Tables used: interactions

By analyzing interactions across different dimensions separately, it is possible to identify performance patterns. Each query focuses on a single dimension to avoid inflated counts from cross-joining multiple array fields.

Performance by Flow

Shows which workflows (Flows) are being triggered most frequently and their deflection success rates.

-- Step 1: Flatten the started_processes array to create one row per flow
-- UNNEST is a BigQuery-specific operator that expands array fields into rows
WITH interaction_flows AS (
  SELECT
    i.interaction_id,
    i.status,
    process AS flow  -- Each element from started_processes array becomes a separate row
  FROM interactions i,
  -- UNNEST: Converts array into rows (like a lateral join in other SQL dialects)
  -- If an interaction has 3 flows, it creates 3 rows with the same interaction_id
  UNNEST(started_processes) AS process
  WHERE 
    i.owner_user_id = 'here enter chatbotID'  -- Filter to specific chatbot
    -- TIMESTAMP(): Convert DATE to TIMESTAMP for comparison with created_at
    AND i.created_at >= TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
),

-- Step 2: Aggregate metrics by flow
flow_metrics AS (
  SELECT
    flow,
    
    -- COUNT(DISTINCT) is crucial here because UNNEST can create duplicate interaction_ids
    -- if the same interaction started the same flow multiple times
    COUNT(DISTINCT interaction_id) AS total_interactions,
    
    -- COUNTIF counts rows where condition is true
    -- Note: This counts rows, not unique interactions (one interaction can have multiple status rows)
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred,
    
    -- Transfer rate calculation uses DISTINCT count in denominator
    -- to get accurate percentage (interactions, not flow instances)
    ROUND(COUNTIF(status = 'transferred') / COUNT(DISTINCT interaction_id) * 100, 1) AS transfer_rate_pct
    
  FROM interaction_flows
  GROUP BY flow
)

-- Step 3: Return results ordered by volume (highest traffic flows first)
SELECT
  flow,
  total_interactions,
  deflected,
  transferred,
  transfer_rate_pct
FROM flow_metrics
ORDER BY total_interactions DESC

Performance by Knowledge Base

Shows which knowledge sources are being used most frequently and their effectiveness in resolving customer issues.

-- Step 1: Flatten the used_knowledge_sources array to create one row per source
-- UNNEST is a BigQuery-specific operator that expands array fields into rows
WITH interaction_knowledge AS (
  SELECT
    i.interaction_id,
    i.status,
    knowledge_source  -- Each element from used_knowledge_sources array becomes a separate row
  FROM interactions i,
  -- UNNEST: Converts array into rows (like a lateral join in other SQL dialects)
  -- If an interaction uses 3 sources, it creates 3 rows with the same interaction_id
  UNNEST(used_knowledge_sources) AS knowledge_source
  WHERE 
    i.owner_user_id = 'here enter chatbotID'  -- Filter to specific chatbot
    -- DATE_SUB: BigQuery function to subtract days from current date
    AND i.created_at >= TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
),

-- Step 2: Aggregate metrics by knowledge source
knowledge_metrics AS (
  SELECT
    knowledge_source,
    
    -- COUNT(DISTINCT) is crucial here because UNNEST can create duplicate interaction_ids
    -- if the same interaction used the same knowledge source multiple times
    COUNT(DISTINCT interaction_id) AS total_interactions,
    
    -- COUNTIF counts rows where condition is true
    -- Note: This counts rows, not unique interactions (one interaction can have multiple status rows)
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred,
    
    -- Transfer rate calculation uses DISTINCT count in denominator
    -- to get accurate percentage (interactions, not knowledge source instances)
    ROUND(COUNTIF(status = 'transferred') / COUNT(DISTINCT interaction_id) * 100, 1) AS transfer_rate_pct
    
  FROM interaction_knowledge
  GROUP BY knowledge_source
)

-- Step 3: Return results ordered by volume (most-used knowledge sources first)
SELECT
  knowledge_source,
  total_interactions,
  deflected,
  transferred,
  transfer_rate_pct
FROM knowledge_metrics
ORDER BY total_interactions DESC

Each query returns one row per unique value in that dimension, showing total interactions, deflected count, transferred count, and transfer rate percentage. This makes it easy to identify high-volume areas and compare deflection performance across different aspects of the chatbot's configuration.


Monitoring standalone inbox ticket volume and resolution

A support manager needs to track daily ticket creation and resolution rates for their human-only inbox — tickets that go directly to human agents without any AI Agent interaction — to understand workload trends and team capacity needs.

Tables used: interactions, tickets

By identifying tickets that only have human_agent interactions (no AI handoff), it is possible to isolate the standalone inbox workload. The query uses CTEs to break down the logic: first identifying standalone tickets, then counting created and solved tickets separately, and finally joining them by date to show both metrics side-by-side.

-- Step 1: Identify standalone inbox tickets
-- These are tickets where only human_agent interacted (no AI Agent handoff)
WITH standalone_tickets AS (
  SELECT 
    ticket_id
  FROM interactions
  GROUP BY ticket_id
  HAVING 
    COUNT(DISTINCT owner) = 1       -- Only one type of owner
    AND MAX(owner) = 'human_agent'  -- And that owner is human_agent
),

-- Step 2: Count tickets created per day
created_daily AS (
  SELECT
    -- Convert UTC to local timezone (UTC+2) before extracting date
    DATE(TIMESTAMP_ADD(created_at, INTERVAL 2 HOUR)) AS ticket_date,
    COUNT(DISTINCT ticket_id) AS tickets_created
  FROM tickets
  WHERE 
    ticket_id IN (SELECT ticket_id FROM standalone_tickets)
    -- DATE_SUB: BigQuery function to subtract days from current date
    AND created_at >= TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
  GROUP BY ticket_date
),

-- Step 3: Count tickets solved (closed) per day
solved_daily AS (
  SELECT
    DATE(TIMESTAMP_ADD(updated_at, INTERVAL 2 HOUR)) AS solved_date,
    COUNT(DISTINCT ticket_id) AS tickets_solved
  FROM interactions
  WHERE 
    status = 'closed'
    AND owner = 'human_agent'
    AND ticket_id IN (SELECT ticket_id FROM standalone_tickets)
    AND updated_at >= TIMESTAMP(DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
  GROUP BY solved_date
)

-- Step 4: Combine created and solved metrics by date
-- Using FULL OUTER JOIN to include days where tickets were only created or only solved
SELECT
  -- COALESCE handles NULLs from FULL OUTER JOIN - takes first non-NULL value
  COALESCE(c.ticket_date, s.solved_date) AS date,
  COALESCE(c.tickets_created, 0) AS tickets_created,
  COALESCE(s.tickets_solved, 0) AS tickets_solved
FROM created_daily c
FULL OUTER JOIN solved_daily s 
  ON c.ticket_date = s.solved_date
ORDER BY date DESC

This returns one row per day with both created and solved counts. Days with only created tickets or only solved tickets will still appear (thanks to FULL OUTER JOIN), showing 0 for the missing metric. This makes it easy to spot backlogs (more created than solved) or catch-up days (more solved than created)


Measuring AI Agent transfer rate by contact reason

A CX operations manager wants to understand which contact reasons most often result in the AI Agent transferring to a human agent, in order to prioritize knowledge base improvements.

Tables used: interactions

Filtering for ai_agent interactions and grouping by final_contact_reason reveals which topics the AI Agent handles well (deflected) versus which consistently require human help (transferred).

-- Step 1: Aggregate interaction metrics by contact reason
WITH contact_reason_metrics AS (
  SELECT
    -- BigQuery STRUCT field access: final_contact_reason is a struct with nested fields
    -- Use dot notation to access the 'name' field within the struct
    final_contact_reason.name AS contact_reason,
    
    -- Count all interactions for this contact reason
    COUNT(*) AS total_interactions,
    
    -- BigQuery COUNTIF: counts rows where condition is true (cleaner than SUM(CASE))
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred,
    
    -- Calculate transfer rate as percentage
    -- High transfer rate = AI struggled with this contact reason
    ROUND(COUNTIF(status = 'transferred') / COUNT(*) * 100, 1) AS transfer_rate_pct
    
  FROM interactions
  WHERE 
    owner = 'ai_agent'  -- Only AI Agent interactions (exclude human_agent)
    -- String literal date automatically converted to TIMESTAMP for comparison
    -- BigQuery allows '2026-01-01' instead of requiring TIMESTAMP('2026-01-01')
    AND created_at >= '2026-01-01'
  
  GROUP BY contact_reason
)

-- Step 2: Return results ordered by transfer rate (highest first)
-- This surfaces the contact reasons where AI Agent struggles most
SELECT
  contact_reason,
  total_interactions,
  deflected,
  transferred,
  transfer_rate_pct
FROM contact_reason_metrics
-- ORDER BY transfer_rate_pct DESC prioritizes improvement opportunities
-- Contact reasons with high transfer rates should be reviewed first
ORDER BY transfer_rate_pct DESC

Contact reasons with high transfer rates are strong candidates for adding new knowledge policies or building dedicated Decision Engine processes.


Aggregating interactions by month

Tables used: interactions

This query aggregates interaction metrics by month to track trends over time. Includes deflection rates, CSAT scores, and AI vs human agent split

  • Time range: From January 1, 2025 onwards
    Ordering: Most recent months first
-- Step 1: Calculate monthly aggregations with all metrics
WITH monthly_metrics AS (
  SELECT
    -- BigQuery DATE_TRUNC: Truncates date to the start of the month (1st day)
    -- Returns YYYY-MM-01 for grouping all interactions in the same month
    DATE_TRUNC(DATE(created_at), MONTH) AS month_start_date,
    
    -- BigQuery FORMAT_DATE: Formats date as string using strftime-like patterns
    -- '%Y-%m' produces format like '2025-01' (useful for display/exports)
    FORMAT_DATE('%Y-%m', DATE(created_at)) AS year_month,
    
    -- EXTRACT(MONTH): Returns month number (1-12)
    EXTRACT(MONTH FROM created_at) AS month_number,
    
    -- FORMAT_DATE('%B'): Returns full month name ('January', 'February', etc.)
    -- Useful for human-readable reports
    FORMAT_DATE('%B', DATE(created_at)) AS month_name,
    
    -- Extract year for context (useful when viewing multi-year trends)
    EXTRACT(YEAR FROM created_at) AS year,
    
    -- Total interaction count for the month
    COUNT(*) AS total_interactions,
    
    -- BigQuery COUNTIF: Conditional counting (more efficient than SUM(CASE))
    COUNTIF(owner = 'ai_agent') AS ai_agent_interactions,
    COUNTIF(owner = 'human_agent') AS human_agent_interactions,
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred,
    
    -- Deflection rate: percentage of AI Agent interactions that were successfully deflected
    -- NULLIF prevents division by zero when there are no AI Agent interactions
    ROUND(
      COUNTIF(status = 'deflected') / NULLIF(COUNTIF(owner = 'ai_agent'), 0) * 100, 
      1
    ) AS deflection_rate_pct,
    
    -- Average CSAT score for the month
    ROUND(AVG(quality_csat_score), 2) AS avg_csat_score
    
  FROM interactions
  WHERE 
    -- String literal date automatically converted to TIMESTAMP
    created_at >= '2025-01-01'
  
  -- GROUP BY all non-aggregated columns
  GROUP BY 
    month_start_date,
    year_month,
    month_number,
    month_name,
    year
)

-- Step 2: Return monthly metrics ordered by most recent first
SELECT
  month_start_date,
  year_month,
  month_number,
  month_name,
  year,
  total_interactions,
  ai_agent_interactions,
  human_agent_interactions,
  deflected,
  transferred,
  deflection_rate_pct,
  avg_csat_score
FROM monthly_metrics
ORDER BY month_start_date DESC

Analyzing the AI-to-human handoff experience

A team lead wants to understand how long customers wait in the queue after the AI Agent transfers them, and whether SLA targets are being met.

Tables used: interactions

Each ticket that involves a transfer typically has two or more interactions: one with owner = 'ai_agent' and status = 'transferred', followed by one with owner = 'queue' or owner = 'human_agent'. The queue and SLA fields on human agent interactions capture wait times and SLA outcomes.

-- Step 1: Extract AI Agent interactions with contact reason
-- These represent the initial AI interaction before transfer to human
WITH ai_interactions AS (
  SELECT
    ticket_id,
    -- BigQuery STRUCT field access: final_contact_reason is a nested struct
    -- Use dot notation to extract the 'name' field
    final_contact_reason.name AS contact_reason
  FROM interactions
  WHERE owner = 'ai_agent'
),

-- Step 2: Extract human agent interactions with performance metrics
-- These represent the human agent interaction after AI handoff
human_interactions AS (
  SELECT
    ticket_id,
    -- Convert milliseconds to seconds for readability
    -- BigQuery automatically handles integer division with type promotion
    statistics_queue_waiting_service_time_millis / 1000 AS queue_wait_seconds,
    
    -- SLA metrics: time-based targets for response and completion
    statistics_sla_first_response_time AS first_response_sla,
    statistics_sla_completion_time AS completion_sla,
    
    -- Queue assignment for routing analysis
    assigned_queue_id,
    
    created_at  -- Keep for filtering
  FROM interactions
  WHERE 
    owner = 'human_agent'
    -- String literal date automatically converted to TIMESTAMP
    AND created_at >= '2026-01-01'
),

-- Step 3: Join AI and human interactions to analyze handoff flow
-- Self-join pattern: same table joined to itself on ticket_id
handoff_metrics AS (
  SELECT
    h.ticket_id,
    a.contact_reason,              -- From AI interaction: why customer contacted
    h.queue_wait_seconds,          -- From human interaction: how long customer waited
    h.first_response_sla,          -- SLA for first human response
    h.completion_sla,              -- SLA for ticket completion
    h.assigned_queue_id            -- Which queue handled the ticket
  FROM human_interactions h
  -- INNER JOIN: Only tickets that have both AI and human interactions
  -- This isolates handoff scenarios (AI transferred to human)
  JOIN ai_interactions a
    ON h.ticket_id = a.ticket_id
)

-- Step 4: Return handoff analysis ordered by longest wait times
-- Longest waits appear first to identify queue bottlenecks
SELECT
  ticket_id,
  contact_reason,
  queue_wait_seconds,
  first_response_sla,
  completion_sla,
  assigned_queue_id
FROM handoff_metrics
-- ORDER BY DESC: Prioritize tickets with longest wait times
-- These represent the worst customer experience and potential process issues
ORDER BY queue_wait_seconds DESC

This query pairs each human agent interaction with the AI interaction on the same ticket, making it possible to see which contact reasons lead to the longest queue waits and whether SLA targets (success vs. exceeded) are being hit.


Comparing automation across channels

A data analyst wants to compare how the AI Agent performs on chat versus email — are deflection rates different? Are certain contact reasons channel-specific?

Tables used: tickets, interactions

-- Step 1: Extract AI Agent interactions with contact reason
WITH ai_agent_interactions AS (
  SELECT
    ticket_id,
    -- BigQuery STRUCT field access: final_contact_reason is a nested struct
    -- Use dot notation to extract the 'name' field
    final_contact_reason.name AS contact_reason,
    status,  -- 'deflected' or 'transferred'
    created_at
  FROM interactions
  WHERE 
    owner = 'ai_agent'  -- Only AI Agent interactions
    -- String literal date automatically converted to TIMESTAMP
    AND created_at >= '2026-01-01'
),

-- Step 2: Join with tickets to get channel information
-- This enriches interaction data with ticket-level attributes
channel_contact_data AS (
  SELECT
    t.channel_id,           -- Communication channel: chat, email, etc.
    a.contact_reason,         -- Why customer contacted support
    a.status                  -- Interaction outcome
  FROM ai_agent_interactions a
  -- INNER JOIN: Links interactions to their parent tickets
  -- Ticket provides context like channel_id not available in interaction record
  JOIN tickets t 
    ON a.ticket_id = t.ticket_id
),

-- Step 3: Aggregate metrics by channel and contact reason
-- Creates a matrix showing performance for each channel/reason combination
channel_contact_metrics AS (
  SELECT
    channel_id,
    contact_reason,
    
    -- Total AI Agent interactions for this channel/contact reason combination
    COUNT(*) AS ai_interactions,
    
    -- BigQuery COUNTIF: conditional counting (cleaner than SUM(CASE))
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred
    
  FROM channel_contact_data
  -- GROUP BY multiple dimensions: creates one row per unique combination
  -- Results show performance breakdown by both channel AND contact reason
  GROUP BY channel_id, contact_reason
)

-- Step 4: Return results grouped by channel, ordered by volume
SELECT
  channel_id,
  contact_reason,
  ai_interactions,
  deflected,
  transferred
FROM channel_contact_metrics
-- Multi-column ORDER BY: first group by channel, then sort by volume within each channel
-- This shows which contact reasons are most common in each channel
ORDER BY 
  channel_id,           -- Group channels together
  ai_interactions DESC    -- Highest volume contact reasons first within each channel

This reveals whether the AI Agent deflects at different rates on email versus chat, and whether certain contact reasons appear predominantly on one channel.


Understanding what knowledge sources the AI Agent relies on

A knowledge base manager wants to see which knowledge policies are used most frequently, and whether heavily-used policies correlate with positive or negative sentiment.

Tables used: interactions

The used_knowledge_sources field on AI Agent interactions contains an array of knowledge policy references. Unnesting this array and joining to the knowledge policies table provides a full picture.

-- Step 1: Filter to AI Agent interactions only
WITH ai_agent_interactions AS (
  SELECT
    interaction_id,
    -- Array field: used_knowledge_sources contains multiple knowledge base entries
    -- Each entry is a STRUCT with fields like 'name', 'id', etc.
    used_knowledge_sources,
    quality_sentiment,  -- Customer sentiment: POSITIVE, NEGATIVE, NEUTRAL
    status              -- Interaction outcome: deflected or transferred
  FROM interactions
  WHERE 
    owner = 'ai_agent'  -- Only AI Agent interactions
    -- String literal date automatically converted to TIMESTAMP
    AND created_at >= '2026-01-01'
),

-- Step 2: Flatten knowledge sources array into individual rows
-- UNNEST transforms array into separate rows (one row per array element)
interaction_knowledge_sources AS (
  SELECT
    i.interaction_id,
    -- BigQuery STRUCT field access: ks is a struct, extract 'name' field
    -- UNNEST aliases each array element as 'ks' (knowledge source struct)
    ks.name AS knowledge_source,
    i.quality_sentiment,
    i.status
  FROM ai_agent_interactions i,
  -- BigQuery UNNEST: Expands array into rows (implicit CROSS JOIN)
  -- If interaction uses 3 knowledge sources, this creates 3 rows
  UNNEST(i.used_knowledge_sources) AS ks
),

-- Step 3: Aggregate metrics by knowledge source
knowledge_source_metrics AS (
  SELECT
    knowledge_source,
    
    -- COUNT(*) counts rows, not unique interactions
    -- Since UNNEST creates multiple rows per interaction, this counts usage instances
    COUNT(*) AS times_used,
    
    -- BigQuery COUNTIF: conditional counting (more efficient than SUM(CASE))
    COUNTIF(quality_sentiment = 'POSITIVE') AS positive,
    COUNTIF(quality_sentiment = 'NEGATIVE') AS negative,
    COUNTIF(status = 'deflected') AS deflected,
    COUNTIF(status = 'transferred') AS transferred
    
  FROM interaction_knowledge_sources
  GROUP BY knowledge_source
)

-- Step 4: Return results ordered by usage frequency
SELECT
  knowledge_source,
  times_used,
  positive,
  negative,
  deflected,
  transferred
FROM knowledge_source_metrics
-- ORDER BY DESC: Most frequently used knowledge sources first
-- High usage + low deflection = opportunity to improve that knowledge base
ORDER BY times_used DESC

Knowledge policies that appear frequently alongside negative sentiment or high transfer rates may need to be rewritten or expanded.


Reviewing thumbs down interactions for quality improvement

A quality assurance team wants to find all AI Agent interactions that received negative feedback (thumbs down) to understand what went wrong and retrain.

Tables used: interactions

-- Step 1: Filter to AI Agent interactions within time range
WITH ai_agent_interactions AS (
  SELECT
    interaction_id,
    -- BigQuery STRUCT field access: final_contact_reason is a nested struct
    -- Use dot notation to extract the 'name' field
    final_contact_reason.name AS contact_reason,
    
    -- Quality metrics for feedback analysis
    quality_thumbs_down_count,   -- Number of thumbs down received
    quality_sentiment,            -- Overall sentiment: POSITIVE, NEGATIVE, NEUTRAL
    
    -- BigQuery ARRAY fields: These contain multiple values per interaction
    -- Not unnested here - returned as arrays for full context view
    recognized_intents,          -- Array of detected customer intents
    started_processes,           -- Array of workflows/flows triggered
    used_knowledge_sources,      -- Array of knowledge base articles referenced
    
    status,                      -- Interaction outcome: deflected or transferred
    created_at
  FROM interactions
  WHERE 
    owner = 'ai_agent'  -- Only AI Agent interactions (exclude human_agent)
    -- String literal date automatically converted to TIMESTAMP
    AND created_at >= '2026-01-01'
),

-- Step 2: Filter to interactions with negative feedback
-- Isolates problematic interactions for root cause analysis
negative_feedback_interactions AS (
  SELECT
    interaction_id,
    contact_reason,
    quality_thumbs_down_count,
    quality_sentiment,
    recognized_intents,
    started_processes,
    used_knowledge_sources,
    status,
    created_at
  FROM ai_agent_interactions
  WHERE 
    -- Greater than 0: At least one thumbs down received
    -- These represent customer dissatisfaction and need investigation
    quality_thumbs_down_count > 0
)

-- Step 3: Return problematic interactions for analysis
SELECT
  interaction_id,
  contact_reason,
  quality_thumbs_down_count,
  quality_sentiment,
  recognized_intents,
  started_processes,
  used_knowledge_sources,
  status
FROM negative_feedback_interactions
-- ORDER BY DESC: Most recent problems first
-- Recent issues may indicate new bugs or configuration changes
ORDER BY created_at DESC