Hibernate.orgCommunity Documentation

Capítulo 16. HQL: El lenguaje de consulta de Hibernate

16.1. Sensibilidad a mayúsculas
16.2. La cláusula from
16.3. Asociaciones y uniones (joins)
16.4. Formas de sintaxis unida
16.5. Referencia a la propiedad identificadora
16.6. La cláusula select
16.7. Funciones de agregación
16.8. Consultas polimórficas
16.9. La cláusula where
16.10. Expresiones
16.11. La cláusula order by
16.12. La cláusula group by
16.13. Subconsultas
16.14. Ejemplos de HQL
16.15. Declaraciones UPDATE y DELETE masivas
16.16. Consejos y Trucos
16.17. Componentes
16.18. Sintaxis del constructor de valores por fila

Hibernate utiliza un lenguaje de consulta potente (HQL) que se parece a SQL. Sin embargo, comparado con SQL, HQL es completamente orientado a objetos y comprende nociones como herencia, polimorfismo y asociación.

Las consultas no son sensibles a mayúsculas, a excepción de los nombres de las clases y propiedades Java. De modo que SeLeCT es lo mismo que sELEct e igual a SELECT, pero org.hibernate.eg.FOO no es lo mismo que org.hibernate.eg.Foo y foo.barSet no es igual a foo.BARSET.

Este manual utiliza palabras clave HQL en minúsculas. Algunos usuarios encuentran que las consultas con palabras clave en mayúsculas son más fáciles de leer, pero esta convención no es apropiada para las peticiones incluidas en código Java.

La consulta posible más simple de Hibernate es de esta manera:

from eg.Cat

Esto retorna todas las instancias de la clase eg.Cat. Usualmente no es necesario calificar el nombre de la clase ya que auto-import es el valor predeterminado. Por ejemplo:

from Cat

Con el fin de referirse al Cat en otras partes de la petición, necesitará asignar un alias. Por ejemplo:

from Cat as cat

Esta consulta asigna el alias cat a las instancias Cat, de modo que puede utilizar ese alias luego en la consulta. La palabra clave as es opcional. También podría escribir:

from Cat cat

Pueden aparecer múltiples clases, lo que causa un producto cartesiano o una unión "cruzada" (cross join).

from Formula, Parameter
from Formula as form, Parameter as param

Se considera como una buena práctica el nombrar los alias de consulta utilizando una inicial en minúsculas, consistente con los estándares de nombrado de Java para las variables locales (por ejemplo, domesticCat).

También puede asignar alias a entidades asociadas o a elementos de una colección de valores utilizando una join. Por ejemplo:

from Cat as cat
    inner join cat.mate as mate
    left outer join cat.kittens as kitten
from Cat as cat left join cat.mate.kittens as kittens
from Formula form full join form.parameter param

Los tipos de uniones soportadas se tomaron prestados de ANSI SQL

Las construcciones inner join, left outer join y right outer join se pueden abreviar.

from Cat as cat
    join cat.mate as mate
    left join cat.kittens as kitten

Puede proveer condiciones extras de unión utilizando la palabra clave with de HQL.

from Cat as cat
    left join cat.kittens as kitten
        with kitten.bodyWeight 
> 10.0

A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection. It effectively overrides the outer join and lazy declarations of the mapping file for associations and collections. See Sección 21.1, “Estrategias de recuperación” for more information.

from Cat as cat
    inner join fetch cat.mate
    left join fetch cat.kittens

Usualmente no se necesita asignársele un alias a una unión de recuperación ya que los objetos asociados no se deben utilizar en la cláusula where (ni en cualquier otra cláusula). Los objetos asociados no se retornan directamente en los resultados de la consulta. En cambio, se pueden acceder por medio del objeto padre. La única razón por la que necesitaríamos un alias es si estamos uniendo recursivamente otra colección:

from Cat as cat
    inner join fetch cat.mate
    left join fetch cat.kittens child
    left join fetch child.kittens

