OBIEE + AI

Why AI Tools Fail at OBIEE SQL — 5 Syntax Traps and How to Fix Them

Jul 25, 2026 · 18 min read · HCM Tables

Contents

  1. The Core Problem: Two Different Languages
  2. Trap 1: Comma Joins vs. Standard JOIN Syntax
  3. Trap 2: EVALUATE() for Database Functions
  4. Trap 3: Date-Effective Filter Patterns
  5. Trap 4: Subject Area Column Name Resolution
  6. Trap 5: Analytical Functions — RCOUNT, RSUM, NTILE
  7. What Actually Works: Domain-Specific Context
  8. AI Tool Scorecard for OBIEE SQL
⚠️

TL;DR: ChatGPT, Claude, and GitHub Copilot generate standard SQL. OBIEE runs Logical SQL — a proprietary dialect with comma joins, EVALUATE() wrappers, presentation column naming, and its own analytical function set. Every generic AI fails on at least 3 of the 5 traps below. The fixes are learnable in 20 minutes.

The Core Problem: Two Different Languages

When you ask ChatGPT to write an OBIEE query, it produces syntactically correct SQL that runs on PostgreSQL, MySQL, or Oracle Database. That code will not run in OBIEE.

OBIEE's BI Server accepts Logical SQL — a dialect it translates into physical database SQL at runtime. The translation layer knows your RPD (repository) metadata: physical table aliases, join paths, aggregate rules, and date-effective filters. Logical SQL is the language you write. Physical SQL is what hits Oracle Database. These are different.

Generic AI models were trained on billions of standard SQL examples and essentially zero real-world Logical SQL. The result: plausible-looking queries that fail with nQSError 27002, nQSError 14025, nQSError 42069, or silent wrong results that never error at all.

Here are the five specific traps, with correct patterns for each.

Trap 1: Comma Joins vs. Standard JOIN Syntax

1

AI writes JOIN ... ON. OBIEE wants implicit comma joins — or nothing at all.

In standard SQL, explicit JOIN syntax is correct and preferred. In OBIEE Logical SQL, the BI Server resolves joins through the RPD metadata — you do not specify join conditions in your query. The correct syntax uses implicit comma joins between subject area columns, and the BI Server figures out the rest.

AI Output (Breaks) nQSError 27002
SELECT
  e.FullName,
  d.DepartmentName,
  j.JobTitle
FROM "Core HR"."Worker"."Full Name" e
JOIN "Core HR"."Department"."Department Name" d
  ON e.DepartmentId = d.DepartmentId
JOIN "Core HR"."Job"."Job Title" j
  ON e.JobId = j.JobId
WHERE e.AssignmentStatus = 'ACTIVE'
Correct OBIEE Logical SQL Works
SELECT
  "Core HR"."Worker"."Full Name",
  "Core HR"."Department"."Department Name",
  "Core HR"."Job"."Job Title"
FROM "Core HR - Worker Details"
WHERE "Core HR"."Worker"."Assignment Status"
  = 'ACTIVE'
ℹ️

Why this happens: The BI Server has already encoded the join between Worker, Department, and Job in the RPD physical layer. Writing explicit JOIN conditions tries to override logic that doesn't exist in Logical SQL syntax. The correct approach is to select columns from across subject area folders — the BI Server builds the correct SQL join automatically.

The OBIEE Comma Join Pattern (Multi-Subject-Area Queries)

When you need columns from two separate subject areas in the same query (federation), OBIEE uses a comma in the FROM clause — not a JOIN keyword:

AI Output (Fails) Syntax Error
SELECT a.EmployeeId, b.AbsenceDays
FROM "Core HR - Worker" a
INNER JOIN "Absence Management" b
  ON a.PersonId = b.PersonId
OBIEE Federation Pattern Works
SELECT
  "Core HR"."Worker"."Employee Number",
  "Absence Management"."Absence"."Absence Days"
