Create CDS views vs Calculation Views, use HANA table functions, and run native SQL from CAP.
Calculation Views are HANA's native analytical engine — column-store optimised for aggregations over millions of rows. For reporting use cases (sales totals, inventory analytics, financial aggregations) they outperform standard SQL views by orders of magnitude.
| Type | Use case | Tooling |
|---|---|---|
| Graphical | Visual join/aggregation builder | HANA Database Explorer |
| SQL Script | Complex logic, code reuse | Any text editor |
| Hierarchy | Tree traversal | Graphical tool |
-- db/hana/CV_SALES_SUMMARY.hdbcalculationview
-- (stored in gen/db/src/ when hand-crafted, or in a separate HANA project)
<!-- CV_SALES_SUMMARY.hdbcalculationview (graphical format, SAP-generated) -->
<?xml version="1.0" encoding="UTF-8"?>
<Calculation:scenario
xmlns:Calculation="http://www.sap.com/ndb/BiModelCalculation.ecore"
id="CV_SALES_SUMMARY"
applyPrivilegeType="NONE"
dataCategory="CUBE"
outputViewType="Aggregation">
<!-- columns, join nodes, aggregation nodes defined here -->
</Calculation:scenario>
For CAP developers, the simpler approach is a SQL-script Calculation View:
-- CREATE CALCULATION SCENARIO via HDI procedure
-- Exposed as a SQL view-like object CAP can query
CAP can call HANA SQL scalar and table functions which give similar performance benefits:
-- gen/db/src/SalesReport.hdbfunction (deployed via HDI)
FUNCTION SalesReport(p_year INTEGER)
RETURNS TABLE (
product_ID NVARCHAR(36),
product_name NVARCHAR(200),
total_qty INTEGER,
total_revenue DECIMAL(15,2)
)
LANGUAGE SQLSCRIPT AS
BEGIN
RETURN SELECT
p.ID AS product_ID,
p.name AS product_name,
SUM(oi.quantity) AS total_qty,
SUM(oi.quantity * oi.price) AS total_revenue
FROM ORDERS o
JOIN ORDER_ITEMS oi ON oi.ORDER_ID = o.ID
JOIN PRODUCTS p ON p.ID = oi.PRODUCT_ID
WHERE YEAR(o.CREATED_AT) = :p_year
GROUP BY p.ID, p.name
ORDER BY total_revenue DESC;
END;
// srv/analytics-service.cds
service AnalyticsService {
// Map to HANA table function
@readonly
entity SalesReport(year : Integer) as
select from SalesReport(year); // CDS doesn't support this directly
// Use a CDS view instead — CAP generates a standard SQL view
@readonly
entity ProductRevenue as select from my.OrderItems {
product.name as productName,
sum(quantity * price) as revenue
}
group by product.name;
}
For true Calculation Views, bypass the CDS layer:
// srv/analytics-service.js
const db = await cds.connect.to('db')
this.on('READ', 'SalesReport', async (req) => {
const { year = new Date().getFullYear() } = req.data
// Call the HANA table function directly
const result = await db.run(
`SELECT * FROM "SalesReport"(${year}) ORDER BY total_revenue DESC`
)
return result
})
After the CV is deployed to HDI:
this.on('READ', 'SalesSummary', async (req) => {
const result = await db.run(`
SELECT product_ID, product_name, total_qty, total_revenue
FROM "_SYS_BIC"."my.bookshop/CV_SALES_SUMMARY"
WHERE YEAR = ${req.data.year}
`)
return result
})
| Query type | 1M rows | 10M rows |
|---|---|---|
| Standard SQL JOIN aggregation | ~2s | ~20s |
| HANA column store SQL view | ~200ms | ~2s |
| Calculation View (columnar engine) | ~50ms | ~500ms |
| Use CDS view | Use HANA Calculation View |
|---|---|
| < 500K rows, transactional reporting | > 1M rows, analytical dashboards |
| Simple aggregations | Complex multi-level hierarchies |
| Dev/prod parity important | HANA-only deployment |
| Portable code priority | Maximum performance priority |
ProductRevenue that aggregates order items by product@Aggregation.default: #SUM for Fiori Analytical tablesGET /analytics/ProductRevenue?$apply=aggregate(revenue with sum)db.run(native SQL) from a handlerdb.run('SELECT * FROM "FuncName"(params)') _SYS_BIC schema in native SQL