Queries

Top  Previous  Next

SQL (Structured Query Language) is a powerful language for extracting and processing data. Tibbo LinkServer natively supports SQL, making it easy to access its internal data, as well as data collected from hardware devices. The specific SQL dialect supported by LinkServer is called AggreGate Query Language.

AggreGate Query Language is highly integrated into LinkServer. Since all LinkServer internal data is maintained as Data Tables, it's easy to use SQL queries to modify the properties (settings, variables) of various system objects. Data Tables contain actual data, as well as data validation rules, detailed information each field, etc.

note_warning-wt

This section of the manual is not a tutorial of the SQL language itself. Rather, it assumes you already know SQL and SQL syntax. This isn't because we want to make your life hard -- it's just because SQL is an extensive topic, and great minds have already wrote excellent books and tutorials on the topic. So, if you're unfamiliar with SQL, please read a bit about the subject generally. Once you understand SQL as a wider subject, come back to this chapter and learn how we've implemented it in AggreGate. We've listed some places where you can learn more about SQL in the SQL References section.

Purpose Of The Query Language

AggreGate Query Language has two primary uses:

1.Obtaining data from server objects or hardware devices, filtering, sorting and grouping it and presenting it to the user in a convenient form (as a table).
2.Modifying the properties of various contexts (so as to configure various hardware devices, for example) by entering values for settings in the table.

The first task is performed using a normal data selection ("SELECT") query, creating a Data Table viewable through a LinkServer User Interface (such as AggreGate Client).

For example, you can write a query that shows the traffic for all Device Servers in the system in a single convenient sortable table:

ls_query_traffic_results

Queries can also present editable results, allowing users to enter data, for example to configure several instances of the same device. So you can use a query to access the settings of all Device Servers in the system and edit them in a single table:

ls_query_dss_settings

Queries can be also used in different LinkServer facilities. For example, you can set up an alert that will be raised if a query result satisfies some condition (see Hardware Devices in Trouble example in Alerts chapter for details). Query results may be converted into a printable report when they're browsed (e.g. using the Generate Report feature of Data Table Editor in AggreGate Client). Widgets may also be used to present data previously aggregated and filtered by a query.

Administering Queries

Two contexts are used to administer queries: One is the general Queries context, which serves as a container. The other is the Query context, which holds the information for a single query. See descriptions below.

ls_queries

note_further-wt

Every user has his own set of Queries.

Query Configuration

Here is a list of query properties:

Field Description

Field Name

Name. Name of the query context. It should satisfy the context naming conventions. Name is required to refer to this query from other parts of the system. It cannot be changed after creation of the query.

name

Description. Textual description of the query. This is also a description of the Query context.

description

Query Text. Text of the query. Edited in a text area editor.

query

Parameterized. Indicates that query is parameterized. If this flag is set, Query Text should contain parameterized query in XML format, not just the query in plain text.

parameterized

These properties may be accessed through the childInfo variable.

How AggreGate Query Language Compares to Normal SQL

There are two primary differences between "normal" SQL used by most modern Database Engines (like MySQL or Oracle) and AggreGate Query Language.

FIRST DIFFERENCE: Tables vs. contexts

Normal SQL uses tables as the primary data source. Every SELECT statement refers to one or more tables from which data is selected. These references appear in the FROM clause.

AggreGate Query Language works with data stored within LinkServer contexts. This data may be accessed by getting context variables (properties) or calling context functions. The value of every context variable is represented by a Data Table, a data structure that has fields, records and cells. These Data Tables are used by the Query Language engine as the tables from which data is selected.

The same method may be used to refer to tables containing the output values of context functions. When a SELECT statement refers to such a table (containing output values of a function), the reference should also include the input parameters for that function. When the query is executed, the function is called with these parameters. That function then generates a data table containing its output, which in turn will be used by the query.

In the Query Language Syntax article, these references to context variables or functions are called contextReference. Every contextReference may have an alias (tableAlias) that is used to refer to it from other parts of the query.

Just like normal SQL, AggreGate Query Language supports nested SELECT statements, where the FROM section of a SELECT statement contains another SELECT statement.

SECOND DIFFERENCE: FIELD references in 'FROM' CLAUSE

The second difference concerns referring to the fields of tables mentioned in the FROM clause.

In normal SQL, a reference to a table field includes two parts: tableAlias.fieldName. The first part is table's name, an alias given to it in the FROM clause or an alias given to the nested SELECT query. The second part is the field's name.

