Avanzado y Rendimiento

Queries

Dificultad: 7/10

En el mundo real con millones de registros, una Query es el único objeto de AL capaz de interactuar directamente con el motor SQL de forma rápida y eficiente, devolviendo datos ya agrupados y calculados sin necesidad de recorrerlos uno a uno desde AL.

Estructura base de una Query

Una Query se declara como un objeto independiente con su propio ID, igual que una tabla o una página:

AL
query 50100 "Mi Primera Query"
{
    Caption   = 'Mi Primera Query';
    QueryType = Normal;

    elements
    {
        dataitem(Socio; Socio)  // "Lee la tabla Socio"
        {
            column(Num_Socio; "No.")   // "Quiero ver el campo No."
            {
                Caption = 'Número de Socio';
            }
            column(Nombre; Nombre)      // "Quiero ver el campo Nombre"
            {
                Caption = 'Nombre';
            }
        }
    }
}

¿Qué es una Query?

Imagina que tienes una lista de socios abierta en pantalla y necesitas saber: ¿cuántos días en total lleva prestando libros cada socio? Para responder a eso, tendrías que mirar cada préstamo de cada socio, sumar los días uno a uno y apuntarlo en algún sitio. Con 500 socios y 3.000 préstamos, eso es inviable a mano.

Una Query hace ese trabajo por ti: recorre los datos, los agrupa y calcula los totales de forma automática y muy eficiente, todo desde código AL, sin pantallas ni botones.

Query vs Page — ¿cuándo usar cada uno?

Situación Usa esto
El usuario necesita ver y editar registros Page
Necesitas calcular totales o agrupar datos Query
Quieres datos para un proceso en segundo plano Query
Necesitas mostrar un listado con filtros al usuario Page
Quieres saber cuántos préstamos tiene cada socio Query

Rendimiento puro frente a Pages

Si recolectas datos a mano con el estilo clásico de AL (abrir un Record mediante bucle → si pasa mis filtros lo guardo → actualizo mi total), estás ejecutando muchos ciclos de procesador y memoria RAM con SetFilter y FindSet(). Esto destruirá las instancias del servidor a escala.

Una Query toma todo lo que le has definido (varias tablas a unir simultáneamente) y compacta la petición en una sola orden SQL nativa. SQL retorna las tablas ya calculadas infinitamente más rápido.

Piénsalo así: una Page es como una ventanilla de atención al cliente. Una Query es como el empleado del almacén que va, cuenta todo el stock y te trae el número exacto sin interrupciones.

Métodos de agrupación (Method)

La clave de un Query está en la propiedad Method. Cuando la pones en una columna numérica, le dices a BC que no te traiga cada valor por separado, sino que los agregue. Cuando usas Method en una columna, el Query agrupa automáticamente el resultado por todas las demás columnas sin Method.

Method Qué hace
Method = Sum Suma todos los valores de esa columna
Method = Count Cuenta cuántas filas hay
Method = Average Calcula la media de los valores
Method = Min Devuelve el valor más pequeño
Method = Max Devuelve el valor más grande
AL
query 50100 "Ventas Por Almacen"
{
    elements
    {
        dataitem(Venta; "Sales Invoice Header")
        {
            column(Location_Code; "Location Code") { }  // columna de agrupación

            column(TotalEuros; "Amount")
            {
                Method = Sum;  // SQL suma directamente, sin bucles en AL
            }
        }
    }
}

Ejemplo completo: días de préstamo por socio

Una Query que une dos tablas y calcula totales. Las columnas sin Method son las claves de agrupación; BC agrupa los resultados por ellas automáticamente:

AL
query 50101 "Dias Totales Prestamo por Socio"
{
    Caption   = 'Días Totales de Préstamo por Socio';
    QueryType = Normal;

    elements
    {
        dataitem(Socio; Socio)
        {
            // Sin Method → claves de agrupación
            column(Num_Socio; "No.")     { Caption = 'Nº Socio'; }
            column(Nombre_Socio; Nombre)   { Caption = 'Nombre'; }

            dataitem(LineaPrestamo; "Linea Prestamo")
            {
                // Enlace entre tablas: cada línea con su socio
                DataItemLink = "No. Socio" = Socio."No.";

                // LeftOuterJoin: incluye socios sin préstamos (aparecen con 0)
                SqlJoinType  = LeftOuterJoin;

                column(Total_Dias; "Dias Prestamo")
                {
                    Caption = 'Total Días';
                    Method  = Sum;    // suma todos los días por socio
                }
                column(Num_Prestamos; "No. Prestamo")
                {
                    Caption = 'Número de Préstamos';
                    Method  = Count;  // cuenta cuántos préstamos tiene cada socio
                }
            }
        }
    }
}

Usar una Query desde código AL

Una vez definida la Query, se usa desde un Codeunit siguiendo siempre el mismo flujo: filtrar → abrir → leer → cerrar.

⚠️ Orden obligatorio

Los filtros con SetFilter se deben poner antes de Open(). Si los pones después, ya no tienen efecto porque BC ya ha traído los datos.

AL
procedure MostrarDiasPorSocio()
var
    QDias  : Query "Dias Totales Prestamo por Socio";
    Mensaje: Text;
begin
    // Paso 1: aplicar filtros ANTES de abrir
    QDias.SetFilter(Num_Socio, 'S001..S050');

    // Paso 2: abrir la Query (BC va a buscar los datos en este momento)
    QDias.Open();

    // Paso 3: recorrer los resultados fila a fila
    while QDias.Read() do begin
        Mensaje := StrSubstNo(
            'Socio: %1 | Nombre: %2 | Días: %3 | Préstamos: %4',
            QDias.Num_Socio,
            QDias.Nombre_Socio,
            QDias.Total_Dias,
            QDias.Num_Prestamos
        );
        Message(Mensaje);
    end;

    // Paso 4: cerrar siempre al terminar
    QDias.Close();
end;

💡 Tips

  • Una Query no tiene pantalla. Solo lee datos y los devuelve ya calculados. Para mostrarlos al usuario usa una Page con SourceTableTemporary = true.
  • Las columnas sin Method son las claves de agrupación. Las columnas con Method son los valores calculados.
  • SqlJoinType = LeftOuterJoin incluye registros del padre aunque no tengan hijos (aparecen con 0 o vacío). El valor por defecto es InnerJoin, que los excluye.
  • Siempre llama a QDias.Close() al terminar. Es una buena práctica que libera recursos en el servidor.
  • Usa Queries cuando necesites rendimiento a escala: agrupaciones, sumas de millones de registros o uniones de varias tablas simultáneamente.
← Volver a Teoría