惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
C
Check Point Blog
GbyAI
GbyAI
Jina AI
Jina AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 司徒正美
Hacker News - Newest:
Hacker News - Newest: "LLM"
V2EX - 技术
V2EX - 技术
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
F
Full Disclosure
S
Secure Thoughts
WordPress大学
WordPress大学
博客园 - Franky
N
News and Events Feed by Topic
雷峰网
雷峰网
www.infosecurity-magazine.com
www.infosecurity-magazine.com
H
Hacker News: Front Page
N
Netflix TechBlog - Medium
Forbes - Security
Forbes - Security
TaoSecurity Blog
TaoSecurity Blog
C
CERT Recently Published Vulnerability Notes
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Vercel News
Vercel News
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog
Security Latest
Security Latest
Attack and Defense Labs
Attack and Defense Labs
The Register - Security
The Register - Security
Spread Privacy
Spread Privacy
H
Help Net Security
宝玉的分享
宝玉的分享
L
LangChain Blog
MongoDB | Blog
MongoDB | Blog
I
Intezer
Schneier on Security
Schneier on Security
Latest news
Latest news
Y
Y Combinator Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
InfoQ
A
About on SuperTechFans
T
Tor Project blog
Microsoft Security Blog
Microsoft Security Blog
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
Microsoft Azure Blog
Microsoft Azure Blog
T
Threat Research - Cisco Blogs
S
Schneier on Security
Cisco Talos Blog
Cisco Talos Blog
H
Heimdal Security Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
ABAP CDS Views Series — Part 9: Hierarchies, Recursive Structures, and Tree-Based Data Modeling in SAP S/4HANA
Oktay Ates · 2026-05-06 · via DEV Community

ABAP CDS Views Series — Part 9: Hierarchies, Recursive Structures, and Tree-Based Data Modeling in SAP S/4HANA

If you’ve been following this series, you already know how powerful CDS views can be for data modeling in SAP S/4HANA. We’ve covered everything from associations and joins to performance optimization and buffering strategies. But there’s one topic that consistently trips up even experienced ABAP developers: hierarchical data modeling using CDS views. Today, we’re going deep into recursive structures, hierarchy annotations, and how to model tree-based data properly in S/4HANA.

Whether you’re dealing with cost center hierarchies, bill of materials (BOM) structures, organizational hierarchies, or product category trees, understanding how SAP’s CDS hierarchy concept works will save you hours of painful workarounds and give your Fiori apps a serious analytical edge.

Let’s dive in.

Why Hierarchical Data Is Notoriously Tricky in SAP

Let me be honest with you: hierarchies in SAP have historically been a mess. Between SET/GET parameters, SETLEAF/SETNODE logic in classic ABAP, and the painful JOIN gymnastics needed to traverse parent-child relationships, many developers simply gave up and flattened everything into a table — paying the performance price later.

With SAP HANA as the underlying database engine and CDS as the modeling layer, that era is thankfully behind us. The HANA engine natively supports recursive SQL and hierarchical functions. CDS exposes these capabilities through a dedicated DEFINE HIERARCHY syntax that is both elegant and surprisingly powerful when used correctly.

The challenge? Documentation is sparse, examples are often oversimplified, and the interaction between CDS hierarchy views and Fiori/OData consumption has some sharp edges. That’s exactly why we’re here.

Understanding the CDS Hierarchy Building Blocks

Before writing a single line of code, let’s align on the three core concepts you need to master.

1. The Source View (Parent-Child Association)

Every hierarchy starts with a flat, relational source — a CDS view or table that contains a node key and a reference to its parent. Think of the classic KOSTL (Cost Center) to KOKHIER (Cost Center Hierarchy) relationship, or a custom product category table where each category row holds a PARENT_ID.

2. The Hierarchy View (DEFINE HIERARCHY)

This is a special CDS entity type — not a regular view, not an association. It tells the HANA engine: “treat this as a recursive traversal.” You define the parent and child keys, configure the traversal direction, and optionally set cycle handling.

3. The Consumption Layer

You don’t query a hierarchy view directly in ABAP Open SQL in the traditional sense. Hierarchy views are consumed via the HIERARCHY function in ABAP SQL, or exposed through OData using specific VDM annotations for Fiori tree tables and drilldown scenarios.

