Query by Example (QBE) is a database query language for relational databases. The motivation behind QBE is that a parser can convert the user's actions into statements expressed in a database manipulation language, such as SQL. Behind the scenes, it is this statement that is actually executed. A suitably comprehensive front-end can minimize the burden on the user to remember the finer details of SQL, and it is easier and more productive for end-users (and even programmers) to select tables and columns by selecting them rather than typing in their names.

QBE is use by Axional Studio forms and DB studio table query. And it's available from Javascript to allow programmatic QBE interfaces.

1 The QBE class

To generate a QBE query, first create a QBE class on a database instance and add full qualified table columns with a QBE expression.

Return Options Description
JQBE Ax.sql.QBE(DatabaseConnection) The constructor receives a database connection to know SQL Syntax to generate (e.g. new Ax.sql.QBE(Ax.db).
Void addColumn(String colname, String query) Defines a column name with format "tabname.colname" and the query expression. The column name will determine field type and will create where syntax properly.
Void addColumn(String colname, int sqltype, String query) colname represents the text used for colname in where condition constructed, sqltype is the SQL Type used for this column in SQL generation and query, the query by example string to process.
String toString() Returns the query/where string with the columns and query expressions added to the QBE Object.
Copy
<script>
    var qbe = new Ax.sql.QBE(Ax.db);
    qbe.addColumn("systables.tabid", "<10");
    qbe.addColumn("systables.tabname", "sys*");
    console.log(qbe);
</script>
systables.tabid < 10 AND systables.tabname MATCHES 'sys*'

2 QBE expressions

Type Expression SQL
NULL = c1 IS NULL
!= c1 IS NOT NULL
CHAR =hello c1 = 'hello'
!=hello c1 != 'hello'
a|b|c c1 IN ('a','b','c')
!a|b|c c1 NOT IN ('a','b','c')
hello* c1 MATCHES 'hello*'
!hello* c1 NOT MATCHES 'hello*'
=||a*||hello c1 IS NULL OR c1 MATCHES 'a*' OR c1 = 'hello')
=||a*||hello* c1 IS NULL OR c1 MATCHES 'a*' OR c1 MATCHES 'hello*')
=|| ||hello (c1 IS NULL OR c1 = 'hello')
NUMERIC =10 c2 = 10
>10 c2 >10
10:20 c2 BETWEEN 10 AND 20
10|20 c2 IN (10,20)
DATE >12-31-2018 c3 > MDY(12,31,2018)
12-31-2018|05-01-2019 c3 IN (MDY(12,31,2018),MDY(5,1,2019))
31-12-2018:05-01-2019 c3 BETWEEN MDY(12,31,2018) AND MDY(5,1,2019)
TIMESTAMP >31-12-2018 15:30 c3 > TO_DATE("31-12-2018 50:30", "%d-%m-%Y %H:%M")
>2018-12-31 15:30:00 c3 > TO_DATE("2018-12-31 15:30:00", "%Y-%m-%d %H:%M:%S")
Copy
<script>
    var tests = [
            {colname: "systables.tabid", query:"10",        result:"systables.tabid = 10"},
            {colname: "systables.tabid", query:"50||60",    result:"(systables.tabid = 50 OR systables.tabid = 60)"},
            {colname: "systables.tabid", query:"50|60",     result:"systables.tabid IN (50,60)"},
            {colname: "systables.tabid", query:"!50|60",    result:"systables.tabid NOT IN (50,60)"},
            {colname: "systables.tabid", query:"10:20",     result:"systables.tabid BETWEEN 10 AND 20"},
            {colname: "systables.tabid", query:"!10:20",    result:"systables.tabid NOT BETWEEN 10 AND 20"},
            
            {colname: "systables.tabname", query:"!=hello",       result:"systables.tabname != 'hello'"},
            {colname: "systables.tabname", query:"a|b|c",         result:"systables.tabname IN ('a','b','c')"},
            {colname: "systables.tabname", query:"!a|b|c",        result:"systables.tabname NOT IN ('a','b','c')"},
            {colname: "systables.tabname", query:"=||a*||hello*", result:"(systables.tabname IS NULL OR systables.tabname MATCHES 'a*' OR systables.tabname MATCHES 'hello*')"},
            {colname: "systables.tabname", query:"=||||hello",    result:"(systables.tabname IS NULL OR systables.tabname = 'hello')"},
            {colname: "systables.tabname", query:"10",            result:"systables.tabname = '10'"},
            
            {colname: "systables.created", query:"12-31-2018",             result:"systables.created = MDY(12,31,2018)"},
            {colname: "systables.created", query:">1-11-2019",            result:"systables.created > MDY(1,11,2019)"},
            {colname: "systables.created", query:"01-01-2018:12-31-2018",  result:"systables.created BETWEEN MDY(1,1,2018) AND MDY(12,31,2018)"},
            {colname: "systables.created", query:"!01-01-2018:12-31-2018", result:"systables.created NOT BETWEEN MDY(1,1,2018) AND MDY(12,31,2018)"},
            
            {colname: "systables.ustlowts", query:"!=12-31-2018 00:15:00",         result:"systables.ustlowts != TO_DATE('31-12-2018 00:15', '%d-%m-%Y %H:%M')"},
            {colname: "systables.ustlowts", query:">12-31-2018 00:15:30",       result:"systables.ustlowts > TO_DATE('31-12-2018 00:15:30', '%d-%m-%Y %H:%M:%S')"},
        ];
        
    for (var t of tests) {
        var qbe = new Ax.sql.QBE(Ax.db);
        qbe.addColumn(t.colname, t.query);
        console.log("----------------------------------");
        console.log(t.colname + " Query: " + t.query);
        console.log("   Result..: " + qbe.toString());
        console.log("   Expected: " + t.result);
        Ax.jsunit.assertEquals(qbe.toString(),t.result);
    }
</script>