La construcción fetch no puede utilizarse en consultas llamadas que usen iterate() (aunque se puede utilizar scroll()). Fetch se debe usar junto con setMaxResults() o setFirstResult() ya que estas operaciones se basan en las filas de resultados, las cuales usualmente contienen duplicados para la recuperación de colección temprana, por lo tanto, el número de filas no es lo que se esperaría. Fetch no se debe usar junto con una condición with improvisadas. Es posible crear un producto cartesiano por medio de una recuperación por union más de una colección en una consulta, así que tenga cuidado en este caso. La recuperación por unión de múltiples roles de colección también da resultados a veces inesperados para mapeos de bag, así que tenga cuidado de cómo formular sus consultas en este caso. Finalmente, observe que full join fetch y right join fetch no son significativos.

Si está utilizando una recuperación perezosa a nivel de propiedad (con instrumentación de código byte), es posible forzar a Hibernate a traer las propiedades perezosas inmediatamente utilizando fetch all properties.

from Document fetch all properties order by name
from Document doc fetch all properties where lower(doc.name) like '%cats%'

HQL soporta dos formas de unión de asociación: implicit y explicit.

Las consultas que se mostraron en la sección anterior todas utilizan la forma explicit, en donde la palabra clave join se utiliza explícitamente en la claúsula from. Esta es la forma recomendada.

La forma implicit no utiliza la palabra clave join. Las asociaciones se "desreferencian" utilizando la notación punto. Uniones implicit pueden aparecer en cualquiera de las cláusulas HQL. La unión implicit causa uniones internas (inner joins) en la declaración SQL que resulta.

from Cat as cat where cat.mate.name like '%s%'

Hay dos maneras de referirse a la propiedad identificadora de una entidad:

Las referencias a propiedades identificadoras compuestas siguen las mismas reglas de nombramiento. Si la entidad no tiene un id del nombre de la propiedad no-identificadora, la propiedad identificadora compuesta sólamente puede ser referenciada por su nombre definido. De otra manera se puede utilizar la propiedad id especial para referenciar la propiedad identificadora.

La cláusula select escoge qué objetos y propiedades devolver en el conjunto de resultados de la consulta. Considere lo siguiente:

select mate
from Cat as cat
    inner join cat.mate as mate

La consulta seleccionará mates de otros Cats. Puede expresar esta consulta de una manera más compacta así:

select cat.mate from Cat cat

Las consultas pueden retornar propiedades de cualquier tipo de valor incluyendo propiedades del tipo componente:

select cat.name from DomesticCat cat
where cat.name like 'fri%'
select cust.name.firstName from Customer as cust

Las consultas pueden retornar múltiples objetos y/o propiedades como un array de tipo Object[],

select mother, offspr, mate.name
from DomesticCat as mother
    inner join mother.mate as mate
    left outer join mother.kittens as offspr

O como una List:

select new list(mother, offspr, mate.name)
from DomesticCat as mother
    inner join mother.mate as mate
    left outer join mother.kittens as offspr

O asumiendo que la clase Family tiene un constructor apropiado - como un objeto Java de tipo seguro:

select new Family(mother, mate, offspr)
from DomesticCat as mother
    join mother.mate as mate
    left join mother.kittens as offspr

Puede asignar alias para expresiones seleccionadas utilizando as:

select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n
from Cat cat

Esto es lo más útil cuando se usa junto con select new map:

select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n )
from Cat cat

Esta consulta devuelve un Map de alias a valores seleccionados.

Las consultas HQL pueden incluso retornar resultados de funciones de agregación sobre propiedades:

select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
from Cat cat

Las funciones de agregación soportadas son:

Puede utilizar operadores aritméticos, concatenación y funciones SQL reconocidas en la cláusula select:

select cat.weight + sum(kitten.weight)
from Cat cat
    join cat.kittens kitten
group by cat.id, cat.weight
select firstName||' '||initial||' '||upper(lastName) from Person

Las palabras clave distinct y all se pueden utilizar y tienen las misma semántica que en SQL.

select distinct cat.name from Cat cat

select count(distinct cat.name), count(cat) from Cat cat

Una consulta como:

from Cat as cat

devuelve instancias no sólamente de Cat, sino también de subclases como DomesticCat. Las consultas de Hibernate pueden nombrar cualquier clase o interfaz Java en la cláusula from. La consulta retornará instancias de todas las clases persistentes que extiendan esa clase o implementen la interfaz. La siguiente consulta retornaría todos los objetos persistentes.

from java.lang.Object o

La interfaz Named se podría implementar por varias clases persistentes:

from Named n, Named m where n.name = m.name

Las dos últimas consultas requerirán más de un SELECT SQL. Esto significa que la cláusula order by no ordenará correctamente todo el conjunto que resulte. También significa que no puede llamar estas consulta usando Query.scroll().

La cláusula where le permite refinar la lista de instancias retornadas. Si no existe ningún alias, puede referirse a las propiedades por nombre:

from Cat where name='Fritz'

Si existe un alias, use un nombre de propiedad calificado:

from Cat as cat where cat.name='Fritz'

Esto retorna instancias de Cat llamadas 'Fritz'.

La siguiente petición:

select foo
from Foo foo, Bar bar
where foo.startDate = bar.date

retornará todas las instancias de Foo con una instancia de bar con una propiedad date igual a la propiedad startDate del Foo. Las expresiones de ruta compuestas hacen la cláusula where extremadamente potente. Tome en consideración lo siguiente:

from Cat cat where cat.mate.name is not null

Esta consulta se traduce a una consulta SQL con una unión de tabla (interna). Por ejemplo:

from Foo foo
where foo.bar.baz.customer.address.city is not null

terminaría con una consulta que requeriría cuatro uniones de tablas en SQL.

El operador = se puede utilizar para comparar no sólamente propiedades sino también instancias:

from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate
from Cat cat, Cat mate
where cat.mate = mate

The special property (lowercase) id can be used to reference the unique identifier of an object. See Sección 16.5, “Referencia a la propiedad identificadora ” for more information.

from Cat as cat where cat.id = 123

from Cat as cat where cat.mate.id = 69

La segunda consulta es eficiente y no se necesita una unión de tablas.

También se pueden utilizar las propiedades de identificadores compuestos. Considere el siguiente ejemplo en donde Person tiene identificadores compuestos que consisten de country y medicareNumber:

from bank.Person person
where person.id.country = 'AU'
    and person.id.medicareNumber = 123456
from bank.Account account
where account.owner.id.country = 'AU'
    and account.owner.id.medicareNumber = 123456

Una vez más, la segunda consulta no requiere una unión de tablas.

See Sección 16.5, “Referencia a la propiedad identificadora ” for more information regarding referencing identifier properties)

La propiedad especial class acccede al valor discriminador de una instancia en el caso de persistencia polimórfica. Un nombre de clase Java incluído en la cláusula where será traducido a su valor discriminador.

from Cat cat where cat.class = DomesticCat

You can also use components or composite user types, or properties of said component types. See Sección 16.17, “Componentes” for more information.

Un tipo "any" tiene las propiedades especiales id y class, permiténdole expresar una unión de la siguiente forma (en donde AuditLog.item es una propiedad mapeada con <any>).

from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id = payment.id

La log.item.class y payment.class harían referencia a los valores de columnas de la base de datos completamente diferentes en la consulta anterior.

Las expresiones utilizadas en la cláusula where incluyen lo siguiente:

in y between pueden utilizarse así:

from DomesticCat cat where cat.name between 'A' and 'B'
from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )

Las formas negadas se pueden escribir así:

from DomesticCat cat where cat.name not between 'A' and 'B'
from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )

De manera similar, is null y is not null se pueden utilizar para probar valores nulos.

Los valores booleanos se pueden utilizar fácilmente en expresiones declarando substituciones de consulta HQL en la configuración de Hibernate:

<property name="hibernate.query.substitutions"
>true 1, false 0</property
>

Esto remplazará las palabras clave true y false con los literales 1 y 0 en el SQL traducido de este HQL:

from Cat cat where cat.alive = true