Building Your First CDS Hierarchy: A Practical Example

Let’s walk through a realistic example — modeling a product category hierarchy. Imagine an e-commerce or retail SAP implementation where categories nest: Electronics → Mobile Phones → Smartphones → Android Phones.

Step 1: Define the Source Data View

Start with the base CDS view that maps to your category table. In real S/4HANA projects, this often wraps a custom Z-table or an SAP standard object.


@AbapCatalog.sqlViewName: 'ZPROD_CAT_BASE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Product Category Base View'
define view ZCDS_PRODUCT_CATEGORY_BASE
  as select from zprod_category as pc
{
  key pc.category_id    as CategoryId,
      pc.category_name  as CategoryName,
      pc.parent_id      as ParentId,
      pc.level          as HierarchyLevel,
      pc.sort_order     as SortOrder,
      pc.is_active      as IsActive
}
where pc.is_active = 'X'

Enter fullscreen mode Exit fullscreen mode

What’s happening here: This is a straightforward projection view. The critical columns are CategoryId (the node key) and ParentId (the reference to the parent node). Root nodes will have ParentId as null or a designated root marker value — decide this consistently in your data model.

Step 2: Define the CDS Hierarchy View

Now comes the interesting part. The DEFINE HIERARCHY keyword creates a special entity that encapsulates the recursive traversal logic.


@EndUserText.label: 'Product Category Hierarchy Definition'
define hierarchy ZCDS_PROD_CAT_HIERARCHY
  as parent child hierarchy(
    source        ZCDS_PRODUCT_CATEGORY_BASE
    child to parent association _ParentCategory
    start where   ParentId is initial
    siblings order by SortOrder ascending
  )
{
  CategoryId,
  CategoryName,
  ParentId,
  HierarchyLevel,
  SortOrder,
  $node.node_id         as NodeId,
  $node.parent_id       as NodeParentId,
  $node.hierarchy_level as NodeLevel,
  $node.is_root         as IsRoot,
  $node.is_leaf         as IsLeaf
}

Enter fullscreen mode Exit fullscreen mode

Key annotations explained:

  • parent child hierarchy: Declares the traversal type. SAP CDS supports both parent-child and leveled hierarchy types.

  • child to parent association: References the association defined on the source view that links a node to its parent.

  • start where ParentId is initial: Defines the root condition. Nodes with no parent are traversal roots.

  • $node.hierarchy_level: A virtual node attribute computed by the engine — no need to maintain this manually.

  • $node.is_leaf: Automatically set to true for nodes with no children. This is gold for Fiori tree table rendering.

Important: You need to define the _ParentCategory association on the source view. Let me show you how to add that properly.


@AbapCatalog.sqlViewName: 'ZPROD_CAT_BASE'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Product Category Base View with Association'
define view ZCDS_PRODUCT_CATEGORY_BASE
  as select from zprod_category as pc
  association [0..1] to ZCDS_PRODUCT_CATEGORY_BASE as _ParentCategory
    on $projection.ParentId = _ParentCategory.CategoryId
{
  key pc.category_id    as CategoryId,
      pc.category_name  as CategoryName,
      pc.parent_id      as ParentId,
      pc.level          as HierarchyLevel,
      pc.sort_order     as SortOrder,
      pc.is_active      as IsActive,
      _ParentCategory   -- Association to self
}
where pc.is_active = 'X'

Enter fullscreen mode Exit fullscreen mode

Notice the self-referencing association — the view joins back to itself. This is the cornerstone of any parent-child hierarchy in CDS and what enables the recursive traversal at the database level.

Consuming a Hierarchy View in ABAP Open SQL

Here’s where many developers get confused. You don’t SELECT FROM a hierarchy view like a regular CDS view. Instead, you use the HIERARCHY function as a data source in your SQL statement.


CLASS zcl_product_category_reader DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES:
      BEGIN OF ty_category_node,
        category_id   TYPE zprod_category-category_id,
        category_name TYPE zprod_category-category_name,
        parent_id     TYPE zprod_category-parent_id,
        node_level    TYPE i,
        is_leaf       TYPE abap_bool,
        is_root       TYPE abap_bool,
      END OF ty_category_node,
      tt_category_nodes TYPE STANDARD TABLE OF ty_category_node
                         WITH EMPTY KEY.

    METHODS:
      get_full_hierarchy
        RETURNING VALUE(rt_nodes) TYPE tt_category_nodes,

      get_subtree
        IMPORTING iv_root_id        TYPE zprod_category-category_id
        RETURNING VALUE(rt_nodes)   TYPE tt_category_nodes.