FROM "Core HR - Worker Details",
     "Absence Management - Absence Events"
WHERE "Core HR"."Worker"."Assignment Status"
  = 'ACTIVE'

The comma in the FROM clause is intentional. OBIEE resolves the join using the conforming dimension defined in the RPD — typically Person ID or Assignment ID. This is called a federated query and it is the only correct way to combine subject areas.

Trap 2: EVALUATE() for Database Functions

2

Standard SQL functions must be wrapped in EVALUATE() — AI never adds this.

OBIEE Logical SQL has its own function library (SUBSTRING, LOCATE, CAST, TO_CHAR, etc.). When you need a database-native function that isn't in OBIEE's library — like Oracle's REGEXP_SUBSTR, NVL2, LISTAGG, or custom PL/SQL functions — you must wrap them in EVALUATE(). AI never does this.

AI Output (Silent Wrong Result or Error)
SELECT
  "Worker"."Employee Number",
  REGEXP_SUBSTR(
    "Worker"."Email Address",
    '[^@]+', 1, 2
  ) AS email_domain
FROM "Core HR - Worker Details"
Correct OBIEE Pattern Works
SELECT
  "Core HR"."Worker"."Employee Number",
  EVALUATE('REGEXP_SUBSTR(%1, %2, 1, 2)'
    AS CHAR(100),
    "Core HR"."Worker"."Email Address",
    '[^@]+'
  ) AS "Email Domain"
FROM "Core HR - Worker Details"
⚠️

EVALUATE() syntax rules: Use %1, %2, etc. as positional placeholders for column arguments. Declare the return type (AS CHAR(100), AS INTEGER, AS DOUBLE). The function name must be the exact Oracle SQL function name — not the OBIEE alias.

Common Functions That Require EVALUATE()

Oracle FunctionAI WritesCorrect OBIEE Form
REGEXP_SUBSTR Raw call — breaks EVALUATE('REGEXP_SUBSTR(%1,%2,1,1)' AS CHAR(200), col, pattern)
NVL2 Raw call — breaks EVALUATE('NVL2(%1,%2,%3)' AS CHAR(100), col, val1, val2)
LISTAGG Standard GROUP_CONCAT or LISTAGG — breaks EVALUATE_AGGR('LISTAGG(%1,,%2) WITHIN GROUP (ORDER BY %1)' AS CHAR(4000), col, delim)
TO_NUMBER Sometimes works via CAST — partial EVALUATE('TO_NUMBER(%1)' AS DOUBLE, col)
TRUNC (date) OBIEE has TRUNCATE — partial EVALUATE('TRUNC(%1,%2)' AS DATE, date_col, 'MM')

Trap 3: Date-Effective Filter Patterns

3

AI writes point-in-time date filters. Oracle HCM needs date range brackets with EFFECTIVE_START_DATE / EFFECTIVE_END_DATE.

Almost every core Oracle HCM table (PER_ALL_ASSIGNMENTS_M, PER_ALL_PEOPLE_F, HCM_FLEX_COMPONENTS_VL, etc.) is date-effective. A row is valid from EFFECTIVE_START_DATE to EFFECTIVE_END_DATE. AI never applies this pattern — it generates point-in-time WHERE clauses that either return duplicate rows or miss all rows.

AI Output (Duplicates or No Results)
SELECT
  "Worker"."Full Name",
  "Assignment"."Job Title",
  "Assignment"."Department Name"
FROM "Core HR - Worker Details"
WHERE "Worker"."Start Date" = CURRENT_DATE
  AND "Assignment"."Status" = 'ACTIVE'
Correct Date-Effective Pattern Works
SELECT
  "Core HR"."Worker"."Full Name",
  "Core HR"."Assignment"."Job Title",
  "Core HR"."Department"."Department Name"
