Skip to main content
If you know SQL, you already know most of NQL. This page explains what’s the same, what’s different, and what NQL adds for data collaboration scenarios.

What stays the same

NQL uses standard SQL syntax for core query operations. If you’ve written SQL queries before, these patterns work exactly as you’d expect:

SELECT statements

SELECT
  column1,
  column2,
  UPPER(column3) AS transformed
FROM table_reference
WHERE condition
ORDER BY column1 DESC
LIMIT 100

Aggregations and grouping

SELECT
  category,
  COUNT(*) AS count,
  SUM(amount) AS total,
  AVG(score) AS average
FROM table_reference
GROUP BY category
HAVING COUNT(*) > 10

Joins

SELECT t1.column1, t2.column2
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.foreign_id
LEFT JOIN table3 t3 ON t1.id = t3.ref_id

Subqueries and CTEs

WITH summary AS (
  SELECT category, SUM(amount) AS total
  FROM sales
  GROUP BY category
)
SELECT * FROM summary WHERE total > 1000

Standard functions

Most SQL functions work as expected:
  • String: UPPER, LOWER, CONCAT, SUBSTRING, TRIM
  • Numeric: ABS, ROUND, FLOOR, CEIL
  • Date: CURRENT_DATE, DATE_TRUNC, EXTRACT
  • Aggregate: COUNT, SUM, AVG, MIN, MAX

What’s different

Table references use dataset IDs

In traditional SQL, you reference tables by name. In NQL, you reference datasets by their numeric ID within a schema:
SELECT * FROM users
SELECT * FROM analytics.events
The ID must be quoted because it’s numeric. The company_data schema contains your datasets.

Special data sources

NQL provides access to shared resources that don’t exist in traditional databases:
SourcePurpose
narrative.rosetta_stoneIdentity resolution across datasets
provider_slug."access_rule"Data shared via access rules
-- Identity resolution data
SELECT unique_id, event_timestamp
FROM narrative.rosetta_stone

-- Data from another company's access rule
SELECT * FROM partner_company."fitness_data"

Price filtering

Every dataset includes a _price_cpm_usd column representing the cost per 1,000 rows. This doesn’t exist in traditional databases:
-- Only get data priced at $1 or less per 1000 rows
SELECT *
FROM company_data."123"
WHERE _price_cpm_usd <= 1.00

EXPLAIN forecasts instead of plans

In traditional SQL, EXPLAIN shows the query execution plan. In NQL, EXPLAIN forecasts data availability and cost:
-- Shows: Seq Scan, Index Scan, costs, row estimates
EXPLAIN SELECT * FROM users WHERE age > 30
Use NQL’s EXPLAIN to preview what data matches your criteria before purchasing.

What NQL adds

Budget controls

NQL includes budget clauses to control data spending—a concept that doesn’t exist in traditional SQL:
CREATE MATERIALIZED VIEW "my_data"
AS (SELECT * FROM company_data."123")
BUDGET 50 USD

-- Recurring budget limits
LIMIT 100 USD PER CALENDAR_MONTH
LIMIT 10 USD PER CALENDAR_DAY

Materialized views with options

NQL’s CREATE MATERIALIZED VIEW includes scheduling, partitioning, and metadata options beyond what most databases offer:
CREATE MATERIALIZED VIEW "daily_summary"
REFRESH_SCHEDULE = '@daily'
DISPLAY_NAME = 'Daily Summary Report'
DESCRIPTION = 'Aggregated daily metrics'
EXPIRE = 'P30D'
TAGS = ('analytics', 'daily')
PARTITIONED_BY event_date DAY
AS (
  SELECT date, COUNT(*) as events
  FROM company_data."123"
  GROUP BY date
)
BUDGET 25 USD

QUALIFY clause

While some databases support QUALIFY, it’s not part of standard SQL. NQL includes it for filtering on window function results:
-- Keep only the most recent record per user
SELECT user_id, email, last_seen
FROM company_data."123"
QUALIFY ROW_NUMBER() OVER (
  PARTITION BY user_id
  ORDER BY last_seen DESC
) = 1
Without QUALIFY, you’d need a subquery:
-- Equivalent without QUALIFY (more verbose)
SELECT user_id, email, last_seen
FROM (
  SELECT
    user_id, email, last_seen,
    ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY last_seen DESC) AS rn
  FROM company_data."123"
) sub
WHERE rn = 1

DELTA tables

Query only changed records since the last execution—useful for incremental processing:
SELECT *
FROM DELTA(TABLE company_data."123")
This capability requires the platform to track changes, which isn’t standard SQL functionality.

Rosetta Stone integration

Access identity resolution through special columns:
SELECT
  company_data."1"._rosetta_stone.unique_id,
  company_data."1".user_attribute
FROM company_data."1"

Narrative-specific functions

Functions designed for data collaboration scenarios:
SELECT
  NORMALIZE_EMAIL(email) AS clean_email,
  NORMALIZE_PHONE('E164', phone, 'US') AS clean_phone,
  HASH(user_id, email) AS identity_hash,
  UNIVERSE_SAMPLE(user_id, 0.1) AS in_sample
FROM company_data."123"

SQL features not supported

Some SQL features aren’t available in NQL:
FeatureStatusAlternative
CREATE TABLENot supportedUse CREATE MATERIALIZED VIEW
INSERT/UPDATE/DELETENot supportedViews are the output mechanism
CREATE INDEXNot supportedPartitioning provides similar benefits
Stored proceduresNot supported
TransactionsNot supportedEach query is atomic
UNIONLimited supportSome restrictions apply

Dialect differences

NQL is transpiled to different database engines (Snowflake, Spark). The transpiler handles dialect differences, but awareness helps when debugging:

Date functions

-- NQL uses consistent syntax
WHERE event_date >= CURRENT_DATE - INTERVAL '30' DAY

NULL handling

NQL follows standard SQL NULL semantics, but the underlying engine may have subtle differences. The transpiler normalizes most cases.

Migration tips

Coming from PostgreSQL/MySQL

  1. Replace table names with dataset IDs: userscompany_data."123"
  2. Add budget clauses to materialized views
  3. Use EXPLAIN to forecast rather than plan
  4. Consider _price_cpm_usd filtering for cost control

Coming from Snowflake/BigQuery

  1. Dataset references use numeric IDs instead of table names
  2. Most functions work the same
  3. QUALIFY is fully supported
  4. Window functions work as expected

Coming from Spark SQL

  1. Similar syntax for complex types (arrays, structs, maps)
  2. UNNEST and LATERAL joins work as expected
  3. UDFs are replaced with Narrative-specific functions