ENDCLASS.

CLASS zcl_product_category_reader IMPLEMENTATION.

  METHOD get_full_hierarchy.
    """
    Traverse the entire product category hierarchy.
    The HIERARCHY function returns all nodes with
    computed depth and leaf/root indicators.
    """
    SELECT
      CategoryId,
      CategoryName,
      ParentId,
      NodeLevel,
      IsLeaf,
      IsRoot
    FROM HIERARCHY(
      SOURCE ZCDS_PRODUCT_CATEGORY_BASE
      CHILD TO PARENT ASSOCIATION _ParentCategory
      START WHERE ParentId IS NULL
      SIBLINGS ORDER BY SortOrder ASCENDING
    )
    ORDER BY NodeLevel ASCENDING, SortOrder ASCENDING
    INTO TABLE @rt_nodes.

    IF sy-subrc <> 0.
      &quot; Handle empty result - this is a valid state
      &quot; Log it if needed, but don&#039;t raise an exception
      RETURN.
    ENDIF.
  ENDMETHOD.

  METHOD get_subtree.
    &quot;&quot;&quot;
    Retrieve only the subtree below a specific root node.
    Useful for lazy loading in Fiori tree tables.
    &quot;&quot;&quot;
    SELECT
      CategoryId,
      CategoryName,
      ParentId,
      NodeLevel,
      IsLeaf,
      IsRoot
    FROM HIERARCHY(
      SOURCE ZCDS_PRODUCT_CATEGORY_BASE
      CHILD TO PARENT ASSOCIATION _ParentCategory
      START WHERE CategoryId = @iv_root_id
      SIBLINGS ORDER BY SortOrder ASCENDING
    )
    ORDER BY NodeLevel ASCENDING
    INTO TABLE @rt_nodes.
  ENDMETHOD.

ENDCLASS.

Enter fullscreen mode Exit fullscreen mode

This is clean, readable, and — critically — pushes the recursion down to HANA. No ABAP-side looping, no recursive method calls, no buffer-busting SELECT loops. The entire hierarchy traversal happens at the database layer where it belongs.

Hierarchy Annotations for Fiori and OData Exposure

If your goal is to expose a hierarchy through OData for a Fiori tree table or analytical drill-down, you need to add specific annotations to your consumption view. This bridges directly into what we covered in Part 7 on consumption views and OData exposure.


@OData.publish: true
@Analytics.query: true
@Hierarchy.parentChild: [
  {
    name: &#039;ProductCategoryHierarchy&#039;,
    source: &#039;ZCDS_PROD_CAT_HIERARCHY&#039;,
    parentNodeField: &#039;ParentId&#039;,
    childNodeField: &#039;CategoryId&#039;,
    levelField: &#039;NodeLevel&#039;,
    isLeafField: &#039;IsLeaf&#039;,
    drillDownState: #EXPANDED
  }
]
@EndUserText.label: &#039;Product Category Hierarchy - Consumption&#039;
define view ZCDS_PRODCAT_HIER_CONSUMPTION
  as select from ZCDS_PROD_CAT_HIERARCHY
{
  CategoryId,
  CategoryName,
  ParentId,
  NodeLevel,
  IsLeaf,
  IsRoot,
  SortOrder
}

Enter fullscreen mode Exit fullscreen mode

The @Hierarchy.parentChild annotation is what tells the OData framework to render this as a hierarchical dataset rather than a flat list. Combined with @Analytics.query: true, this enables tree tables in SAP Analytics Cloud and Fiori Elements with almost zero additional UI configuration.

Handling Real-World Complexity: Cycles, Orphans, and Missing Parents

In theory, your hierarchy is clean and consistent. In practice — especially when migrating legacy data — you’ll encounter cycles (A is parent of B, B is parent of A), orphan nodes (ParentId points to a non-existent node), and inconsistent depth levels.

Cycle Detection and Handling