In AggreGate, a reference to a table field consists of three parts. It looks like this: tableAlias.fieldAlias. tableAlias is the alias assigned to the table generated from context's data (see First Difference, above). fieldAlias is in the form entityName$fieldName. entityName is the name of a context variable or function (such as userInfo, which is the "user information" variable of the user context). fieldName is a specific field of that variable or function output (such as firstname, the user's first name, for the userInfo context).

note_tip-wt

People familiar with classic SQL language may use the following analogy to understand the AggreGate Query Language quickly: a query in AggreGate is exactly like a normal SQL query with some exceptions:

1. Table names used in normal SQL are substituted by Context References. However, every Context Reference is used to build a table before the query is executed. So the AggreGate Query Language processor deals with tables, just like a normal database engine.
2. Field names used in normal SQL are substituted by Field References. But these Field References behave exactly like normal field names in the classic SQL query. They just have a slightly different syntax, allowing a user to refer to the fields of "virtual" tables generated from Context References. To see the exact differences in the syntax, and the logic behind them, please read about Field References below.
3. When an SQL query is executed, you get a "dumb" table, containing just values of data. However, when an AggreGate Query is executed, you get something we call a "Data Table", which is smarter than "just" a table -- it contains data validation rules, field types, and other such metadata.

Query Execution Flow

This section describes the step-by-step process of how a query text is used to generate query results.

1. At first stage, the query processing engine finds all Context References in the FROM clause of the query text. These references are then used to build ad-hoc tables, filled with data from the actual contexts, on which the query will later be executed. This process is described here.
2. A Classic SQL query is executed using the tables that were created at the previous step. The query produces a table, built according to the query syntax.
3. This table is now converted to a Data Table. This makes it simpler to understand: the final Data Table contains all field descriptions, formatting and validation rules and any other metadata that may be fetched from the Data Tables on which the query was executed. So, for all intents and purposes. AggreGate uses a query to generate a Data Table. The only reason we mentioned the previous step (2) is to give you a glimpse at the internals of the system.
4. The query result is shown to the user. If the results are editable, the user can now make changes to the data presented.
5. If the user saves the modified results, the query processing engine writes all modified data back to the context variables from which it came.

As stated in the previous chapter, the difference between AggreGate query and a normal SQL query is that it is executed on "virtual" tables generated from context data rather than on "plain" tables.

Query Syntax Overview

The only operation supported in AggreGate Query Language is the data selection operation which is performed with the SELECT keyword. SELECT retrieves data from a specified table, or from multiple related tables, that are created ad hoc when the Context References in the query are processed.

Each query has several clauses:

The primary SELECT clause, defining which fields of the original tables should be included in the query result.
The FROM clause, indicating the source table or tables from which the data is to be retrieved. The FROM clause can include optional JOIN clauses, joining related tables with each other based on user-specified criteria.
The WHERE clause includes a conditional expression, which is used to filter data returned by the query. The WHERE clause is applied before the GROUP BY clause. Technically speaking, the WHERE clause eliminates all rows from the result set where the conditional expression does not evaluate to True.
The GROUP BY clause is used to combine, or group, rows with related values. GROUP BY is often used to calculate statistics (total, average, etc) of similar rows or to eliminate duplicate rows from a result set.
The HAVING clause includes a conditional expression used to eliminate rows after the GROUP BY clause is applied to the result set.
The UNION clause allows to combine the results of two or more SELECT statements into a single result.
The ORDER BY clause is used to designate the columns used to sort resulting data, and whether they should be ascending or descending. The order of rows returned by an SQL query is never guaranteed unless an ORDER BY clause is specified.
The LIMIT clause restricts the final result to a predefined range of rows. This is just a numeric range -- for example, if you have a query resulting in 100 records, but you just want 10 arbitrary records (the first ten, last ten, ten records starting from the 15th record, etc.), you can use a LIMIT clause to get just as many records as you want.

AggreGate Query Language supports most features of SQL standard selection queries. It has support for nested SELECT queries, SQL aggregation functions used to perform calculations on various data values (COUNT, MIN, MAX, SUM, AVG, etc), and built-in functions (numerical, string, date/time and system).

For a detailed description of AggreGate Query Language syntax, click here.

Context References

A Context Reference looks like this:

contextMask { :contextEntityReference [: ...] }

contextMask is a mask of contexts whose entities (variables or functions) will be used to build tables on which the query will be executed. This mask can be a plain context name (without any wildcards) or resolve to several contexts.

