INDEXES
Database fundamentals
1 Public
WHO AM I
| Ralph Oostheim
| Consultant @ Transfer Solutions
| 1998 – 2018 Oracle Developer
| 2019 - .... OutSystems Developer
2 Public
DISCLAIMER
| Simplified
| Differences between Oracle and SQL Server
| Examples are a way of thinking to get to index definitions
| If you only have access to this presentation: sheets support my presentation but be
careful when interpretating these without any explanation.
3 Public
DATABASE & INDEXES
A DATABASE SHOULD NOT BE
TREATED AS A ROLODEX
ALTHOUGH A ROLODEX IS
SOMETIMES MORE INDEXED
THAN A TABLE IN AN
OUTSYSTEMS APP 
Public
4
WHAT ARE INDEXES?
| An index is a pointer to data in a table
5 Public
Id Name FirstName Attribute
3…20
1 Berg Lyla
2 Stein June
3 Schmidt Will
4 Hodges Rudy
5 Morris Larry
6 Patel Ashley
7 Barber Rosaly
… ..
100 Moris Rosaly
…
Block
(4K)
Name
Barber
Berg
Hodges
Moris
Moris
Patel
Schmidt
Stein
..
Where name = ‘Moris’
Index CustomerIX1
All blocks = “Full table scan”
3 blocks instead of all
WHAT ARE INDEXES
| B-Tree Indexes
6 Public
7 Public
Name
Barber
Berg
Hodges
Moris
Moris
Patel
Schmidt
Stein
..
CustomerIX1
FirstName
Ashley
June
Larry
Lyla
Rosaly
Rosaly
Rudy
Will
..
CustomerIX2
Name FirstName
Barber Rosaly
Berg Lyla
Hodges Rudy
Moris Larry
Moris Rosaly
Patel Ashley
Schmidt Will
Stein June
..
CustomerIX3
where name = ‘Moris’
and FirstName = ‘Rosaly’
where name = ‘Moris’
or FirstName = ‘Rosaly’
Answer: IX3
Which index(es) will be used ?
Answer: depends on
statistics (in principle only
one index can be used per
table instance in a query)
GOAL OF INDEXES
| Minimize disk reads (blocks)
| Avoid full table scan
• When limited number of records are needed
• Rule of thumb < 10%
| Performance
8 Public
DEFAULT OUTSYSTEM INDEXES
| …there are indexes, why ?
| Will be used in aggregates, but created to serve a different purpose
9 Public
This will avoid a full table scan (and possibly table locks) to check
referential integrity when rows are deleted
(MULTI) COLUMN INDEXES
| Are not created by Outsystems: Developer need to think about them
| In general:
- Foreign Key columns first
- Most distinctive column first means less blocks
| Candidates
- Entities with large number of rows
- Logging entities
- Smaller entities that are accessed a lot
10 Public
Public
11
select Customer.Name
, CustomerCategory.Label
from Orderline
join Order on OrderLine.OrderId = Order.Id
join Customer on Order.CustomerId = Customer.Id
join CustomerCategory cc on Customer.CustomerCategory = cc.Id
join Product on OrderLine.ProductId = Product.Id
where Product.ProductCategoryId = @ProductCategory
and Order.Date > @DateOrdered
group by Customer.Name, CustomerCategory.Label
HOW ?
| Use an environment with realistic data: statistics are important.
- A database generally create statics once a day to determine the distribution of data and table size
| Use DB-tools provided like SQL Server Management Studio
- Show predicted or actual plan
- Suggestions: Use as a guideline, not a truth (look at your aggregate !)
- OutSystems does not support all suggested options
| If you don’t have access to the DB: aim at limiting disk reads
| Keep in mind: Indexes create overhead at Create/Update/Delete
- But in general reads exceed number of writes
12 Public
DO (1/3)
| Rule of thumb: each table should have at least on defined index: the Unique index.
- Defines the functional identification of a record. Often used by user to find data.
| Keep it positive: indexes are optimized to find present values !
- Order.IsDeleted = False instead of not Order.IsDeleted
- Order.OrderStatusId = @StatusIdNew or Order.OrderStatusId = @StatusIdStarted instead of
Order.OrderStatusId <> @StatusIdFinal
- OrderStatus.IsInProgress instead of Order.OrderStatusId <> @StatusIdFinal
| Limit large tables on at least 1 indexed column
- Order.StatusId = @StatusIdFinal and Customer.Address like “%”+@City’+”%”
| Think about your screen design (add a filter above your table with a default to initially limit data)
- ChangeLog.DateCreated > @MinDate
13 Public
DO (2/3)
| Consider adding more attributes at the end of an index to avoid reading actual table
blocks (but be careful !)
| Consider adding an index on Foreign Key columns with delete rule ‘Ignore’
- no AutoIndex ! Can be useful in joins.
14 Public
DO (3/3)
| ServiceCenter / Analytics / report Queries Performance
- Fix SlowSQL entity actions (missing index ? Lock issue ?)
- Fix SlowSQL with high number of executions and high Total Time
| ServiceCenter / Monitoring / Screen requests
- Individual aggregates can be fast (<200ms), data action as a whole slow !
| ServiceCenter / Monitoring / General, filter on SLOWSQL
- List slow individual aggregates
| Export Screen Requests/General to excel
- more columns available. For instance userId/Name: you can ask them what they did or experienced.
- Create pivot tables in Excel to analyze trents in time (column “instant”)
15 Public
AVOID
| Data transformations on indexed attributes
- DateToText(Order.OrderDateTime,”yyyy-mm-dd”) = @OrderDateText
- Like “%”+@Value
| Soft Delete: use only when needed
| Indexing uneven distributed attributes
- IsActive=True: 99%, IsInactive=false: 1% where customer.IsActive=True
| Tuning for 1 specific query: look at the broader picture
16 Public
OUTSYSTEMS AND WHERE-CLAUSES (1/4)
| Benefit from OutSystems query optimization
17 Public
OUTSYSTEMS AND WHERE-CLAUSES (2/4)
| Don’t mix unrelated where clauses:
18 Public
OUTSYSTEMS AND WHERE-CLAUSES (3/4)
| Keep it positive: avoid “not”
| Instead “= False”
- parameter value ‘False’ : the entire where is left out
- parameter value is ‘True’: OR removed from where clause
| In general: Outsystems can omit where clauses that already evaluate to true with a given
parameter value (and apparently also parts of where clauses).
- This can explain why the same query is sometimes fast, sometimes slow: the database receives a different query, so can calculate a
different execution plan. Also the data input itself can change the execution plan dependent of the distribution of the given parameter
value
19 Public
OUTSYSTEMS AND WHERE-CLAUSES (4/4)
| Don’t use ‘If’: where clause ís already an if !
| Instead
| You can find the queries (after assigning parameter values on the test tab) here:
20 Public
QUESTIONS?
21 Public