CDS hierarchy views support cycle detection out of the box. Use the WITH CYCLE clause to handle cycles gracefully instead of letting the query run into infinite recursion:


define hierarchy ZCDS_PROD_CAT_HIERARCHY
  as parent child hierarchy(
    source        ZCDS_PRODUCT_CATEGORY_BASE
    child to parent association _ParentCategory
    start where   ParentId is initial
    siblings order by SortOrder ascending
    with cycle node $node.is_cycle as IsCycle  -- Flag cyclic nodes
  )
{
  CategoryId,
  CategoryName,
  ParentId,
  $node.node_id         as NodeId,
  $node.hierarchy_level as NodeLevel,
  $node.is_root         as IsRoot,
  $node.is_leaf         as IsLeaf,
  IsCycle               -- Include cycle indicator in output
}

Enter fullscreen mode Exit fullscreen mode

With WITH CYCLE, the engine will traverse until it detects a revisited node, flag it, and stop — rather than crashing. You can then filter cyclic nodes in your consumer layer and trigger a data quality alert.

Orphan Node Strategy

My recommendation here is pragmatic: handle orphans at the data layer with a dedicated data quality CDS view that runs as a scheduled check, not by making your hierarchy view overly defensive. Keep the hierarchy clean by design; validate on inbound data entry. This aligns with the clean code principles we covered in our clean code refactoring guide.

Performance Considerations for Hierarchy Queries

A few hard-won lessons from production systems:

  • Never traverse full hierarchies on every request. Cache the flattened structure in a buffer if it changes infrequently. Cost center hierarchies rarely change daily — don’t pretend they do.

  • Use subtree queries for UI-driven scenarios. The get_subtree pattern above is your friend for lazy-loaded tree tables. Only load what the user expands.

  • Index your parent key columns. Sounds obvious, but I’ve seen production systems with multi-million row hierarchy tables and no index on PARENT_ID. The HANA engine’s recursive traversal benefits enormously from proper indexing.

  • Limit hierarchy depth in abnormal cases. Use the WHERE $node.hierarchy_level <= 10 guard clause on systems where data quality is uncertain. Runaway recursion on corrupted data is a real outage risk.

For a deeper dive into query tuning strategies, revisit Part 8 of this series on performance optimization and buffering — the techniques there apply directly to hierarchy consumption views as well.

A Note on Leveled vs. Parent-Child Hierarchies

Everything above covers parent-child hierarchies, which are the most flexible and common type. SAP CDS also supports leveled hierarchies — structures where the hierarchy is fixed and defined by a set of levels rather than a recursive parent reference. Think: Region → Country → State → City.

Leveled hierarchies are simpler to model and perform slightly better because there’s no recursion — each level is a separate join. However, they’re rigid. If your business needs to add an intermediate level (e.g., Sub-Region between Region and Country), you have a schema change on your hands.

My rule of thumb: Use leveled hierarchies for stable, well-defined dimensions (geography, time, fixed org structures). Use parent-child for dynamic, user-maintained hierarchies where depth is variable (product categories, custom org structures, BOM-like relationships).

Key Takeaways

  • CDS hierarchy views push recursive traversal to the HANA layer — stop doing recursion in ABAP application code.

  • A self-referencing association on the source view is the foundation of every parent-child CDS hierarchy.

  • The $node virtual attributes (hierarchy_level, is_leaf, is_root) are automatically computed — use them freely.

  • The WITH CYCLE clause is non-negotiable for any hierarchy that accepts user-maintained data.

  • For Fiori exposure, the @Hierarchy.parentChild annotation is the bridge between your CDS hierarchy and the UI tree table component.

  • Cache aggressively for hierarchies that change infrequently — full traversals on every page load are a red flag.

What’s Next in the Series?

In Part 10, we’ll tackle one of the most requested topics from readers: CDS-based analytical queries, cube views, and dimension views — the backbone of embedded analytics in S/4HANA. If you’ve wondered how to replace custom BW extractors with CDS-native analytical models, that’s exactly where we’re headed.

If you found this article useful, drop a comment below — especially if you’ve hit any specific hierarchy modeling challenges I haven’t covered here. I read every comment, and real-world questions often shape the direction of future articles. And if you’re sharing this with your team, the next time someone asks “how do I model a tree structure in CDS,” you’ll know exactly where to send them.