Logical SQL Reference¶
Last updated: 2026-04-27 Tags: logical SQL, LSQL, query language, functions, time series, variables, XSA, syntax
📖 Full Oracle Documentation: Logical SQL Reference Guide
Summary¶
Logical SQL is Oracle Analytics Cloud's query language for the Presentation/Semantic layer. It looks like standard SQL but operates on Subject Area column names rather than physical table/column names. The BI Server translates Logical SQL into optimized physical SQL for the underlying database. All OAC analyses, Workbook data requests, and API queries ultimately use Logical SQL.
Basic Syntax¶
SELECT
"Subject Area"."Folder"."Column1",
"Subject Area"."Folder"."Measure1"
FROM "Subject Area"
WHERE "Subject Area"."Folder"."Column1" = 'Value'
ORDER BY "Subject Area"."Folder"."Measure1" DESC
FETCH FIRST 100 ROWS ONLY
Naming Convention¶
- Three-part name:
"Subject Area"."Folder"."Column" - Always use double quotes
- Case-sensitive for Subject Area and folder names
- Spaces are allowed inside double quotes
SELECT Clause¶
-- Attribute
SELECT "Sales"."Product"."Category"
-- Measure (aggregated automatically)
SELECT "Sales"."Facts"."Revenue"
-- Expression / calculation
SELECT "Sales"."Facts"."Revenue" / "Sales"."Facts"."Units" AS "Avg Price"
-- Distinct count
SELECT COUNT(DISTINCT "Sales"."Customer"."Customer ID")
WHERE Clause¶
-- Equality
WHERE "Sales"."Time"."Year" = 2024
-- IN list
WHERE "Sales"."Product"."Category" IN ('Electronics', 'Clothing')
-- Range
WHERE "Sales"."Facts"."Revenue" BETWEEN 10000 AND 50000
-- LIKE
WHERE "Sales"."Customer"."Name" LIKE 'Oracle%'
-- NULL check
WHERE "Sales"."Order"."Ship Date" IS NULL
-- Multiple conditions
WHERE "Sales"."Time"."Year" = 2024
AND "Sales"."Product"."Category" = 'Electronics'
ORDER BY and FETCH¶
ORDER BY "Sales"."Facts"."Revenue" DESC
-- Limit rows
FETCH FIRST 50 ROWS ONLY
-- Pagination
FETCH FIRST 50 ROWS ONLY OFFSET 100
Aggregate Functions¶
SUM("Sales"."Facts"."Revenue")
COUNT("Sales"."Order"."Order ID")
COUNT(DISTINCT "Sales"."Customer"."Customer ID")
AVG("Sales"."Facts"."Revenue")
MIN("Sales"."Facts"."Revenue")
MAX("Sales"."Facts"."Revenue")
MEDIAN("Sales"."Facts"."Revenue")
💡 Tip: Measures defined in the Semantic Model have predefined aggregation. Simply SELECTing a measure applies its default aggregation. Only use explicit aggregation functions for custom calculations.
Time Series Functions¶
AGO — Period Comparison¶
-- Revenue same period 1 year ago
AGO("Sales"."Facts"."Revenue", "Sales"."Time"."Month", 12)
-- Revenue previous quarter
AGO("Sales"."Facts"."Revenue", "Sales"."Time"."Quarter", 1)
TODATE — Year-to-Date / Period-to-Date¶
-- Year-to-date revenue
TODATE("Sales"."Facts"."Revenue", "Sales"."Time"."Year")
-- Quarter-to-date
TODATE("Sales"."Facts"."Revenue", "Sales"."Time"."Quarter")
PERIODROLLING — Rolling Window¶
-- Rolling 3-month sum
PERIODROLLING("Sales"."Facts"."Revenue", -2, 0)
-- Rolling 12-month average
PERIODROLLING("Sales"."Facts"."Revenue", -11, 0) / 12
⚠️ Warning: Time series functions require a time dimension column in the SELECT. They will error if no time grain is selected.
Ranking Functions¶
-- Rank by measure (1 = highest)
RANK("Sales"."Facts"."Revenue" BY "Sales"."Product"."Category")
-- Dense rank (no gaps)
DENSE_RANK("Sales"."Facts"."Revenue" BY "Sales"."Product"."Category")
-- Top N filter
TOPN("Sales"."Facts"."Revenue", 10)
-- Bottom N filter
BOTTOMN("Sales"."Facts"."Revenue", 5)
-- Percentile rank (0-100)
NTILE("Sales"."Facts"."Revenue", 4) -- Quartile
Running Aggregates (Cumulative)¶
-- Running sum
RSUM("Sales"."Facts"."Revenue")
-- Running average
RAVG("Sales"."Facts"."Revenue")
-- Running count
RCOUNT("Sales"."Facts"."Revenue")
-- Running minimum / maximum
RMIN("Sales"."Facts"."Revenue")
RMAX("Sales"."Facts"."Revenue")
String Functions¶
UPPER("Sales"."Customer"."Name")
LOWER("Sales"."Customer"."Name")
INITCAP("Sales"."Customer"."Name")
LENGTH("Sales"."Customer"."Name")
SUBSTRING("Sales"."Customer"."Name", 1, 10)
CONCAT("First Name", ' ', "Last Name")
REPLACE("Sales"."Product"."SKU", '-', '')
TRIM("Sales"."Customer"."Name")
LEFT("Sales"."Customer"."Name", 5)
RIGHT("Sales"."Customer"."Name", 3)
Numeric Functions¶
ROUND("Sales"."Facts"."Revenue", 2)
CEIL("Sales"."Facts"."Revenue")
FLOOR("Sales"."Facts"."Revenue")
ABS("Sales"."Facts"."Variance")
MOD("Sales"."Order"."Order ID", 10)
POWER("Sales"."Facts"."Growth", 2)
SQRT("Sales"."Facts"."Variance")
LOG("Sales"."Facts"."Revenue", 10)
EXP("Sales"."Facts"."Rate")
Date Functions¶
-- Extract parts
YEAR("Sales"."Time"."Order Date")
MONTH("Sales"."Time"."Order Date")
DAYOFMONTH("Sales"."Time"."Order Date")
DAYOFWEEK("Sales"."Time"."Order Date")
QUARTER("Sales"."Time"."Order Date")
HOUR("Sales"."Time"."Order Timestamp")
-- Difference between dates
TIMESTAMPDIFF(SQL_TSI_DAY, "Ship Date", "Order Date")
TIMESTAMPDIFF(SQL_TSI_MONTH, "Start Date", "End Date")
-- Date arithmetic
TIMESTAMPADD(SQL_TSI_DAY, 7, "Sales"."Time"."Order Date")
-- Current date/time
CURRENT_DATE
CURRENT_TIMESTAMP
NOW()
Conditional (CASE/IF)¶
-- Simple CASE
CASE "Sales"."Product"."Category"
WHEN 'Electronics' THEN 'Tech'
WHEN 'Clothing' THEN 'Apparel'
ELSE 'Other'
END
-- Searched CASE
CASE
WHEN "Sales"."Facts"."Revenue" > 100000 THEN 'High'
WHEN "Sales"."Facts"."Revenue" > 50000 THEN 'Medium'
ELSE 'Low'
END
-- IF shorthand
IFNULL("Sales"."Facts"."Revenue", 0)
NULLIF("Sales"."Facts"."Cost", 0)
COALESCE("Sales"."Facts"."Alt Revenue", "Sales"."Facts"."Revenue", 0)
Variables in Logical SQL¶
-- Session variable
VALUEOF(NQ_SESSION.USER)
VALUEOF(NQ_SESSION.REGION)
-- Repository variable
VALUEOF(CURRENT_FISCAL_YEAR)
-- Presentation variable (set by dashboard prompts)
'@{myPromptVar}{default_value}'
-- Request variable (pass in query)
VALUEOF(NQ_REQUEST.MY_VAR)
XSA (Extended Subject Area / Dataset) Queries¶
-- Basic XSA query
SELECT
XSA('admin'.'sales_upload')."Sheet1"."Region",
SUM(OVERRIDEAGGR(XSA('admin'.'sales_upload')."Sheet1"."Revenue"))
FROM XSA('admin'.'sales_upload')
ORDER BY 2 DESC
FETCH FIRST 50 ROWS ONLY
💡 Tip: Use
OVERRIDEAGGR()around XSA measures because they don't have predefined aggregation rules like Subject Area measures do.
FILTER Function (Conditional Aggregation)¶
-- Revenue only for Electronics category
FILTER("Sales"."Facts"."Revenue" USING "Sales"."Product"."Category" = 'Electronics')
-- Count of orders above $1000
FILTER(COUNT("Sales"."Order"."Order ID") USING "Sales"."Facts"."Revenue" > 1000)
EVALUATE (Native DB Function Passthrough)¶
Call database-specific functions not available in Logical SQL:
-- Oracle-specific regex
EVALUATE('REGEXP_SUBSTR(%1, %2)', "Sales"."Customer"."Email", '@(.+)$')
-- Oracle-specific analytic function
EVALUATE('LISTAGG(%1, %2) WITHIN GROUP (ORDER BY %3)',
"Sales"."Product"."Name", ', ', "Sales"."Facts"."Revenue")
⚠️ Warning:
EVALUATEbypasses the Logical SQL layer and sends raw SQL to the database. It is DB-specific and not portable.
Common Query Patterns¶
YTD vs Prior YTD Comparison¶
SELECT
"Sales"."Time"."Year",
TODATE("Sales"."Facts"."Revenue", "Sales"."Time"."Year") AS "YTD Revenue",
AGO(TODATE("Sales"."Facts"."Revenue", "Sales"."Time"."Year"),
"Sales"."Time"."Year", 1) AS "Prior YTD Revenue"
FROM "Sales"
Top 10 with Percentage of Total¶
SELECT
"Sales"."Product"."Product Name",
"Sales"."Facts"."Revenue",
"Sales"."Facts"."Revenue" / SUM("Sales"."Facts"."Revenue") * 100 AS "% of Total"
FROM "Sales"
WHERE TOPN("Sales"."Facts"."Revenue", 10) <= 10
ORDER BY "Sales"."Facts"."Revenue" DESC