contextEntityReference may point to a context variable, a group of variables or function. It looks like:

{ contextVariableName | contextVariableGroupName.* | contextFunctionName ( contextFunctionParameters ) }

The pipe marks above denote alternatives -- they're not actually included in the contextEntityReference. Here is the simplest example of a context reference:

users.admin:userInfo

this reference points to the value of the userInfo variable from the users.admin context.

The colon (:) mark is used to separate the context mask from the entity. An "entity" is a fancy name for a variable or a function. So, once you specify a mask (i.e, "where" you want to go), you then put a colon and specify the entity (i.e, "what" you want to find out when you get to where you're going). You can even use a wildcard after the colon, to refer to a group of variables (such as user.charlie.deviceservers.ds10:ds_settings.*, which will give you all settings (variables) of the ds10 Device Server which belongs to the user charlie).

Every Context Reference may combine context masks and several entity references (even of different types):

users.*:userInfo:variableGroup.*:status()

This reference points to the value of the userInfo variable, all variables that belong to the variableGroup group and to the output of the status function called without parameters. Values are fetched from all contexts corresponding to the mask users.*, i.e. to the context of all users in the system that are visible with the permission level of the user who executes this query.

Building Source Tables from Context References

When a query is executed, the first thing which happens is that every Context Reference is used to build a single table, on which the  query will be performed. Building of this table is a complex process which includes several steps:

1. The context mask is resolved to the list of contexts that correspond to it, and are accessible to the user executing the query (according to his permissions).
2. The query processing engine now retrieves and caches the values of the variables that the context reference points to. It also calls all referred functions with the parameters specified in the query text and caches the output of these functions.
3. Now a list of fields for the resulting table is generated, according to several rules. If the context reference refers to just one variable or function, the resulting table will have exactly the same fields as that variable or function output. If the context reference refers to several variables or functions, the format of the resulting table will include all of the fields that appear in the format of these variables or in the function output. The only exception occurs when a value or a function output contains more than one record. In this case, the resulting table will have just one field for this variable/function. That field will contain a nested table with the value of the variable or the function output.
4. The table is filled with data. Usually, one record is created for every context that corresponds to the context reference mask. But if just one variable/function is referred and its value/output contains several records, all these records are put in the resulting table (i.e, you won't have a table containing just one record, that record being another nested table. That wouldn't make much sense).

Click here to see where the Context References may appear in Query Language Syntax.

SPECIFYING FUNCTION PARAMETERS

When a context function is called during Context Reference resolution, it usually has to get some input values. These values are specified in a list containing string parameters. The parameters are all in one long string, and are sepearated by commas (","). The list may contain the following value types:

Value

Example

Description

Single-quoted string

'123'

Quotes are trimmed from the string. The resulting value is converted to the variable type the function expects to get for that parameter (see below).

null

null

This parameter is used to insert NULL (Non-defined) value to function input parameters table.

When the parameter list is resolved, it is used to build a Data Table according to the function input format. The value of each cell is composed from the corresponding argument. So, let's say you have a function, func1 (just an arbitrary name), which expects the following parameters:

string_field

integer_field

"abc"

123

"xxx"

456

You would then call it like this: func1('abc', '123', 'xxx', '456'). Note that even the integer values have single quotes. So, since you have two parameters per row, the first two parameters are used to fill the first row. The next two parameters are used to fill the second row, etc.

examples of field references

Let's assume you have two contexts, path.name and path.name2. The path.* context mask matches both.

path.name has two variables:

var1:

stringField

"test string"

var2:

integerField

booleanField

123

TRUE

This context also defines the func1 function, which returns the following value:

floatField

integerField

45.6

456

78.9

789

path.name2 has the same variable names, but with different values:

var1:

stringField

"string in 2nd context"

var2:

integerField

booleanField

555

FALSE

This context also defines a func1 function which returns the following value:

floatField

integerField

11.1

666

22.2

777

33.3

888

Example 1

Context Reference: path.name:var2

Table built from this simple context reference will be exactly the same as value of var2:

var2$integerField

var2$booleanField

123

TRUE

note_further-wt

Note that names of fields in the resulting table include the variables from which each field was taken (var2 above). This helps to refer to these fields using Field References.

Example 2

Context Reference: path.name:var1:var2

This reference will resolve to a table with three fields, one from the value of var1 and two from the value of var2. There's just one line, because both variables come from the same context.

var1$stringField

var2$integerField

var2$booleanField

"test string"

123

TRUE

Example 3

Context Reference: path.*:var1:var2

This reference points to a context mask, so it will result to the table with three fields, like in the previous example, but with two records, one per every context:

var1$stringField

var2$integerField

var2$booleanField

"test string"

123

TRUE

"string in 2nd context"

555

FALSE

Example 4

Context Reference: path.*:func1

This reference points to a single entity (function) with a value containing multiple records, so the resulting table will have the same fields as the function output. The total number of records in the table is five, since two records are provided by the value of func1 in path.name and three records by the value of func1 in path.name2

func1$floatField

func1$integerField

45.6

456

78.9

789

11.1

666

22.2

777

33.3

888

Example 5

Context Reference: path.*:var1:func1

Since several entities (var1 variable and func1 function) are referred here, the multi-line values of func1 will be put in nested tables. Fields in these nested tables can not be referred using Field References.

var1$stringField

func1

"test string"

[Nested table with the value of "func1" in "path.name"]

"string in 2nd context"

[Nested table with the value of "func1" in "path.name2"]

Field References

The Context References section describes how context references are used to build the tables on which queries are executed. These tables contain multiple fields that may be referred in different parts of the query, i.e. in the WHERE clause.

Field references may comprise of two or three parts:

1. When a query has just one context reference in the FROM clause, fields in the table that was built from this reference may be referred with a two-part Field References:

contextEntityName$dataTableFieldName

In this Field Reference, contextEntityName is the name of the context variable or function that was used by the single Context Reference in the query. dataTableFieldName is the name of the field in the Record Format of the Data Table containing the value of that entity.

note_example-wt

Example:

SELECT userInfo$firstname FROM users.*:userInfo

In this example, the Field Reference userInfo$firstname refers to the firstname field of a Data Table containing the value of the userInfo variable in all contexts corresponding to the users.* mask.

2. If a query has more then one Context Reference, each reference must have an alias so you'd be able to refer to the fields of the table that was built from it. The alias is assigned using the AS keyword:

contextReference AS tableAlias

note_example-wt

Example:

users.*:userInfo as ui

Here we give a table built from the users.*:userInfo Context Reference an alias named ui. Now we can refer to the fields of this table using the alias.

The generic form of a Field Reference that includes an alias is:

tableAlias.contextEntityName$dataTableFieldName

note_example-wt

Example:

SELECT ui.userInfo$firstname FROM users.*:userInfo as ui

This query works exactly as the query from previous example, but here field in the table build from "users.*:userInfo" is referred by an alias. You can see another example of using an alias below, in Example 6.

Click here to see where Field References may appear in Query Language Syntax.

note_further-wt

At first look it may seem that a reference to a field should include only two components: tableAlias and fieldName. But AggreGate Query Language lets you generate one source table (a table on which the query will be run) from the values of several context variables or from the output of several context functions. That is why we need an additional entityName. It points to the name of a context variable or a function which contains the fieldName field.

Debugging Queries

Some queries may execute without errors but return unpredictable or unexpected results. Such behaviour may be caused by non-critical errors that occur during query execution. The Query debugging feature helps view these errors. To run a query in debug mode, use the Debug Query action defined in the Query Context. This action lets you browse a query debug report before showing execution result.

The debug report has the following fields:

Message. Debug message.
Context (optional). Server context processing the data which caused the error.
Exception (optional). Text of the error generated by the context when the query accessed its variables or functions.
Stack Trace (optional). Stack trace of exception generated by LinkServer. May be requested by AggreGate technical support team during resolving of complex problems. A stack trace is just computer-generated debug information -- it's not supposed to be very human-readable.

Here is an example of debug report (screenshot made in AggreGate Client):

ls_query_debug_report2

Editable Query Results

Results of execution of some queries are editable. This is a unique feature of AggreGate Query Language, that helps collect properties of different objects or settings of hardware devices in a single table (including only the necessary fields, sorted, grouped etc) and edit these properties using a convenient form.

A field in a query result is editable if several conditions are met:

1. The field must belong to some context variable, not a function output. This is obvious, since you can write a new value for a variable back to the context from which it came, but there is no way to "write back" a function output.
2. This field must not be defined as read-only in the format of the variable from which it was taken.
3. The value of this field must not be calculated (using simple or aggregation functions or some query language expressions).
4.The field must not come from a table that was built using nested SELECT statement.
5. All fields of a source table should be selected in the SELECT clause using * or tableAlias.* (this can be avoided by using write-back fields).

Example 1:

SELECT * FROM users.*:userInfo

Most fields in the result of this query will be editable, because:

They came directly from the value of the userInfo variable,
They are writable in the the format of this variable,
They are not converted using any function or expression in the query result,
They are selected using *, without using Field References

Some fields (i.e. "Username" field) will not be editable, because they are not defined as editable in the format of the userInfo variable.

Example 2:

SELECT userInfo$firstname, userInfo$lastname FROM users.*:userInfo

The result of this query will have no editable fields, because fields to be selected are listed explicitly in the SELECT clause and write-back fields are not defined in the query text.

WRITE-BACK FIELDS

There is a special way to select a pre-defined number of fields by explicitly listing them in the SELECT clause and still make these fields editable. To do this, you have to add three so-called "write-back" fields to the list of Field References listed in the SELECT clause. These fields help the query processing engine figure out where the information contained in the editable query results should be stored when the modified result is saved.

The format of these write-back field references is as follows:

tableAlias.CONTEXT_ID, tableAlias.PARENT_ID, tableAlias.RECORD_INDEX

where tableAlias is name of alias given to the Context Reference in the FROM clause, CONTEXT_ID, PARENT_ID and RECORD_INDEX are predefined constants. Just put them in the query text as-is, without modifying anything.

These write-back fields must be always used all together.

Example:

Assuming we have a query that shows statistics of traffic generated by all Device Servers accessible by the user executing it:

SELECT

info.deviceServerInfo$owner,

info.deviceServerInfo$name,

info.deviceServerInfo$description,

info.deviceServerInfo$blocked,

info.status$servertods,

info.status$dstoserver,

FROM

users.*.deviceservers.*:status:deviceServerInfo as info

Result of this query is not editable, because selected fields are explicitly listed in the SELECT clause:

ls_query_dsstats_readonly

To make it editable, we should add write-back fields to SELECT clause:

SELECT

info.deviceServerInfo$owner,

info.deviceServerInfo$name,

info.deviceServerInfo$description,

info.deviceServerInfo$blocked,

info.status$servertods,

info.status$dstoserver,

info.CONTEXT_ID,

info.PARENT_ID,

info.RECORD_INDEX

FROM

users.*.deviceservers.*:status:deviceServerInfo as info

 

This correction will make Device Server description and its "blocked" status editable in the query result. Other fields will remain read-only, since they are defined as read-only in the format of the deviceServerInfo ("Device Server Info") and status ("Device Server Status") variables:

ls_query_dsstats_editable

If some columns are not editable due to the absence of write-back fields, this is indicated in the query debug report:

ls_query_debug_report

Parameterized Queries

Parameterized queries are used to specify some parameters during query execution. Source data for the parameterization engine is written XML format, rather than in AggreGate Query Language. When the Parameterized option is enabled in the query settings, the query engine considers the query text as parameterization data. See Parameterization Engine for more info.

Here is an example of parameterization data:

Query Text:

<form>

<format>

  <![CDATA[

        <<byusername><B><D=Filter by User Name>>

        <<username><S><D=User Name>>

  ]]>

</format>

<expression>

  SELECT * FROM users.*:userInfo <p enabled="{byusername}">WHERE userInfo$username LIKE '%<e>{username}</e>%'</p>

</expression>

</form>

This query will ask for two parameters upon execution:

ls_query_parameters

If the user checks "Filter by User Name", final parameterized query text will be

SELECT * FROM users.*:userInfo WHERE userInfo$username LIKE '%text_entered_in_User_Name_field%'

i.e. only users whose usernames include the specified text will be shown.

If "Filter by User Name" is not checked, all users will be shown because parameterized query text will be

SELECT * FROM users.*:userInfo

Built-In Queries

Several queries are built into LinkServer and appear under every user account:

All Users. This query allows viewing and editing the settings of all user accounts in a single table.
All Device Servers. This query allows viewing and editing the settings of all Device Server accounts in a single table.
Buzz All External Device Servers. Helps detect what Device Servers are currently visible by LinkServer by buzzing them all simultaneously. Note that Device Servers discovery must be executed before running this query to allow LinkServer to detect Device Servers in the local network segment. See External Device Servers for more info.
Device Server Traffic Statistics. Shows how much data was sent to and from every Device Server.

These queries are described below, in the Examples chapter.

SQL References

For more information on SQL language check the following links:

http://en.wikipedia.org/wiki/SQL

http://www.w3schools.com/sql/sql_select.asp

http://sqlzoo.net/

http://www.fluffycat.com/SQL/

http://www.baycongroup.com/tocsql.htm

Examples

EXAMPLE 1: VIEWING/EDITING USER ACCOUNTS

Viewing or changing various settings of User Accounts is a common administrative task. The query language may help LinkServer administrators to perform bulk changes to several user accounts, or view certain settings for multiple user accounts in a single ordered table.

To access the basic User Account settings we'll use a variable called "userInfo" ("User Information") defined in the User context. The value of this variable has a single record with several fields containing user's first/last name, country etc.

Here is a typical view of the User Info variable:

ls_query_userinfo_1

Now let's use one of the simplest possible queries to get the value of this variable from just one context.

Query Text:

SELECT * FROM users.admin:userInfo

 

Result of this query is a table showing the value of the userInfo variable defined in the users.admin context.

note_further-wt

When viewing or editing the properties of some system object or hardware device, its context name is usually shown in the window header:

ls_context_names_1

This query contains two clauses: SELECT and FROM. The SELECT clauses says that we should select all fields ("*"). The FROM clause contains a single Context Reference ("users.admin:userInfo") used to build a table on which the query will be executed. The contents and format of this table will match the value of the userInfo variable exactly.

All fields in the result of this query will be editable except for the fields that are defined as read-only in the format of userInfo variable.

note_further-wt

When viewing or editing the properties of some context (such as the User context), you usually see variable descriptions rather than variable names. But we must use variable names in query text: Variable names are usually shown in the tooltips that appear when mouse hovers over some variable description:

ls_variable_names_1   ls_variable_names_2

Query Result:

ls_query_userinfo_2

Now let's select the properties of all users in the system:

Query Text:

SELECT

*

FROM

users.*:userInfo

The Context Reference (users.*:userInfo) contained in the text of this query is actually a mask of contexts. During query execution this mask will resolve to the list of all user contexts accessible by the user executing the query. See Context References chapter for more info.

This query results in a table with the values of the userInfo variable of every user account accessible by the user who is executing the query. It will probably contain multiple records, one per every user account. The result of this query will also be editable.

This query is built-in to the LinkServer distribution. It is called All Users.

Query Result:

ls_query_userinfo_3

For example, you can now change the first name and last name of some user and save the changes:

ls_query_userinfo_4

Changes will be immediately visible in other parts of the system:

ls_query_userinfo_5

Now let's sort the table.

Query Text:

SELECT

*

FROM

users.*:userInfo order by userInfo$username desc

This query outputs almost the same table as the previous one, but the rows in this table are sorted by Username (username field in the format of the userInfo variable) in descending order. The query contains a two-component Field Reference (userInfo$username), used to refer to a field within the table built when resolving the Context Reference users.*:userInfo.

note_further-wt

When we examine value of "userInfo" variable in AggreGate Client or other LinkServer User Interface, field descriptions are shown in the table header instead of field names. But we must use field name in order to refer to it in query text. Field names are usually shown in tooltips that appear when mouse hovers over the field header:

ls_field_names_1   ls_field_names_2

Query Result:

ls_query_userinfo_6

We can edit the query and assign an alias, info, to our Context Reference, and then use a three-component Field Reference that includes this alias:

SELECT

*

FROM

users.*:userInfo as info

ORDER BY

info.userInfo$username desc

 

The result of this query is exactly like the previous one.

Now let's select just a limited number of fields by further modifying the same query.

Query Text:

SELECT

userInfo$username, userInfo$firstname, userInfo$lastname

FROM

users.*:userInfo

ORDER BY

userInfo$username desc

This query contains a number of Field References in the SELECT clause. Since the fields are specified explicitly, the query result will be not editable, unless we add the write-back fields. We'll add them to this query in one of the next examples.

The result of this query will contain just three columns.

Query Result:

ls_query_userinfo_7

Let's now use a Query Language expression to show the first and last name of users in a single column, to make browsing more convenient. We'll also add a new column to the query result, user's country.

Query Text:

SELECT

userInfo$username,

CASEWHEN((userInfo$firstname IS NULL), '', userInfo$firstname)  || ' ' || CASEWHEN((userInfo$lastname IS NULL), '', userInfo$lastname),

userInfo$country

FROM

users.*:userInfo

ORDER BY

userInfo$username desc

This query selects three fields from the source table. The first and third fields contain the user's username and country respectively. The value of the second field is calculated using an expression. This expression gets a user's first name and appends a space and the user's last name to it. The double pipe marks (||) used are just a standard SQL operator, used for concatenating the strings. The first and last names are transformed using the CASEWHEN built-in function to make sure that an empty string is shown if value of the first or last name is NULL ("<Not set>"). Note that the name for the second column is generated automatically.

Query Result:

ls_query_userinfo_8

Now let's assign a custom name ("label") to the second column to make the result more readable.

Query Text:

SELECT

userInfo$username,

CASEWHEN((userInfo$firstname IS NULL), '', userInfo$firstname)  || ' ' || CASEWHEN((userInfo$lastname IS NULL), '', userInfo$lastname)        as name,

userInfo$country

FROM

users.*:userInfo

ORDER BY

userInfo$username desc

Second column is now called "name" in query result.

Query Result:

ls_query_userinfo_9

Now let's make it possible to change the users' countries -- add write-back fields to make the result editable. To add them we should assign an alias to the Context Reference ("table alias"), because write-back fields (CONTEXT_ID, PARENT_ID and RECORD_INDEX) have to be specified for a given context reference. To specify which reference should be written back, you have to use an alias. Carefully read the example below to figure it all out.

Query Text:

SELECT

info.userInfo$username,

CASEWHEN((info.userInfo$firstname IS NULL), '', info.userInfo$firstname)  || ' ' || CASEWHEN((info.userInfo$lastname IS NULL), '',        info.userInfo$lastname) as name,

info.userInfo$country,

info.CONTEXT_ID,

info.PARENT_ID,

info.RECORD_INDEX

FROM

users.*:userInfo as info

ORDER BY

info.userInfo$username desc

In the result of this query, Country column will be editable. Username column is still read-only because it is read-only in the source variable ("userInfo"). Name column is read-only because it is calculated using an expression.

Query Result:

ls_query_userinfo_10

Now let's add some filtering rules to the query. As an example, let's select only users whose usernames doesn't contain word "test".

Query Text:

SELECT

info.userInfo$username,

CASEWHEN((info.userInfo$firstname IS NULL), '', info.userInfo$firstname)  || ' ' || CASEWHEN((info.userInfo$lastname IS NULL), '',        info.userInfo$lastname) as name,

info.userInfo$country,

info.CONTEXT_ID,

info.PARENT_ID,

info.RECORD_INDEX

FROM

users.*:userInfo as info

WHERE

info.userInfo$username NOT LIKE '%test%'

ORDER BY

info.userInfo$username desc

This query contains an additional WHERE clause, specifying which records should be included in the result.

Query Result:

ls_query_userinfo_11

The last line of the next example shows the LIMIT clause, used to restrict query result to a specified range of rows.

Query Text:

SELECT

info.userInfo$username,

CASEWHEN((info.userInfo$firstname IS NULL), '', info.userInfo$firstname)  || ' ' || CASEWHEN((info.userInfo$lastname IS NULL), '',        info.userInfo$lastname) as name,

info.userInfo$country,

info.CONTEXT_ID,

info.PARENT_ID,

info.RECORD_INDEX

FROM

users.*:userInfo as info

WHERE

info.userInfo$username NOT LIKE '%test%'

ORDER BY

info.userInfo$username desc

LIMIT 2 OFFSET 1

Result of this query includes just two (LIMIT 2) records (2nd and 3rd) from the result of previous one. We use OFFSET 1 here.  This means we're going to get 2 records (LIMIT 2) starting from the second record (OFFSET 1). The OFFSET clause tells AggreGate  to start taking record from the second record onwards (the first record's offset is 0).