Puede comprobar el tamaño de una colección con la propiedad especial size o la función especial size().

from Cat cat where cat.kittens.size 
> 0
from Cat cat where size(cat.kittens) 
> 0

Para las colecciones indexadas, puede referirse a los índices máximo y mínimo utilizando las funciones minindex y maxindex. De manera similar, se puede referir a los elementos máximo y mínimo de una colección de tipo básico utilizando las funciones minelement y maxelement. Por ejemplo:

from Calendar cal where maxelement(cal.holidays) 
> current_date
from Order order where maxindex(order.items) 
> 100
from Order order where minelement(order.items) 
> 10000

Las funciones SQL any, some, all, exists, in están soportadas cuando se les pasa el conjunto de elementos o índices de una colección (las funciones elements e indices) o el resultado de una subconsulta (vea a continuación):

select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)
select p from NameList list, Person p
where p.name = some elements(list.names)
from Cat cat where exists elements(cat.kittens)
from Player p where 3 
> all elements(p.scores)
from Show show where 'fizard' in indices(show.acts)

Note que estas construcciones - size, elements, indices, minindex, maxindex, minelement, maxelement - solo se pueden utilizar en la cláusula where en Hibernate3.

Los elementos de colecciones indexadas (arrays, listas, mapas) se pueden referir por índice sólamente en una cláusula where:

from Order order where order.items[0].id = 1234
select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
    and person.nationality.calendar = calendar
select item from Item item, Order order
where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
select item from Item item, Order order
where order.items[ maxindex(order.items) ] = item and order.id = 11

La expresión dentro de [] puede incluso ser una expresión aritmética:

select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item

HQL también proporciona la función incorporada index(), para los elementos de una asociación uno-a-muchos o una colección de valores.

select item, index(item) from Order order
    join order.items item
where index(item) < 5

Se pueden utilizar las funciones SQL escalares soportadas por la base de datos subyacente:

from DomesticCat cat where upper(cat.name) like 'FRI%'

Considere qué tan larga y menos leíble sería la siguiente consulta en SQL:

select cust
from Product prod,
    Store store
    inner join store.customers cust
where prod.name = 'widget'
    and store.location.name in ( 'Melbourne', 'Sydney' )
    and prod = all elements(cust.currentOrder.lineItems)

Ayuda: algo como

SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order
FROM customers cust,
    stores store,
    locations loc,
    store_customers sc,
    product prod
WHERE prod.name = 'widget'
    AND store.loc_id = loc.id
    AND loc.name IN ( 'Melbourne', 'Sydney' )
    AND sc.store_id = store.id
    AND sc.cust_id = cust.id
    AND prod.id = ALL(
        SELECT item.prod_id
        FROM line_items item, orders o
        WHERE item.order_id = o.id
            AND cust.current_order = o.id
    )

La lista retornada por una consulta se puede ordenar por cualquier propiedad de una clase retornada o componentes:

from DomesticCat cat
order by cat.name asc, cat.weight desc, cat.birthdate

Los asc o desc opcionales indican ordenamiento ascendente o descendente respectivamente.

Una consulta que retorna valores agregados se puede agrupar por cualquier propiedad de una clase retornada o componentes:

select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id

Se permite también una cláusula having.

select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)

Las funciones SQL y las funciones de agregación SQL están permitidas en las cláusulas having y order by, si están soportadas por la base de datos subyacente (por ejemplo, no lo están en MySQL).

select cat
from Cat cat
    join cat.kittens kitten
group by cat.id, cat.name, cat.other, cat.properties
having avg(kitten.weight) 
> 100
order by count(kitten) asc, sum(kitten.weight) desc

La cláusula group by ni la cláusula order by pueden contener expresiones aritméticas. Hibernate tampocoo expande una entidad agrupada así que no puede escribir group by cat si todas las propiedades de cat son no-agregadas. Tiene que enumerar todas la propiedades no-agregadas explícitamente.