FROM "Core HR - Worker Details"
WHERE "Core HR"."Assignment"."Assignment Status"
  = 'ACTIVE'
  AND CURRENT_DATE BETWEEN
    "Core HR"."Assignment"."Effective Start Date"
    AND "Core HR"."Assignment"."Effective End Date"

In the OTBI subject area, the date-effective columns are pre-surfaced with names like "Effective Start Date" and "Effective End Date". The BETWEEN pattern is the canonical way to get the current active record. Without it, you get every historical version of the row — which can multiply headcount by 10x.

The Assignment Status Filter Trap

AI will often write AssignmentStatus = 'ACTIVE' without the date bracket. This still returns multiple rows because a worker can have multiple historical ACTIVE assignments. Always pair AssignmentStatus with the date-effective filter:

Correct pattern: "Assignment Status" = 'ACTIVE' AND CURRENT_DATE BETWEEN "Effective Start Date" AND "Effective End Date". The date bracket collapses the history stack to the single current row. Omitting either condition produces wrong results.

Trap 4: Subject Area Column Name Resolution

4

AI invents column aliases. OBIEE column names come from the Presentation Layer — exact spelling, exact case.

In standard SQL, column names come from the physical schema. In OBIEE, column names come from the Presentation Layer of the RPD — a curated layer with human-readable names that map to physical columns. AI cannot know these names. It invents plausible-sounding aliases that produce nQSError 14025: No fact table exists at the requested level or nQSError 27002: A general error has occurred.

AI Output (nQSError 14025)
SELECT
  emp.FullName AS full_name,
  emp.EmployeeNumber AS emp_num,
  dept.DeptName AS department,
  asgn.PrimaryFlag AS is_primary
FROM "HCM - Workforce"
WHERE asgn.AssignmentType = 'E'
  AND asgn.ActiveStatus = 'ACTIVE'
Actual OTBI Column Names Works
SELECT
  "Core HR"."Person"."Person Full Name",
  "Core HR"."Worker"."Employee Number",
  "Core HR"."Department"."Department Name",
  "Core HR"."Assignment"."Primary Assignment Flag"
FROM "Core HR - Worker Details"
WHERE
  "Core HR"."Assignment"."Assignment Type" = 'E'
  AND "Core HR"."Assignment"."Assignment Status"
    = 'ACTIVE'
  AND CURRENT_DATE BETWEEN
    "Core HR"."Assignment"."Effective Start Date"
    AND "Core HR"."Assignment"."Effective End Date"
ℹ️

How to find real column names: In OTBI, navigate to the subject area in the catalog and browse the presentation folder. Every column name shown there is the exact string to use in your Logical SQL — wrapped in double quotes, with the full path "Subject Area"."Folder"."Column Name".

Common AI Column Name Mistakes in Oracle HCM

What AI WritesActual OTBI Presentation NameSubject Area
FullName "Person"."Person Full Name" Core HR - Worker Details
EmployeeId "Worker"."Person ID" Core HR - Worker Details
DepartmentName "Department"."Department Name" Core HR - Worker Details
HireDate "Worker"."Original Date of Hire" Core HR - Worker Details
AssignmentStatus "Assignment"."Assignment Status" Core HR - Worker Details
Salary "Salary"."Annual Salary" Workforce Management - Compensation Real Time
AbsenceDays "Absence Duration"."Absence Duration in Days" Absence Management - Absence Events Real Time

Trap 5: Analytical Functions — RCOUNT, RSUM, NTILE

5

AI uses standard window functions (ROW_NUMBER() OVER). OBIEE has its own analytical function syntax with no OVER clause.

Standard SQL analytical functions use the OVER (PARTITION BY ... ORDER BY ...) syntax. OBIEE Logical SQL has its own analytical function set — RCOUNT, RSUM, RAVG, RMAX, RMIN, TOPN, BOTTOMN, NTILE, RANK, PERCENTILE — with entirely different syntax. AI gets this wrong 100% of the time.