Query Result:

ls_query_userinfo_12

EXAMPLE 2:  VIEWING/EDITING Device Server ACCOUNTS

This example is similar to selecting the basic properties of all users as described in the previous example, but here we use the deviceServerInfo variable ("Device Server Information") defined in Device Server context.

Query Text:

SELECT

*

FROM

users.*.deviceservers.*:deviceServerInfo

The text of this query includes a single Context Reference (users.*.deviceservers.*:deviceServerInfo) with a context mask that expands to all Device Server contexts which are accessible with the permissions of the user executing the query. The results of this query are editable, except for the fields that are read-only in the definition of the deviceServerInfo variable.

This query is built-in to the LinkServer distribution. It is called "All Device Servers".

Query Result:

ls_query_dsinfo_1

EXAMPLE 3:  VIEWING Device Server TRAFFIC STATISTICS

This example shows the usage of Context References that include multiple variables/fields.

Query Text:

SELECT

info.deviceServerInfo$owner,

info.deviceServerInfo$name,

info.status$servertods,

info.status$dstoserver

FROM

users.*.deviceservers.*:status:deviceServerInfo as info

ORDER BY

info.deviceServerInfo$owner,

info.deviceServerInfo$name

Context Reference in this query ("users.*.deviceservers.*:status:deviceServerInfo") refers two variables defined in Device Server context: "deviceServerInfo" mentioned in previous example and "status" ("Device Server Status") that contains some real-time values related to Device Server functioning. The values of both variables contain just one record, so the table that will be built after resolving this context reference will contain all fields that appear in the values of both variables and one record per every Device Server accessible by the user executing query.