Para bases de datos que soportan subconsultas, Hibernate soporta subconsultas dentro de consultas. Una subconsulta se debe encerrar entre paréntesis (frecuentemente por una llamada a una función de agregación SQL). Incluso se permiten subconsultas correlacionadas (subconsultas que se refieren a un alias en la consulta exterior).

from Cat as fatcat
where fatcat.weight 
> (
    select avg(cat.weight) from DomesticCat cat
)
from DomesticCat as cat
where cat.name = some (
    select name.nickName from Name as name
)
from Cat as cat
where not exists (
    from Cat as mate where mate.mate = cat
)
from DomesticCat as cat
where cat.name not in (
    select name.nickName from Name as name
)
select cat.id, (select max(kit.weight) from cat.kitten kit)
from Cat as cat

Note que las subconsultas HQL pueden ocurrir sólamente en las cláusulas select o where.

Note that subqueries can also utilize row value constructor syntax. See Sección 16.18, “Sintaxis del constructor de valores por fila” for more information.

Las consultas de Hibernate pueden ser bastante potentes y complejas. De hecho, el poder del lenguaje de consulta es uno de las fortalezas principales de Hibernate. He aquí algunos ejemplos de consultas muy similares a las consultas de proyectos recientes. Note que la mayoría de las consultas que escribirá son mucho más simples que los siguientes ejemplos.

La siguiente consulta retorna el order id, número de items y valor total mínimo dado y el valor de la orden para todas las órdenes no pagadas de un cliente en particular. Los resultados se ordenan de acuerdo al valor total. Al determinar los precios, usa el catálogo actual. La consulta SQL resultante, contra las tablas ORDER, ORDER_LINE, PRODUCT, CATALOG y PRICE tiene cuatro uniones interiores y una subselección (no correlacionada).

select order.id, sum(price.amount), count(item)
from Order as order
    join order.lineItems as item
    join item.product as product,
    Catalog as catalog
    join catalog.prices as price
where order.paid = false
    and order.customer = :customer
    and price.product = product
    and catalog.effectiveDate < sysdate
    and catalog.effectiveDate 
>= all (
        select cat.effectiveDate
        from Catalog as cat
        where cat.effectiveDate < sysdate
    )
group by order
having sum(price.amount) 
> :minAmount
order by sum(price.amount) desc

¡Qué monstruo! Realmente, en la vida real, no me gustan mucho las subconsultas, de modo que mi consulta fue realmente algo como esto:

select order.id, sum(price.amount), count(item)
from Order as order
    join order.lineItems as item
    join item.product as product,
    Catalog as catalog
    join catalog.prices as price
where order.paid = false
    and order.customer = :customer
    and price.product = product
    and catalog = :currentCatalog
group by order
having sum(price.amount) 
> :minAmount
order by sum(price.amount) desc

La próxima consulta cuenta el número de pagos en cada estado, excluyendo todos los pagos en el estado AWAITING_APPROVAL donde el cambio más reciente al estado lo hizo el usuario actual. Se traduce en una consulta SQL con dos uniones interiores y una subselección correlacionada contra las tablas PAYMENT, PAYMENT_STATUS y PAYMENT_STATUS_CHANGE.

select count(payment), status.name
from Payment as payment
    join payment.currentStatus as status
    join payment.statusChanges as statusChange
where payment.status.name <
> PaymentStatus.AWAITING_APPROVAL
    or (
        statusChange.timeStamp = (
            select max(change.timeStamp)
            from PaymentStatusChange change
            where change.payment = payment
        )
        and statusChange.user <
> :currentUser
    )
group by status.name, status.sortOrder
order by status.sortOrder

Si la colección statusChanges se mapeara como una lista, en vez de un conjunto, la consulta habría sido mucho más simple de escribir.

select count(payment), status.name
from Payment as payment
    join payment.currentStatus as status
where payment.status.name <
> PaymentStatus.AWAITING_APPROVAL
    or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <
> :currentUser
group by status.name, status.sortOrder
order by status.sortOrder

La próxima consulta utiliza la función isNull() de MS SQL Server para devolver todas las cuentas y pagos aún no cancelados de la organización a la que pertenece el usuario actual. Se traduce como una consulta SQL con tres uniones interiores, una unión exterior y una subselección contra las tablas ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION y ORG_USER.

