Get started with GoogleSQL
This document helps security analysts, threat hunters, and detection engineers get started with querying data using GoogleSQL in Google Security Operations, supporting both Standard SQL and Pipe SQL syntax.
GoogleSQL provides an alternative to YARA-L 2.0. You can use GoogleSQL to perform ad hoc investigations, statistical aggregations, and complex data analysis on your security telemetry.
GoogleSQL capabilities
GoogleSQL follows a declarative query structure, which is composable and highly adaptable for security analytics. Unlike YARA-L 2.0, which is optimized for streaming detection rules and multi-event correlations, GoogleSQL excels at broad, complex data exploration and deep-dive analysis.
Structural differences
GoogleSQL uses standard ANSI-compliant clauses to define what data to retrieve rather than specifying a step-by-step sequence of execution.
The following table compares the structural differences between YARA-L 2.0 and GoogleSQL:
| Feature | YARA-L 2.0 | GoogleSQL |
|---|---|---|
| Query structure | Section-bound: Requires a specific logical flow (events, match, outcome, condition). |
Declarative: Uses clauses to define what data to retrieve, not the sequence of execution. |
| Core components | Sections: events, match, outcome, condition. |
Clauses: FROM, WHERE, GROUP BY, HAVING, SELECT, ORDER BY, LIMIT. |
Before you begin
Verify the following prerequisites and settings to start querying with GoogleSQL in Google SecOps:
- Access settings: You can toggle between YARA-L 2.0 and GoogleSQL using the language selector in the Query Editor user interface.
- Telemetry tables (schema): You can query the following built-in tables:
events: Contains Unified Data Model (UDM) event data (for example,principal.hostname). See UDM field dictionary reference.graph: Contains Entity Context Graph metadata (for example,entity.hostname). See Entity Context Graph overview.detections: Contains alerts and detections generated by threat detection rules (includes alerts and detections from both custom user-authored rules and curated Google-authored rules). See Detections schema reference.cases: Contains case and incident management data. See Cases and alerts schema.case_history: Contains audit history logs for cases. See Case history schema.
- Data tables: You can query custom, user-defined reference tables directly by table name (for example,
threat_intel_list). - API integration: You can run SQL queries programmatically by setting the
queryDialectparameter toSQLin theudmSearchAPI call:
queryDialect=SQL
Query types and user interface behavior
The fields you select in your query affect the behavior of the query and the interactivity of the user interface as described in the following sections.
Statistical queries for tabular results
When you project specific columns (for example, principal.hostname and metadata.event_type) or perform aggregations (such as COUNT(*)), the query returns a flat table optimized for summary data or counting. Although this lets you perform rapid statistical analysis, it disables interactive user interface features like the Event Viewer and Timeline widgets.
Example of a statistical query follows:
-- Generates a flat table with two specific fields
SELECT
MyEvent.principal.hostname AS MyHost,
MyEvent.metadata.event_type AS MyAction
FROM events AS MyEvent
Event queries for interactive investigation
Retrieving full structured UDM objects using SELECT * provides the complete context required to populate the interactive Event Viewer and Timeline widgets in the Google SecOps interface.
Example of an event query follows:
-- Enables interactive timeline and event viewer widgets
SELECT * FROM events
Recommended search strategies
Depending on your investigation goal, choose a query projection strategy that balances query performance with the level of detail required:
- Statistical analysis: Choose specific field selection when your goal is to quickly aggregate information, summarize data points, or build tables for a dashboard.
- Active investigations: Use
SELECT *when you are hunting threats or analyzing a particular incident and need to review the raw logs or follow a chronology in the interactive timeline.
Architecture and syntax options
GoogleSQL in Google SecOps supports two syntax styles for composing queries: Standard SQL and Pipe SQL.
Standard SQL syntax
In declarative SQL, you begin your query with the SELECT clause:
SELECT principal.hostname, target.hostname
FROM events
WHERE principal.hostname = "my-host"
Pipe SQL syntax
In this alternative syntax, you begin with the data source and sequentially pipe data into operators using the |> operator:
FROM events
|> WHERE principal.hostname = "my-host"
|> SELECT principal.hostname, target.hostname
Choosing the syntax
Pipe SQL offers several benefits for your security investigations. It uses a linear mental model that flows sequentially. You start with your data source and apply transformations in a specific order: Source ➔ Filter ➔ Aggregate ➔ Refine. This progression helps you write, read, and debug complex queries. For more information, see Pipe query syntax.
Key workflows and best practices
This section describes key workflows and best practices to follow when writing queries.
Repeated fields and UNNEST
Security and UDM datasets often store multiple values as an array (for example, security_result and target.ip). To filter, aggregate, or display elements from an array, flatten the array using the UNNEST operator.
When unnesting arrays, use LEFT JOIN UNNEST() to preserve rows where the array is empty or unpopulated. Using a comma join (,) or INNER JOIN with UNNEST() acts as a CROSS JOIN; if the array is empty, the entire event record is dropped from your results.
For example, consider a UDM event record for Workstation-01 where the target.ip array is empty:
| UDM field path | Value |
|---|---|
principal.hostname |
"Workstation-01" |
target.ip |
[] (Empty) |
The following query preserves the row. Workstation-01 appears in your results, and the target_ip column shows as NULL:
-- ✅ "Workstation-01" appears in the results with a NULL target_ip
SELECT principal.hostname, target.hostname, metadata.event_type, target_ip
FROM events
LEFT JOIN UNNEST(target.ip) AS target_ip;
In contrast, the following query uses an implicit CROSS JOIN. Because target.ip is empty for this event, the entire record for Workstation-01 is omitted from the results:
-- ❌ "Workstation-01" will NOT appear in the results at all
SELECT principal.hostname, target.hostname, metadata.event_type, target_ip
FROM
events,
UNNEST(target.ip) AS target_ip;
Nested arrays
If an array contains nested arrays (for example, the action array inside the security_result array), you can directly unnest the nested array path without needing to chain multiple UNNEST operators:
SELECT principal.hostname, target.hostname, metadata.event_type, action
FROM events
LEFT JOIN UNNEST(events.security_result.action) AS action
WHERE action = "BLOCK"
Quantified comparison predicates (ANY and ALL)
You can use quantified comparison predicates to compare a scalar value against elements in an array. This is an alternative to IN and EXISTS subqueries.
The ANY quantifier
This quantifier returns TRUE if the comparison is true for at least one element in the array:
SELECT DISTINCT principal.hostname, target.hostname
FROM events
WHERE "BLOCK" = ANY UNNEST(security_result.action);
The ALL quantifier
This quantifier returns TRUE if the comparison is true for every element in the array.
The following example shows the correct pattern for ALL:
SELECT DISTINCT principal.hostname
FROM events
WHERE ARRAY_LENGTH(target.ip) > 0
AND "127.0.0.1" = ALL UNNEST(target.ip);
Join guidelines
You can join telemetry tables and user-defined data tables based on the following rules:
You can use SELECT * for WHERE ... IN subquery joins:
SELECT * FROM events WHERE target.hostname IN (SELECT entity.hostname FROM graph)
GoogleSQL supports selecting specific columns:
SELECT A.metadata.product_name, B.entity.hostname
FROM events A
JOIN graph B ON A.principal.hostname = B.entity.hostname
Disallowed queries
You cannot combine wildcard selections (such as A.* or *) with explicit column projections in the same SELECT statement.
Example of a disallowed query is shown:
-- ❌ ERROR: Attempting to select all fields from table A alongside a specific field from table B
SELECT
MyEvents.*,
MyGraph.entity.hostname
FROM events AS MyEvents
JOIN graph AS MyGraph
ON MyEvents.principal.hostname = MyGraph.entity.hostname
SELECT *with UNNEST: Because UDM records and arrays can be wide, you cannot useSELECT *when flattening an array usingUNNEST.Example of a disallowed query is shown:
-- ❌ ERROR: Attempting to select all columns while flattening the security_result array SELECT * FROM events, UNNEST(events.security_result) AS MySecurityResult WHERE MySecurityResult.action = 'BLOCK'Wildcard selections in standard table joins: You cannot use
SELECT *for standard table joins. You must explicitly specify the columns you want to return from the joined tables.Example of a disallowed query is shown:
-- ❌ ERROR: Attempting a standard table join without specifying specific column projections SELECT * FROM events AS MyEvents JOIN graph AS MyGraph ON MyEvents.principal.user.userid = MyGraph.entity.user.userid
Limitations and quotas
When writing queries in GoogleSQL, take note of these limitations:
- Single statement only: You can use only a single query statement. You can use CTEs (Common Table Expressions) and subqueries, but you cannot use multiple statements separated by semicolons.
- Read-only: You cannot use data modification commands (such as
INSERT,UPDATE,DELETE) or data definition commands (such asCREATE TABLE…). - System-appended limit: All searches have a limit of 1,000,000 rows. Any limit that you specify that exceeds this value is overridden. For more information, see Search large result sets.
SELECT *projections in joins and UNNEST: You cannot useSELECT *for standard join queries or when you combine it withUNNEST. Explicitly specify the required projection columns instead.
What's next
For more information about querying and analyzing data in Google SecOps, see the following:
- To look up Unified Data Model columns, see the UDM field dictionary reference.
- To learn about advanced search capabilities, see Understand search.
- To learn more about pipe syntax in GoogleSQL, see Pipe query syntax.
Need more help? Get answers from Community members and Google SecOps professionals.