From this table we select four columns: two that appear in the value of deviceServerInfo (Owner and name of Device Server) and two from status (Incoming and outgoing traffic). The table is sorted by two fields: first by Device Server owner, then by Device Server name (because some Device Servers may have the same owner).

This query is built-in to the LinkServer distribution. It is called "Device Server Traffic Statistics".

Query Result:

ls_query_dstraffic_1

EXAMPLE 4:  BUZZING DEVICE SERVERS

This example shows how context functions may be used in the query.

Query Text:

SELECT

*

FROM

external_device_servers.*:buzz()

This query is very interesting because it doesn't produce any output, but causes a side-effect on the server. It includes one Context Reference ("external_device_servers.*:buzz()") that involves the buzz() function ("Buzz Device Server") defined in the External Device Server context. This function is called without parameters, as specified by the empty parenthesis. The buzz function causes Device Server to blink its LEDs, helping to locate visually. Thus, this query helps to locate all Device Server that are visible by the LinkServer in the local network segment. It it very important to run the Discover Device Server action before executing this query. This action forces LinkServer to find all local Device Servers by sending broadcast network commands. See External Device Servers for more info.

If discovery was not performed, the External Device Servers context will not have any children, the external_device_servers.*context mask will not resolve to any context and no action will be performed on query execution.