select account, payment
from Account as account
    left outer join account.payments as payment
where :currentUser in elements(account.holder.users)
    and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate

Para algunas bases de datos, necesitaríamos eliminar la subselección (correlacionada).

select account, payment
from Account as account
    join account.holder.users as user
    left outer join account.payments as payment
where :currentUser = user
    and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID)
order by account.type.sortOrder, account.accountNumber, payment.dueDate

HQL now supports update, delete and insert ... select ... statements. See Sección 15.4, “Operaciones de estilo DML” for more information.

Puede contar el número de resultados de una consulta sin retornarlos:

( (Integer) session.createQuery("select count(*) from ....").iterate().next() ).intValue()

Para ordenar un resultado por el tamaño de una colección, utilice la siguiente consulta:

select usr.id, usr.name
from User as usr
    left join usr.messages as msg
group by usr.id, usr.name
order by count(msg)

Si su base de datos soporta subselecciones, puede colocar una condición sobre el tamaño de selección en la cláusula where de su consulta:

from User usr where size(usr.messages) 
>= 1

Si su base de datos no soporta subselecciones, utilice la siguiente consulta:

select usr.id, usr.name
from User usr
    join usr.messages msg
group by usr.id, usr.name
having count(msg) 
>= 1

Como esta solución no puede retornar un User con cero mensajes debido a la unión interior, la siguiente forma también es útil:

select usr.id, usr.name
from User as usr
    left join usr.messages as msg
group by usr.id, usr.name
having count(msg) = 0

Las propiedades de un JavaBean pueden ser ligadas a los parámetros de consulta con nombre:

Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size");

q.setProperties(fooBean); // fooBean has getName() and getSize()
List foos = q.list();

Las colecciones son paginables usando la interfaz Query con un filtro:

Query q = s.createFilter( collection, "" ); // the trivial filter

q.setMaxResults(PAGE_SIZE);
q.setFirstResult(PAGE_SIZE * pageNumber);
List page = q.list();

Los elementos de colección se pueden ordenar o agrupar usando un filtro de consulta:

Collection orderedCollection = s.filter( collection, "order by this.amount" );

Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );

Puede hallar el tamaño de una colección sin inicializarla:

( (Integer) session.createQuery("select count(*) from ....").iterate().next() ).intValue();

Los componentes se pueden utilizar de la misma manera en que se pueden utilizar los tipos de valores simples en consultas HQL. Pueden aparecer en la cláusula select así:

select p.name from Person p
select p.name.first from Person p

en donde el nombre de la Persona es un componente. Los componentes también se pueden utilizar en la cláusula where:

from Person p where p.name = :name
from Person p where p.name.first = :firstName

Los componentes también se pueden utilizar en la cláusula where:

from Person p order by p.name
from Person p order by p.name.first

Otro uso común de los componentes se encuentra en row value constructors.

HQL soporta la utilización de la sintaxis row value constructor de SQL ANSI que a veces se denomina sintaxis tuple, aunque puede que la base de datos subyacentes no soporte esa noción. Aquí estamos refiriéndonos generalmente a las comparaciones multivaluadas que se asocian típicamente con los componentes. Considere una entidad Persona, la cual define un componente de nombre:

from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt'

Esa es una sintaxis válida aunque un poco verbosa. Puede hacerlo un poco más conciso utilizando la sintaxis row value constructor:

from Person p where p.name=('John', 'Jingleheimer-Schmidt')

También puede ser útil especificar esto en la cláusula select:

select p.name from Person p

También puede ser beneficioso el utilizar la sintaxis row value constructor cuando se utilizan subconsultas que necesitan compararse con valores múltiples:

from Cat as cat
where not ( cat.name, cat.color ) in (
    select cat.name, cat.color from DomesticCat cat
)

Algo que se debe tomar en consideración al decidir si quiere usar esta sintaxis es que la consulta dependerá del orden de las sub-propiedades componentes en los metadatos.