OutSystsems User Group Brabant November 2025

  • 1.
  • 2.
    WHO AM I |Ralph Oostheim | Consultant @ Transfer Solutions | 1998 – 2018 Oracle Developer | 2019 - .... OutSystems Developer 2 Public
  • 3.
    DISCLAIMER | Simplified | Differencesbetween Oracle and SQL Server | Examples are a way of thinking to get to index definitions | If you only have access to this presentation: sheets support my presentation but be careful when interpretating these without any explanation. 3 Public
  • 4.
    DATABASE & INDEXES ADATABASE SHOULD NOT BE TREATED AS A ROLODEX ALTHOUGH A ROLODEX IS SOMETIMES MORE INDEXED THAN A TABLE IN AN OUTSYSTEMS APP  Public 4
  • 5.
    WHAT ARE INDEXES? |An index is a pointer to data in a table 5 Public Id Name FirstName Attribute 3…20 1 Berg Lyla 2 Stein June 3 Schmidt Will 4 Hodges Rudy 5 Morris Larry 6 Patel Ashley 7 Barber Rosaly … .. 100 Moris Rosaly … Block (4K) Name Barber Berg Hodges Moris Moris Patel Schmidt Stein .. Where name = ‘Moris’ Index CustomerIX1 All blocks = “Full table scan” 3 blocks instead of all
  • 6.
    WHAT ARE INDEXES |B-Tree Indexes 6 Public
  • 7.
    7 Public Name Barber Berg Hodges Moris Moris Patel Schmidt Stein .. CustomerIX1 FirstName Ashley June Larry Lyla Rosaly Rosaly Rudy Will .. CustomerIX2 Name FirstName BarberRosaly Berg Lyla Hodges Rudy Moris Larry Moris Rosaly Patel Ashley Schmidt Will Stein June .. CustomerIX3 where name = ‘Moris’ and FirstName = ‘Rosaly’ where name = ‘Moris’ or FirstName = ‘Rosaly’ Answer: IX3 Which index(es) will be used ? Answer: depends on statistics (in principle only one index can be used per table instance in a query)
  • 8.
    GOAL OF INDEXES |Minimize disk reads (blocks) | Avoid full table scan • When limited number of records are needed • Rule of thumb < 10% | Performance 8 Public
  • 9.
    DEFAULT OUTSYSTEM INDEXES |…there are indexes, why ? | Will be used in aggregates, but created to serve a different purpose 9 Public This will avoid a full table scan (and possibly table locks) to check referential integrity when rows are deleted
  • 10.
    (MULTI) COLUMN INDEXES |Are not created by Outsystems: Developer need to think about them | In general: - Foreign Key columns first - Most distinctive column first means less blocks | Candidates - Entities with large number of rows - Logging entities - Smaller entities that are accessed a lot 10 Public
  • 11.
    Public 11 select Customer.Name , CustomerCategory.Label fromOrderline join Order on OrderLine.OrderId = Order.Id join Customer on Order.CustomerId = Customer.Id join CustomerCategory cc on Customer.CustomerCategory = cc.Id join Product on OrderLine.ProductId = Product.Id where Product.ProductCategoryId = @ProductCategory and Order.Date > @DateOrdered group by Customer.Name, CustomerCategory.Label
  • 12.
    HOW ? | Usean environment with realistic data: statistics are important. - A database generally create statics once a day to determine the distribution of data and table size | Use DB-tools provided like SQL Server Management Studio - Show predicted or actual plan - Suggestions: Use as a guideline, not a truth (look at your aggregate !) - OutSystems does not support all suggested options | If you don’t have access to the DB: aim at limiting disk reads | Keep in mind: Indexes create overhead at Create/Update/Delete - But in general reads exceed number of writes 12 Public
  • 13.
    DO (1/3) | Ruleof thumb: each table should have at least on defined index: the Unique index. - Defines the functional identification of a record. Often used by user to find data. | Keep it positive: indexes are optimized to find present values ! - Order.IsDeleted = False instead of not Order.IsDeleted - Order.OrderStatusId = @StatusIdNew or Order.OrderStatusId = @StatusIdStarted instead of Order.OrderStatusId <> @StatusIdFinal - OrderStatus.IsInProgress instead of Order.OrderStatusId <> @StatusIdFinal | Limit large tables on at least 1 indexed column - Order.StatusId = @StatusIdFinal and Customer.Address like “%”+@City’+”%” | Think about your screen design (add a filter above your table with a default to initially limit data) - ChangeLog.DateCreated > @MinDate 13 Public
  • 14.
    DO (2/3) | Consideradding more attributes at the end of an index to avoid reading actual table blocks (but be careful !) | Consider adding an index on Foreign Key columns with delete rule ‘Ignore’ - no AutoIndex ! Can be useful in joins. 14 Public
  • 15.
    DO (3/3) | ServiceCenter/ Analytics / report Queries Performance - Fix SlowSQL entity actions (missing index ? Lock issue ?) - Fix SlowSQL with high number of executions and high Total Time | ServiceCenter / Monitoring / Screen requests - Individual aggregates can be fast (<200ms), data action as a whole slow ! | ServiceCenter / Monitoring / General, filter on SLOWSQL - List slow individual aggregates | Export Screen Requests/General to excel - more columns available. For instance userId/Name: you can ask them what they did or experienced. - Create pivot tables in Excel to analyze trents in time (column “instant”) 15 Public
  • 16.
    AVOID | Data transformationson indexed attributes - DateToText(Order.OrderDateTime,”yyyy-mm-dd”) = @OrderDateText - Like “%”+@Value | Soft Delete: use only when needed | Indexing uneven distributed attributes - IsActive=True: 99%, IsInactive=false: 1% where customer.IsActive=True | Tuning for 1 specific query: look at the broader picture 16 Public
  • 17.
    OUTSYSTEMS AND WHERE-CLAUSES(1/4) | Benefit from OutSystems query optimization 17 Public
  • 18.
    OUTSYSTEMS AND WHERE-CLAUSES(2/4) | Don’t mix unrelated where clauses: 18 Public
  • 19.
    OUTSYSTEMS AND WHERE-CLAUSES(3/4) | Keep it positive: avoid “not” | Instead “= False” - parameter value ‘False’ : the entire where is left out - parameter value is ‘True’: OR removed from where clause | In general: Outsystems can omit where clauses that already evaluate to true with a given parameter value (and apparently also parts of where clauses). - This can explain why the same query is sometimes fast, sometimes slow: the database receives a different query, so can calculate a different execution plan. Also the data input itself can change the execution plan dependent of the distribution of the given parameter value 19 Public
  • 20.
    OUTSYSTEMS AND WHERE-CLAUSES(4/4) | Don’t use ‘If’: where clause ís already an if ! | Instead | You can find the queries (after assigning parameter values on the test tab) here: 20 Public
  • 21.