The output of buzz() function contains no fields of records, so this query doesn't return any data. But its execution causes all External Device Server to buzz helping to locate them.

This query is built-in to the LinkServer distribution. It is called "Buzz All External Device Servers".

EXAMPLE 5:  CALCULATING TOTAL TRAFFIC OF ALL DEVICE SERVERS

This example shows how aggregation functions can be used.

Query Text:

SELECT

SUM(status$servertods) as server_to_ds,

SUM(status$dstoserver) as ds_to_server

FROM

users.*.deviceservers.*:status

In one of the previous examples we've mentioned the status variable ("Device Server Status") that is defined in the Device Server context and contains statistics of traffic between LinkServer and the Device Server. This query calculates how many bytes were sent to all currently visible Device Server and how many bytes were received from them. The query result contain one record, because the SUM aggregation function is used.

Query Result:

ls_query_dss_traffic_sum

note_further-wt

Since the query result contains just one row, it is shown using a "vertical" two-column layout, where field names are shown in the first column and field values in the second.

note_tip-wt

This example could be easily modified to use other aggregation functions:

AVG will help to calculate an average traffic of Device Server
MIN and MAX may be used to calculate minimum and maximum traffic respectively
COUNT may be used to find the number of Device Server (although this example is not the best one for doing that)

EXAMPLE 6: EXECUTING QUERY ON MULTIPLE TABLES

This example shows how to execute queries on multiple tables (built from multiple Context References).

Query Text:

SELECT

d.deviceServerInfo$owner || '.' || d.deviceServerInfo$name as device_server,

d.deviceServerInfo$blocked,

u.userInfo$city,

u.userInfo$country

FROM

users.*:userInfo as u, users.*.deviceservers.*:deviceServerInfo as d

WHERE

u.userInfo$username = d.deviceServerInfo$owner

This query has two Context References in the FROM clause. The first one is used to build a table containing all basic settings of all users, and the second one results in a table of all basic settings of all Device Server. These tables are then joined together based on the formula contained in WHERE clause ("u.userInfo$username = d.deviceServerInfo$owner"). The query result has four fields: first contains full Device Server name in the form "Owner Name"."Device Server Name". Second shows "Blocked" status of the Device Server. Two other columns show city and country of the user who owns the Device Server (which are probably the city and country where Device Server is located).

Query Result:

ls_query_dss_plus_users