Skip to main content
TeeBI provides powerful filtering capabilities through WHERE clauses, expressions, and filter objects.

WHERE Clause

Basic filtering syntax:
Result := TBISQL.From(Data, 'ProductName, Price where Price > 50');

Comparison Operators

// Equal
Result := TBISQL.From(Data, 'Name where Customer = 123');

// Not equal
Result := TBISQL.From(Data, 'Name where Status <> "Inactive"');

// Greater than / Less than
Result := TBISQL.From(Data, 'Name where Price > 100');
Result := TBISQL.From(Data, 'Name where Quantity < 10');

// Greater or equal / Less or equal
Result := TBISQL.From(Data, 'Name where Price >= 100');
Result := TBISQL.From(Data, 'Name where Quantity <= 10');

Logical Operators

AND

Result := TBISQL.From(Data, 
  'ProductName where (Price > 50) and (Category = "Electronics")');
  
Result := TBISQL.From(Data,
  'sum(Amount) where (Customer=123) and (Product<456) group by Country');

OR

Result := TBISQL.From(Data, 
  'ProductName where (Category = "Electronics") or (Category = "Books")');

NOT

Result := TBISQL.From(Data, 
  'ProductName where not (Price > 100)');
See TLogicalExpression at BI.Expression.pas:238.

IN Operator

Test if value is in a list:
// Static list
Result := TBISQL.From(Data, 
  'ProductName where Category in ["Electronics", "Books", "Toys"]');

// Sub-query
Result := TBISQL.From(Data, 
  'ProductName where CategoryID in select ID from Categories where Active=true');
See LeftInRight at BI.Expression.pas:769.

Text Operators

Starts With

Result := TBISQL.From(Data, 
  'ProductName where ProductName starts "Pro"');

Ends With

Result := TBISQL.From(Data, 
  'ProductName where ProductName ends ".com"');

Contains

Result := TBISQL.From(Data, 
  'ProductName where Description contains "premium"');
See TTextLogicalExpression at BI.Expression.pas:532.

Sub-Queries in Filters

Use sub-queries for dynamic filtering:
// Compare to aggregate
Result := TBISQL.From(Data, 
  'ProductName, UnitPrice where UnitPrice > select Average(UnitPrice)');

// Using IN with sub-query
Result := TBISQL.From(Data,
  'OrderID where CustomerID in select ID from Customers where Country="USA"');
Sub-queries are evaluated first and their results used in the outer query filter.

TBIQuery Component

Component-based filtering with the TBIQuery class. See BI.Query.pas:250.

Filter Property

var
  Query: TBIQuery;
begin
  Query := TBIQuery.Create(nil);
  try
    Query.Data := MyData;
    
    // Set filter as text
    Query.Filter.Text := 'Price > 100';
    
    // Or use custom expression
    Query.Filter.Custom := TLogicalExpression.Create(
      TDataItemExpression.Create(MyData['Price']),
      TLogicalOperand.Greater,
      TIntegerExpression.Create(100)
    );
    
    Result := Query.Calculate;
  finally
    Query.Free;
  end;
end;

TDataSelect Filtering

Direct filtering with TDataSelect. See BI.DataSource.pas:379.
var
  Select: TDataSelect;
begin
  Select := TDataSelect.Create(nil);
  try
    Select.Data := MyData;
    Select.Add(MyData['ProductName']);
    Select.Add(MyData['Price']);
    
    // Set filter expression
    Select.Filter := TLogicalExpression.Create(
      TDataItemExpression.Create(MyData['Price']),
      TLogicalOperand.Greater,
      TIntegerExpression.Create(100)
    );
    
    Result := Select.Calculate;
  finally
    Select.Free;
  end;
end;

Filter Expressions

Build complex filters programmatically:
var
  Filter1, Filter2, Combined: TLogicalExpression;
begin
  // Price > 100
  Filter1 := TLogicalExpression.Create(
    TDataItemExpression.Create(MyData['Price']),
    TLogicalOperand.Greater,
    TIntegerExpression.Create(100)
  );
  
  // Category = "Electronics"
  Filter2 := TLogicalExpression.Create(
    TDataItemExpression.Create(MyData['Category']),
    TLogicalOperand.Equal,
    TTextExpression.Create('Electronics')
  );
  
  // Combine with AND
  Combined := TLogicalExpression.Join(Filter1, Filter2, TLogicalOperand.&And);
  
  Select.Filter := Combined;
end;
See TLogicalExpression.Join at BI.Expression.pas:737.

Distinct Values

Eliminate duplicate rows:
// Using SQL
Result := TBISQL.From(Data, 'distinct Country, City');

// Using TDataSelect
Select.Distinct := True;
See TDataSelect.Distinct at BI.DataSource.pas:381.

Pagination

Using TOP and OFFSET

// First 100 rows
Result := TBISQL.From(Data, 'top 100 ProductName, Price');

// Skip 50, take 100
Result := TBISQL.From(Data, 'top 100 offset 50 ProductName, Price');

Using TDataSelect Properties

Select.Max := 100;    // Maximum rows to return
Select.Start := 50;   // Rows to skip
See TDataSelect.Max at BI.DataSource.pas:160 and Start at line 159.

Master-Detail Filtering

Automatic filtering based on master-detail relationships:
var
  MasterData, DetailData: TDataItem;
  DetailIndex: TCursorIndex;
begin
  // Get filtered detail records for master row 5
  DetailIndex := TDataCursor.MasterDetailIndex(
    DetailData,
    TDataArray.Create([MasterData]),
    5
  );
end;
See TDataCursor.MasterDetailIndex at BI.DataSource.pas:142.

Filter Performance

UseFilter Property

Control whether filters are applied:
Select.UseFilter := False; // Temporarily disable filtering
See TDataCursor.UseFilter at BI.DataSource.pas:161.

Filter Index Caching

Filters create an index array for efficient access:
var
  Cursor: TDataCursor;
begin
  Cursor.Filter := MyFilterExpression;
  Cursor.PrepareIndex; // Build index
  
  // Access filtered rows efficiently
  for I := 0 to Cursor.Count - 1 do
    Position := Cursor.Index[I];
end;
See TDataCursor.PrepareIndex at BI.DataSource.pas:149.

HAVING Clause

Filter after aggregation (used with GROUP BY):
var
  Summary: TSummary;
begin
  Summary := TSummary.Create(nil);
  try
    Summary.Measures.Add(MyData['Sales'], TAggregate.Sum);
    Summary.By.Add(MyData['Country']);
    
    // Filter aggregated results
    Summary.Having.Add('Sum(Sales) > 10000');
    
    Result := Summary.Calculate;
  finally
    Summary.Free;
  end;
end;
See TSummary.Having at BI.Summary.pas:467.

Error Handling

Handle filter parsing errors:
function FilterErrorHandler(
  const APos: Integer;
  const AMessage: String
): Boolean;
begin
  ShowMessage(Format('Filter error at position %d: %s', [APos, AMessage]));
  Result := True; // Don't raise exception
end;

Result := TBISQL.From(Data, 
  'ProductName where invalid syntax', 
  nil, 
  FilterErrorHandler
);

Build docs developers (and LLMs) love