Axaptapedia is now maintained by AgileCadence For more information please click here.
Having Clause
The Having Clause is a part of an SQL Statemet which places a condition on an Aggregate Element. If for Instance you execute a select statement which looks like this:
Select count(RecId), ItemId From InventTrans Where DateExpected >= 01012006 And Qty > 0 Group By ItemId;
The result set would show the number of records grouped by ItemId from the InventTrans Table whith the restrictions that the DateExpected Field must have a Date greater or Equal to the 1st of January 2006 and the Quantity Field must have a Value greater than zero.
If you wish to additionally restrict the selection so that only records with a count greater than one are selected, then the Having Clause must be used:
Select count(RecId), ItemId From InventTrans Where DateExpected >= 01012006 And Qty > 0 Group By ItemId Haveing count(RecId) > 1;
Neat huh !? The only problem is this won't work in Axapta, because the Having Clause is not supported :-(
Well... It's a little complicated, but I've given it a shot and maybe this solution will help. It will only work with Queries.
Here's the Code: First you need to build a small class for FieldId information: <xpp> class My_FieldId implements SysPackable {
TableId tableId; FieldId fieldId; Name dsName;
#define.CurrentVersion(1) #localmacro.CurrentList tableId,
fieldId, dsName
#endmacro } </xpp> <xpp> void new(
TableId _tableId = 0, FieldId _fieldId = 0, Name _dsName = )
{
;
tableId = _tableId; fieldId = _fieldId; dsName = _dsName;
} </xpp> <xpp> public container pack() { ; return [#CurrentVersion,#CurrentList]; } </xpp> <xpp> public boolean unpack(container _packedClass) { int version = runbase::getVersion(_packedClass); switch (version) { case #CurrentVersion: [version,#CurrentList] = _packedClass; return true; default : return false; } return false; } </xpp> <xpp> TableId tableId(TableId _tableId = tableId) {
; tableId = _tableId; return tableId;
} </xpp> <xpp> FieldId fieldId(FieldId _fieldId = fieldId) {
; fieldId = _fieldId; return fieldId;
} </xpp> <xpp> Name dsName(Name _dsName = dsName) {
; dsName = _dsName; return dsName;
} </xpp> <xpp> str toString() {
return strfmt("%1;%2;%3", tableId, fieldId, dsName);
} </xpp>
Now comes a new Static Method for the SysQuery Class: <xpp> public static str My_HavingList(
Query _query, QueryBuildDataSource _qbds, List _groupByFields, FieldId _evaluationField, AnyType _value, CuesThresholdCriteria _criteria )
{
Queryrun queryRun; My_FieldId qgbf; TableId tableId; FieldId fieldId; TableId gb_tableId; FieldId gb_fieldId; Common common; Map mapRecords; AnyType value;
str txt; str ret; str condition; int i;
boolean test() { ; switch (_criteria) { case CuesThresholdCriteria::Equals : return value == _value; case CuesThresholdCriteria::NotEquals : return value != _value; case CuesThresholdCriteria::LessThan : return value < _value; case CuesThresholdCriteria::LessThanOrEqual : return value <= _value; case CuesThresholdCriteria::GreaterThan : return value > _value; case CuesThresholdCriteria::GreaterThanOrEqual : return value >= _value; default : return false; } }
str buildCondition() { ListEnumerator le = _groupByFields.getEnumerator(); Name dsName; Common lCommon; str lret; str lValue; int lCnt; ; while (le.moveNext()) { qgbf = new My_FieldId(); qgbf.unpack(le.current()); gb_tableId = qgbf.tableId(); gb_fieldId = qgbf.fieldId(); dsName = qgbf.dsName(); if (dsName) lCommon = mapRecords.lookup(dsName); else lCommon = queryRun.get(gb_tableId);
if (lCommon) { if (new DictField(lCommon.TableId, gb_fieldId).baseType() == Types::String) lValue = strfmt('(%1.%2 == "%3")', dsName, fieldid2name(gb_tableId, gb_fieldId), queryValue(lCommon.(gb_fieldId))); else lValue = strfmt('(%1.%2 == %3)', dsName, fieldid2name(gb_tableId, gb_fieldId), lCommon.(gb_fieldId));
lret += (lret ? ' && ' : ) + lValue; lCnt++; } } return (lCnt > 1 ? '(' + lret + ')' : lCnt == 1 ? lret : ); } ;
if(_query && _groupByFields && _value && _criteria) { txt = _query.dataSourceNo(1).toString(); tableId = _qbds.table();
if (tableId && _evaluationField) { queryRun = new QueryRun(_query); while (queryRun.next()) { mapRecords = new Map(Types::String, Types::Record); for (i = 1; i <= _query.dataSourceCount(); i++) { common = queryRun.get(_query.dataSourceNo(i).table()); mapRecords.insert(_query.dataSourceNo(i).name(), common); } common = queryRun.get(tableId); value = common.(_evaluationField); if (test()) { condition = strfmt('%1', buildCondition()); if (condition) { ret += (ret ? ' || ' : ) + condition; } } } } }
return (ret ? strfmt('(%1)', ret) : );
} </xpp>
And now an Example of How to Use it:
<xpp>
static void TestJob_HavingClause(Args _args)
{
Query query = new Query(); QueryRun qr; QueryBuildDataSource dsSalesTable; QueryBuildFieldList qbflSalesTable;
SalesTable salesTable;
My_FieldId qgbf1; My_FieldId qgbf2; My_FieldId qgbf3;
QueryBuildRange range1; str rangeValue; ;
dsSalesTable = query.addDataSource(tableNum(SalesTable), identifierstr(SalesTable));
qbflSalesTable = dsSalesTable.fields(); qbflSalesTable.clearFieldList();
qbflSalesTable.addField (fieldNum(SalesTable, RecId), SelectionField::Count);
dsSalesTable.orderMode(OrderMode::GroupBy);
dsSalesTable.addSortField (fieldNum(SalesTable, CustAccount)); dsSalesTable.addSortField (fieldNum(SalesTable, InvoiceAccount)); dsSalesTable.addSortField (fieldNum(SalesTable, SalesResponsible)); qgbf1 = new My_FieldId(tablenum(SalesTable), fieldNum(SalesTable, CustAccount), identifierstr(SalesTable)); qgbf2 = new My_FieldId(tablenum(SalesTable), fieldNum(SalesTable, InvoiceAccount), identifierstr(SalesTable)); qgbf3 = new My_FieldId(tablenum(SalesTable), fieldNum(SalesTable, SalesResponsible), identifierstr(SalesTable));
range1 = dsSalesTable.addRange(fieldNum(SalesTable, DataAreaId)); rangeValue = SysQuery::My_HavingList( query, // The Query dsSalesTable, // The DataSource where the Aggregate Function Field comes from con2list([qgbf1.pack(), // A List of packed Field Information of Fields which are not Aggregate qgbf2.pack(), qgbf3.pack()]), fieldNum(SalesTable, RecId), // The FieldId of the Aggreagate Field 1, // The Value for the Having to be evaluated CuesThresholdCriteria::GreaterThan); // The Enumarator which define the evaluation range1.value(rangeValue);
qr = new QueryRun(query); while (qr.next()) { salesTable = qr.get(tablenum(SalesTable)); info (strfmt("%1;%2;%3;%4", salesTable.RecId, salesTable.CustAccount, salesTable.InvoiceAccount, salesTable.SalesResponsible )); }
} </xpp>