AX2012: Poddotazy v pohledech

Dynamics AX 2012 podporuje vypočítané sloupce (computed columns) v pohledech (views) v AOT. To umožňuje hodnoty nějakým způsobem zkonvertovat, skutečně spočítat hodnotu na základě několika dalších polí a tak podobně, a to vše je definováno přímo v SQL Serveru jako jakýkoli jiný pohled. Definice vypočítaného sloupce vlastně pouze vygeneruje T-SQL řetězec, který se pak vloží do definice pohledu (hodnota je ještě přetypována na typ, který jste zvolili při vytváření vypočítaného pole).

Toto je příklad triviální definice vypočítaného sloupce:

public static server str test()
{
    return '42';
}

Pokud zadefinujete pole jako s int, vygeneruje se následující T-SQL kód:

CAST(42 AS INT) AS JMENOVASEHOPOLE

Pokud použijete stejnou metodu, ale s polem typu string, dostanete něco jako:

CAST(42 AS NVARCHAR(10)) AS JMENOVASEHOPOLE

To je všechno celkem přímočaré. Protože vlastně píšeme přímo T-SQL kód, můžeme použít funkce SQL Server, řídící struktury a tak. Například můžete přiřadit každému záznamu číslo pomocí funkce ROW_NUMBER():

public static server str test()
{
    return 'ROW_NUMBER() OVER (ORDER BY RECID)';
}

A pochopitelně můžete použít také poddotazy – pojďme se podívat na kompletní příklad.

Řekněme, že chceme vytvořit seznam uživatelů a přidat vypočítané sloupec s počtem uživatelových rolí. Nejprve vytvořte pohled, nazvaný například UserInfoView. Pak přetáhněte do uzlu Data Sources tabulku UserInfo, což vytvoří datový zdroj UserInfo_1. Přetáhněte nějaká pole do Fields (třeba id a name) a pohled je připraven. Nyní potřebujeme už jen přidat sloupec s počtem rolí.

Přidejte do pohledu novou metodu, deklarujte ji jako public static server str a nazvěte ji numOfRoles.

Na konci budeme chtít dotaz, který vypadá nějak takto:

SELECT COUNT(*) FROM SecurityUserRole WHERE USER = id

ale věci jsou trochu komplikovanější. Za prvé tabulka SecurityUserRole vůbec v SQL databázi neobsahuje pole ‘User’. Problém je pravděpodobně v tom, že USER je v T-SQL rezervované slovo, takže AX musí v databázi použít jiné jméno. To pole se ve skutečnosti jmenuje USER_, ale to není nic, o co byste se měli starat – prostě požádejte AX o správnou variantu jména. Já jsem k tomu použil metodu SysDictTable.fieldName() – všimněte si argumentu DbBackend::Sql.

SysDictTable roleDictTable = new SysDictTable(tableNum(SecurityUserRole));
roleDictTable.fieldName(fieldNum(SecurityUserRole, User), DbBackend::Sql);

Na jiný problém narazíte, pokud chcete identifikovat tabulku, do které pole id patří (to zde není nezbytně nutné, ale pojďme si to také ukázat). Nemůžete použít UserInfo.id, protože pohled přiřadil tabulce UserInfo alias (T1, alespoň na mém počítači). Řešením je použít metodu computedColumnString():

DictView dv = new DictView(tableNum(UserInfoView));
dv.computedColumnString('UserInfo_1', fieldStr(UserIfo, id), FieldNameGenerationMode::WhereClase);

Tento kód vrací úplnou identifikaci pole, v tomto případě. T1.ID. S tabulkou SecurityUserRole takový problém nemáme, protože máme její pojmenování pod kontrolou (ponechali jsme ji bez aliasu, ale mohli bychom nějaký použít, kdybychom chtěli).

Trik s DbBackend::Sql tady není třeba – computedColumnString() vrací správné jméno automaticky.

Toto je celý kód oné metody:

public static server str numOfRoles()
{
    //return 'select count(*) from SECURITYUSERROLE where USER_ = T1.ID';
 
    DictView dv = new DictView(tableNum(UserInfoView));
    SysDictTable roleDictTable = new SysDictTable(tableNum(SecurityUserRole));
    str idFieldName = dv.computedColumnString(
        'UserInfo_1',
        fieldStr(UserInfo, id),
        FieldNameGenerationMode::WhereClause);
 
    return strFmt('select count(*) from %1 where %2 = %3',
        roleDictTable.name(DbBackend::Sql),
        roleDictTable.fieldName(fieldNum(SecurityUserRole, User), DbBackend::Sql),
        idFieldName);
}

Není to právě krásný a čitelný kód, ale alespoň funguje správně. Mohl jsem ho trochu vylepšit vytvořením nějakých pomocných metod, ale tohle je lepší pro názornost. Za zmínku stojí, že můžete také použít (a vytvářet) pomocné metody v třídě SysComputedColumn.

Když použijete tuto metodu ve vypočítaném poli v pohledu, synchronizaci vytvoří následující (nebo podobný) pohled v databázi. Je velmi jednoduchý, ale složité dotazy můžete vytvořit úplně stejným způsobem.

SELECT NAME, ID, RECID,
    CAST ((SELECT COUNT(*) AS Expr1
            FROM  dbo.SECURITYUSERROLE
	    WHERE (USER_ = T1.ID)
        ) AS INT
    ) AS NUMOFROLES
FROM dbo.USERINFO AS T1

Vypočítané sloupce v Dynamics AX 2012 nabízí bezpochyby spoustu možností. Je to způsob, jak plně využít schopnosti SQL Serveru – poddotazy jsou jen jeden malý příklad. Nevýhodou je, že jejich použití není úplně přímočaré a musíte si dávat pozor na věci jako jména polí a dopady na zabezpečení dat.