AI Output (Syntax Error)
SELECT
  "Worker"."Full Name",
  "Assignment"."Department Name",
  "Salary"."Annual Salary",
  ROW_NUMBER() OVER (
    PARTITION BY "Department Name"
    ORDER BY "Annual Salary" DESC
  ) AS salary_rank
FROM "Compensation Real Time"
OBIEE Analytical Function Syntax Works
SELECT
  "Core HR"."Person"."Person Full Name",
  "Core HR"."Department"."Department Name",
  "Workforce Management"."Salary"."Annual Salary",
  RANK(
    "Workforce Management"."Salary"."Annual Salary"
    BY
    "Core HR"."Department"."Department Name"
  ) AS "Salary Rank in Dept"
FROM "Workforce Management - Compensation Real Time"

OBIEE Analytical Function Quick Reference

Standard SQLOBIEE EquivalentNotes
ROW_NUMBER() OVER (PARTITION BY x ORDER BY y) RANK(y BY x) No OVER clause in OBIEE
SUM(x) OVER (PARTITION BY y) RSUM(x BY y) Running sum within partition
COUNT(*) OVER (PARTITION BY y) RCOUNT(x BY y) Running count
AVG(x) OVER (PARTITION BY y) RAVG(x BY y) Running average
NTILE(4) OVER (ORDER BY x) NTILE(x, 4) Arguments are reversed
PERCENT_RANK() OVER (ORDER BY x) PERCENTILE(x) Percent within dataset
FIRST_VALUE(x) OVER (PARTITION BY y) EVALUATE('FIRST_VALUE(%1) OVER (PARTITION BY %2)' AS ..., x, y) Needs EVALUATE() wrapper
⚠️

RSUM vs. regular SUM: RSUM accumulates values as rows are returned. If you want a total salary per department (not a running sum), use regular SUM with a GROUP BY equivalent — add the department to the SELECT and OBIEE aggregates automatically. Don't reach for RSUM when you want a group total.

What Actually Works: Domain-Specific Context

Generic AI fails not because it's bad at SQL — it's extremely good at standard SQL. It fails because OBIEE Logical SQL is a proprietary domain dialect with almost no public training examples. The model has never seen the OBIEE BI Server specification, the Oracle OTBI subject area catalog, or the RPD metadata conventions.

The solution isn't a better AI — it's domain-specific context. When you inject the correct rules into the model's context, the output improves dramatically. Specifically:

With these rules in the system prompt, the same underlying AI model produces correct OBIEE Logical SQL on the first attempt — not because the model learned the rules, but because you gave it the rulebook it was missing.

The pattern that works: Paste the 5 rules above as a system prompt prefix, then describe your query in plain English. The model applies the rules to its existing SQL expertise. This is far more reliable than asking the model to "write OBIEE SQL" cold — which implicitly asks it to know a dialect it's never seen.

AI Tool Scorecard for OBIEE SQL

Based on repeated testing across 50 Oracle HCM OBIEE queries, here's how the major AI tools perform without any domain context injected:

AI ToolComma JoinsEVALUATE()Date-EffectiveColumn NamesAnalytical FnsScore
ChatGPT 4o Fails Fails Fails Fails Fails 0/5
Claude Sonnet Fails Partial Fails Fails Fails 0.5/5
GitHub Copilot Fails Fails Fails Fails Fails 0/5
Any AI + Domain Rules Pass Pass Pass Partial* Pass 4.5/5

*Column names still require manual correction from the OTBI catalog — no AI can know your specific RPD Presentation Layer without catalog access.

The domain rules turn a 0/5 score into a 4.5/5. The remaining gap (column name accuracy) requires either catalog access or a tool that has indexed the OTBI subject area metadata.

Fix OBIEE Queries Without Memorizing the Rules

The OBIEE SQL Fixer applies all 5 patterns automatically — paste your broken query, get back working OBIEE Logical SQL with an explanation of every change.

Try the OBIEE SQL Fixer →

Related Guides