Business Logic Framework - Tutorial 16:
This tutorial shows you how to run queries that use the GROUP BY clause. The business object's simplify these complex queries and you can continue to rely on the consistent way of retrieving records.
To run a query that groups the results set on one or more fields or performs aggregate functions on columns, we are required to set the ObjectMode property of the business object to ObjectModes.GroupBy. It is important to set the ObjectMode property before creating joins with other objects, which will ensure that all joined objects are included in the result set. Failing to do so can result in unexpected results.
In this tutorial, our purpose is to return a list of customer names along with the total amount of orders placed by each of them. Using a SQL statement we can achieve this result by joining the customer table with orders table, and performing group by on the result set by customer name and enclosing the OrderAmount in SUM aggregate function.
| // Please note: This tutorial requires that the QO_SHOP sample database included with v 3.x. | |
| // If you have have the the database schema from a previous version of QO_SHOP please upgrade the | |
| // QO_SHOP database by using the Drop Objects.sql and Create Objects.sql. If you like the default | |
| // test data to be automatically inserted, please use the Insert Data.sql file. | |
| // The purpose of this tutorial is to display the ability to run Group By based SQL Statements without having to | |
| // hard code any element of the statement. Use the fully strongly typed object model, and the Quick Objects will | |
| // take care of the rest. | |
| // In this tutorial we will return a list of Customer names along with their total order amount. | |
| using (Customers customer = new Customers()) | |
| { | |
| // We are going to add a calculated field that will concatenate FirstName and LastName values to create a Name column. | |
| // Lets check what database type is being used and assign the appropriate expression. | |
| // If you know your database type for certain then you don't need to perform this check and you can directly | |
| // assign the expression. Since this sample can be used with Oracle as well we are specifying | |
| // both expressions to ensure compatibility. | |
| string expression = String.Empty; | |
| if (customer.DBType == Akal.QuickObjects.DBLibrary.DatabaseObject.DatabaseTypes.SqlServer2000 | |
| || | |
| customer.DBType == Akal.QuickObjects.DBLibrary.DatabaseObject.DatabaseTypes.SqlServer2005 | |
| ) | |
| { | |
| // in SQL Server a + sign is used to concatenate two string values. | |
| expression = " + ' ' + "; | |
| } | |
| else | |
| { | |
| // in Oracle a || symbols are used to concatenate two string values. | |
| expression = "|| ' ' ||"; | |
| } | |
| // We can use the GetObjectName method of the Utilities to get a database representation of the FirstName field. | |
| // This will ensure that we have not hard coded the underlying name and keeping everything strongly typed. | |
| // Also, this method call will enclose the object name as necessary based on the database type. | |
| expression = "(" | |
| + Utilities.GetObjectName(customer.FirstName.Name, false, true, customer.DBType) | |
| + expression + | |
| Utilities.GetObjectName(customer.LastName.Name, false, true, customer.DBType) | |
| + ")"; | |
| // Note: custom/calculated fields do not perform automatic case conversion so if you want your custom | |
| // column to perform the case conversion you can use any of the alternate lines of code below (commented out). | |
| //string expression = "UPPER(" + customer.FirstName.Name + " + ' ' + " + customer.LastName.Name + ")"; | |
| //string expression = "LOWER(" + customer.FirstName.Name + " + ' ' + " + customer.LastName.Name + ")"; | |
| // In version 3.x a new mode called GroupBy has been introduced in the ObjectModes enumeration. | |
| // By setting the ObjectMode property of the customer object we are letting the customer object know | |
| // that we want the customer object to created a query based on GROUP BY clause. | |
| customer.ObjectMode = ObjectModes.GroupBy; | |
| // Any objects that are joined to the customer object after the ObjectMode has been set will automatically receive the ObjectMode property value. | |
| // This will mean that the Orders object's ObjectMode property will change to ObjectModes.GroupBy automatically. | |
| customer.Join_CustomerID_Orders_Child(); | |
| // Here we create an instance of a StringField | |
| // Each Field's constructor has multiple overloads, and we will use the one that accepts three parameters. | |
| // First parameter is the business object that will contain the field, the second one is the Name of the field. | |
| // Since we are going to create a custom field we use the Name property of the field as our expression. The last argument | |
| // is used to specify the alias of the column. | |
| // For a custom column to work correctly, you must specify a unique value for the ResultSetName property. In our case we specified | |
| // this value in the constructor. | |
| StringField sf = new StringField(customer, expression, "Name"); | |
| // IsCustom property must be set to true. | |
| sf.IsCustom = true; | |
| // Since we want to return order totals for each customer, we must group by the result set by the Name column. | |
| // To achieve this, simply set the GroupType property to GroupTypes.GroupBy (introduced in v 3.x) | |
| sf.GroupType = GroupTypes.GroupBy; | |
| // We must specify the position of this field in the group by clause. Valid position is 1 or greater. | |
| // To ensure accurate results, please ensure that position values are unique in all the fields included. | |
| // GroupPosition value is only required if the GroupType value is set to GroupTypes.GroupBy | |
| sf.GroupPosition = 1; | |
| // To have the result set include order totals for a custom now we need to set the GroupType property of the OrderAmount | |
| // field to GroupTypes.Sum | |
| customer.CustomerID_Orders_Child.OrderAmount.GroupType = GroupTypes.Sum; | |
| // Define the alias that will be used as the column name in the result set. | |
| customer.CustomerID_Orders_Child.OrderAmount.ResultSetName.Value = "Order Amount"; | |
| // Since the OrderAmount field's GroupType property is set to GroupTypes.Sum | |
| // it is not required that you set the GroupPosition value. | |
| //customer.CustomerID_Orders_Child.OrderAmount.GroupPosition = 2; | |
| customer.Find(); | |
| // if the Find method returned any records then we will set the Grid View's DataSource and DataMember properties accordingly. | |
| if (customer.AffectedRecords > 0) | |
| { | |
| this.GridView1.DataSource = customer.ResultSet; | |
| this.GridView1.DataMember = customer.GetResultSetName(); | |
| } | |
| else | |
| { | |
| this.GridView1.DataSource = null; | |
| this.GridView1.DataMember = null; | |
| } | |
| } | |
| 'Please note: This tutorial requires that the QO_SHOP sample database included with v 3.x. | |
| 'If you have have the the database schema from a previous version of QO_SHOP please upgrade the | |
| 'QO_SHOP database by using the Drop Objects.sql and Create Objects.sql. If you like the default | |
| 'test data to be automatically inserted, please use the Insert Data.sql file. | |
| 'The purpose of this tutorial is to display the ability to run Group By based SQL Statements without having to | |
| 'hard code any element of the statement. Use the fully strongly typed object model, and the Quick Objects will | |
| 'take care of the rest. | |
| 'In this tutorial we will return a list of Customer names along with their total order amount. | |
| Using customer As Customers = New Customers() | |
| ' We are going to add a calculated field that will concatenate FirstName and LastName values to create a Name column. | |
| ' Lets check what database type is being used and assign the appropriate expression. | |
| ' If you know your database type for certain then you don't need to perform this check and you can directly | |
| ' assign the expression. Since this sample can be used with Oracle as well we are specifying | |
| ' both expressions to ensure compatibility. | |
| Dim expression As String = String.Empty | |
| If (customer.DBType = Akal.QuickObjects.DBLibrary.DatabaseObject.DatabaseTypes.SqlServer2000 Or customer.DBType = Akal.QuickObjects.DBLibrary.DatabaseObject.DatabaseTypes.SqlServer2005) Then | |
| ' in SQL Server a + sign is used to concatenate two string values. | |
| expression = " + ' ' + " | |
| Else | |
| ' in Oracle a || symbols are used to concatenate two string values. | |
| expression = "|| ' ' ||" | |
| End If | |
| ' We can use the GetObjectName method of the Utilities to get a database representation of the FirstName field. | |
| ' This will ensure that we have not hard coded the underlying name and keeping everything strongly typed. | |
| ' Also, this method call will enclose the object name as necessary based on the database type. | |
| expression = "(" & Utilities.GetObjectName(customer.FirstName.Name, False, True, customer.DBType) & expression & Utilities.GetObjectName(customer.LastName.Name, False, True, customer.DBType) & ")" | |
| 'Note: custom/calculated fields do not perform automatic case conversion so if you want your custom | |
| 'column to perform the case conversion you can use any of the alternate lines of code below (commented out). | |
| 'string expression = "UPPER(" + customer.FirstName.Name + " + ' ' + " + customer.LastName.Name + ")" | |
| 'string expression = "LOWER(" + customer.FirstName.Name + " + ' ' + " + customer.LastName.Name + ")" | |
| 'In version 3.x a new mode called GroupBy has been introduced in the ObjectModes enumeration. | |
| 'By setting the ObjectMode property of the customer object we are letting the customer object know | |
| 'that we want the customer object to created a query based on GROUP BY clause. | |
| customer.ObjectMode = ObjectModes.GroupBy | |
| 'Any objects that are joined to the customer object after the ObjectMode has been set will automatically receive the ObjectMode property value. | |
| 'This will mean that the Orders object's ObjectMode property will change to ObjectModes.Grouping automatically. | |
| customer.Join_CustomerID_Orders_Child() | |
| 'Here we create an instance of a StringField | |
| 'Each Field's constructor has multiple overloads, and we will use the one that accepts three parameters. | |
| 'First parameter is the business object that will contain the field, the second one is the Name of the field. | |
| 'Since we are going to create a custom field we use the Name property of the field as our expression. The last argument | |
| 'is used to specify the alias of the column. | |
| 'For a custom column to work correctly, you must specify a unique value for the ResultSetName property. In our case we specified | |
| 'this value in the constructor. | |
| Dim sf As StringField = New StringField(customer, expression, "Name") | |
| 'IsCustom property must be set to true. | |
| sf.IsCustom = True | |
| 'Since we want to return order totals for each customer, we must group by the result set by the Name column. | |
| 'To achieve this, simply set the GroupType property to GroupTypes.GroupBy (introduced in v 3.x) | |
| sf.GroupType = GroupTypes.GroupBy | |
| 'We must specify the position of this field in the group by clause. Valid position is 1 or greater. | |
| 'To ensure accurate results, please ensure that position values are unique in all the fields included. | |
| 'GroupPosition value is only required if the GroupType value is set to GroupTypes.GroupBy | |
| sf.GroupPosition = 1 | |
| 'To have the result set include order totals for a custom now we need to set the GroupType property of the OrderAmount | |
| 'field to GroupTypes.Sum | |
| customer.CustomerID_Orders_Child.OrderAmount.GroupType = GroupTypes.Sum | |
| ' Define the alias that will be used as the column name in the result set. | |
| customer.CustomerID_Orders_Child.OrderAmount.ResultSetName.Value = "Order Amount" | |
| 'Since the OrderAmount field's GroupType property is set to GroupTypes.Sum | |
| 'it is not required that you set the GroupPosition value. | |
| 'customer.CustomerID_Orders_Child.OrderAmount.GroupPosition = 2 | |
| customer.Find() | |
| 'if the Find method returned any records then we will set the Grid View's DataSource and DataMember properties accordingly. | |
| If customer.AffectedRecords > 0 Then | |
| Me.GridView1.DataSource = customer.ResultSet | |
| Me.GridView1.DataMember = customer.GetResultSetName() | |
| Else | |
| Me.GridView1.DataSource = Nothing | |
| Me.GridView1.DataMember = Nothing | |
| End If | |
| End Using | |
