Hibernate.orgCommunity Documentation

Capítulo 16. HQL: A Linguagem de Consultas do Hibernate

16.1. Diferenciação de maiúscula e minúscula
16.2. A cláusula from
16.3. Associações e uniões
16.4. Formas de sintáxe de uniões
16.5. Referência à propriedade do identificador
16.6. A cláusula select
16.7. Funções de agregação
16.8. Pesquisas Polimórficas
16.9. A cláusula where
16.10. Expressões
16.11. A cláusula ordenar por
16.12. A cláusula agrupar por
16.13. Subconsultas
16.14. Exemplos de HQL
16.15. Atualização e correção em lote
16.16. Dicas & Truques
16.17. Componentes
16.18. Sintáxe do construtor de valores de linha

O Hibernate vem com uma poderosa linguagem de consulta (HQL) que é muito parecida com o SQL. No entanto, comparado com o SQL o HQL é totalmente orientado à objetos, e compreende noções de herança, polimorfismo e associações.

As Consultas não diferenciam maiúscula de minúscula, exceto pelo nomes das classes e propriedades Java. Portanto, SeLeCT é o mesmo que sELEct que é o mesmo que SELECT, mas org.hibernate.eg.FOO não é org.hibernate.eg.Foo e foo.barSet não é foo.BARSET.

Esse manual usa as palavras chave HQL em letras minúsculas. Alguns usuários acreditam que com letras maiúsculas as consultas ficam mais legíveis, mas nós acreditamos que este formato não é apropriado para o código Java.

A consulta mais simples possível do Hibernate é a seguinte:

from eg.Cat

Isto simplesmente retornará todas as instâncias da classe eg.Cat. Geralmente não precisamos qualificar o nome da classe, uma vez que o auto-import é o padrão. Por exemplo:

from Cat

Com o objetivo de referir-se ao Cat em outras partes da consulta, você precisará determinar um alias. Por exemplo:

from Cat as cat

Essa consulta atribui um alias a cat para as instâncias de Cat, portanto poderemos usar esse alias mais tarde na consulta. A palavra chave as é opcional. Você também pode escrever assim:

from Cat cat

Classes múltiplas podem ser envolvidas, resultando em um produto cartesiano ou união "cruzada".

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

É considerada uma boa prática nomear alias de consulta, utilizando uma letra minúscula inicial, consistente com os padrões de nomeação Java para variáveis locais (ex.: domesticCat).

Podemos também atribuir aliases em uma entidade associada, ou mesmo em elementos de uma coleção de valores, usando uma join. Por exemplo:

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

Os tipos de uniões suportados foram inspirados no ANSI SQL:

As construções inteiro, união esquerda externa e união direita externa podem ser abreviadas.

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

Você pode fornecer condições extras de união usando a palavra chave do HQL with.

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 Seção 21.1, “Estratégias de Busca ” for more information.

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

Geralmente, uma união de busca não precisa atribuir um alias, pois o objeto associado não deve ser usado na cláusula where (ou em qualquer outra cláusula). Também, os objetos associados não são retornados diretamente nos resultados da consulta. Ao invés disso, eles devem ser acessados usando o objeto pai. A única razão pela qual precisariamos de um alias é quando fazemos uma união de busca recursivamente em uma coleção adicional:

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

Observe que a construção busca não deve ser usada em consultas invocadas usando iterate() (embora possa ser usado com scroll()). O Fetch também não deve ser usado junto com o setMaxResults() ou setFirstResult() pois essas operações são baseadas nas linhas retornadas, que normalmente contém duplicidade devido à busca das coleções, então o número de linhas pode não ser o que você espera. A Fetch não deve ser usada junto com uma condição with. É possível que seja criado um produto cartesiano pela busca de união em mais do que uma coleção em uma consulta, então tome cuidado nesses casos. Uma busca de união em várias coleções pode trazer resultados inesperados para mapeamentos do tipo bag, tome cuidado na hora de formular consultas como essas. Finalmente, observe o seguinte, a busca de união completa e busca de união direita não são importantes.

Se estiver usando o nível de propriedade busca lazy (com instrumentação de bytecode), é possível forçar o Hibernate a buscar as propriedades lazy imediatamente na primeira consulta, usando buscar todas as propriedades .

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

O HQL suporta duas formas de associação para união: implícita e explícita.

As consultas apresentadas na seção anterior usam a forma explícita, onde a palavra chave união é explicitamente usada na cláusula from. Essa é a forma recomendada.

A forma implícita não usa a palavra chave "união". Entretanto, as associações são "diferenciadas" usando pontuação ("." - dot-notation). Uniõesimplícitas podem aparecer em qualquer uma das cláusulas HQL. A união implícita resulta em declarações SQL que contém uniões inteiras.

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

Geralmente, existem duas formas para se referir à propriedade do indentificador de uma entidade:

As referências à composição das propriedades do identificador seguem as mesmas regras de nomeação. Se a entidade tiver uma propriedade de não identificador chamada id, a composição da propriedade do identificador pode somente ser referenciada pelo seu nome definido. Do contrário, uma propriedade especial id poderá ser usada para referenciar a propriedade do identificador.

A cláusula select seleciona quais objetos e propriedades retornam no resultado da consulta. Considere:

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

A consulta selecionará mates (parceiros), de outros Cats. Atualmente, podemos expressar a consulta de forma mais compacta como:

select cat.mate from Cat cat

As consultas podem retornar propriedades de qualquer tipo de valor, incluindo propriedades de tipo de componente:

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

As consultas podem retornar múltiplos objetos e/ou propriedades como uma matriz do tipo Object[]:

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

Ou como um 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

Ou - considerando que a classe Family tenha um construtor apropriado - como um objeto Java typesafe atual:

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

Pode-se designar alias à expressões selecionadas usando as:

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

Isto é bem mais útil quando usado junto comselecione novo mapa:

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

Esta consulta retorna um Mapa de referências para valores selecionados.

As consultas HQL podem retornar o resultado de funções agregadas nas propriedades:

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

As funções agregadas suportadas são:

Pode-se usar operadores aritméticos, concatenação e funções SQL reconhecidas na 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

As palavras distinct e all podem ser usadas e têm a mesma semântica que no SQL.

select distinct cat.name from Cat cat

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

A consulta:

from Cat as cat

retorna instâncias não só de Cat, mas também de subclasses como DomesticCat. As consultas do Hibernate podem nomear qualquer classe Java ou interface na cláusula from. A consulta retornará instâncias de todas as classes persistentes que extendam a determinada classe ou implemente a determinada interface. A consulta a seguir, poderia retornar todos os objetos persistentes:

from java.lang.Object o

A interface Named pode ser implementada por várias classes persistentes:

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

Note que as duas últimas consultas requerem mais de um SQL SELECT. Isto significa que a cláusula order by não ordena corretamente todo o resultado. Isso também significa que você não pode chamar essas consultas usando consulta.scroll().

A cláusula where permite estreitar a lista de instâncias retornadas. Se não houver referência alguma, pode-se referir à propriedades pelo nome:

from Cat where name='Fritz'

Se houver uma referência, use o nome da propriedade qualificada:

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

Isto retorna instâncias de Cat com nome ‘Fritz’.

A seguinte consulta:

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

retornará todas as instâncias de Foo, para cada um que tiver uma instância de bar com a propriedade date igual a propriedade startDate de Foo. Expressões de caminho compostas fazem da cláusula where, extremamente poderosa. Consideremos:

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

Esta consulta traduz para uma consulta SQL com uma tabela (inner) união. Por exemplo:

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

resultaria numa consulta que necessitasse de união de quatro tabelas, no SQL.

O operador = pode ser usado para comparar não apenas propriedades, mas também instâncias:

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 Seção 16.5, “Referência à propriedade do identificador ” for more information.

from Cat as cat where cat.id = 123

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

A segunda consulta é eficiente e não requer nenhuma união de tabelas.

As propriedades de identificadores compostas também podem ser usadas. Considere o seguinte exemplo onde Person possui identificadores compostos que consistem de country e 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

Mais uma vez, a segunda consulta não precisa de nenhuma união de tabela.

See Seção 16.5, “Referência à propriedade do identificador ” for more information regarding referencing identifier properties)

Da mesma forma, a propriedade especial class acessa o valor discriminador da instância, no caso de persistência polimórfica. O nome de uma classe Java inclusa em uma cláusula where, será traduzida para seu valor discriminante.

from Cat cat where cat.class = DomesticCat

You can also use components or composite user types, or properties of said component types. See Seção 16.17, “Componentes” for more information.

Um tipo "any" possui as propriedades id e class especiais, nos permitindo expressar uma união da seguinte forma (onde AuditLog.item é uma propriedade mapeada com<any>):

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

Veja que log.item.class e payment.class podem referir-se à valores de colunas de banco de dados completamente diferentes, na consulta acima.

As expressões permitidas na cláusula where incluem o seguinte:

in e between podem ser usadas da seguinte maneira:

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

As formas negativas podem ser escritas conforme segue abaixo:

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

Da mesma forma, is null e is not null podem ser usados para testar valores nulos.

Booleanos podem ser facilmente usados em expressões, declarando as substituições da consulta HQL, na configuração do Hibernate:

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

Isso irá substituir as palavras chave true e falsepelos literais 1 e 0 na tradução do HQL para SQL.

from Cat cat where cat.alive = true

Pode-se testar o tamanho de uma coleção com a propriedade especial size ou a função especial size().

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

Para coleções indexadas, você pode se referir aos índices máximo e mínimo, usando as funções minindex e maxindex. Igualmente, pode-se referir aos elementos máximo e mínimo de uma coleção de tipos básicos usando as funções minelement e maxelement. Por exemplo:

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

As funções SQL any, some, all, exists, in são suportadas quando passado o elemento ou o conjunto de índices de uma coleção (elements e índices de funções) ou o resultado de uma subconsulta (veja abaixo):

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 essas construções - tamanho, elementos, índices, minindex, maxindex, minelement, maxelement – só podem ser usados na cláusula where do Hibernate3.

Elementos de coleções com índice (matriz, listas, mapas) podem ser referenciadas pelo índice (apenas na 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

A expressão entre colchetes [] pode ser até uma expressão aritimética:

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

O HQL também provê a função interna index() para elementos de associação um-para-muitos ou coleção de valores.

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

Funções escalares SQL, suportadas pelo banco de dados subjacente podem ser usadas:

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

Se ainda não estiver totalmente convencido, pense o quão maior e menos legível poderia ser a consulta a seguir, em 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)

Hint: 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
    )

A lista retornada pela consulta pode ser ordenada por qualquer propriedade da classe ou componentes retornados:

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

As opções asc ou desc indicam ordem crescente ou decrescente, respectivamente.

Uma consulta que retorne valores agregados, podem ser agrupados por qualquer propriedade de uma classe ou componentes retornados:

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

Uma cláusula having também é permitida.

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)

Funções SQL e funções agregadas são permitidas nas cláusulas having e order by, se suportadas pelo banco de dados subjacentes (ex: não no MeuSQL).

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

Note que, nem a cláusula group by ou order by podem conter expressões aritméticas. O Hibernate também não expande atualmente uma entidade agrupada, portanto você não pode escrever group by cat caso todas as propriedades do cat não estiverem agregadas. Você precisa listar claramente todas as propriedades não-agregadas.

Para bancos de dados que suportam subseleções, o Hibernate suporta subconsultas dentro de consultas. Uma subconsulta precisa estar entre parênteses (normalmente uma chamada de função agregada SQL). Mesmo subconsultas co-relacionadas (subconsultas que fazem referência à alias de outras consultas), são aceitas.

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 HQL subconsultas podem aparecer apenas dentro de cláusulas select ou where.

Note that subqueries can also utilize row value constructor syntax. See Seção 16.18, “Sintáxe do construtor de valores de linha” for more information.

As consultas do Hibernate, podem ser muito poderosas e complexas. De fato, o poder da linguagem de consulta é um dos pontos principais na distribuição do Hibernate. Aqui temos algumas consultas de exemplo, muito similares a consultas usadas em um projeto recente. Note que a maioria das consultas que você irá escrever, são mais simples que estas.

A consulta a seguir retorna o id de ordenar, número de ítens e o valor total do ordenar para todos os ordenar não pagos para um cliente particular e valor total mínimo dado, ordenando os resultados por valor total. Para determinar os preços, utiliza-se o catálogo atual. A consulta SQL resultante, usando tabelas ORDER, ORDER_LINE, PRODUCT, CATALOG e PRICE, têm quatro uniões inteiras e uma subseleção (não 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

Que monstro! Na verdade, na vida real, eu não sou muito afeiçoado à subconsultas, então minha consulta seria mais parecida com isto:

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

A próxima consulta conta o número de pagamentos em cada status, excluindo todos os pagamentos no status AWAITING_APPROVAL, onde a mais recente mudança de status foi feita pelo usuário atual. Traduz-se para uma consulta SQL com duas uniões inteiras e uma subseleção correlacionada, nas tabelas PAYMENT, PAYMENT_STATUS e 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

Se eu tivesse mapeado a coleção statusChanges como um List, ao invés de um Set, a consulta teria sido muito mais simples de escrever.

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

A próxima consulta usa a função isNull() do Servidor MS SQL, para retornar todas as contas e pagamentos não efetuados para a organização, para aquele que o usuário atual pertencer. Traduz-se para uma consulta SQL com três uniões inteiras, uma união externa e uma subseleção nas tabelas ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION e 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 alguns bancos de dados, precisaremos eliminar a subseleção (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 Seção 15.4, “Operações no estilo DML” for more information.

Pode-se contar o número de resultados da consulta, sem realmente retorná-los:

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

Para ordenar um resultado pelo tamanho de uma coleção, use a consulta a seguir.

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)

Se seu banco de dados suporta subseleções, pode-se colocar uma condição sobre tamanho de seleção na cláusula where da sua consulta:

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

Se seu banco de dados não suporta subseleções, use a consulta a seguir:

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

Com essa solução não se pode retornar um User sem nenhuma menssagem, por causa da união inteira, a forma a seguir também é ú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

As propriedades de um JavaBean podem ser limitadas à parâmetros nomeados da consulta:

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();

As coleções são pagináveis, usando a interface Query com um filtro:

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

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

Os elementos da coleção podem ser ordenados ou agrupados usando um 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" );

Pode-se achar o tamanho de uma coleção sem inicializá-la:

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

Os componentes podem ser usados de quase todas as formas que os tipos de valores simples são usados nas consultas HQL. Eles podem aparecer na cláusula select:

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

onde a propriedade do nome da Person é um componente. Os componentes também podem ser utilizados na cláusula where:

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

Os componentes também podem ser usados na cláusula order by:

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

Outro uso comum dos componentes é nos row value constructors.

O HQL suporta o uso da sintáxe ANSI SQL row value constructor, algumas vezes chamado de sintáxe tupla, embora o banco de dados adjacente possa não suportar esta noção. Aqui nós geralmente nos referimos às comparações de valores múltiplos, tipicamente associada aos componentes. Considere uma entidade Person que define um componente de nome:

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

Esta é uma sintáxe válida, embora um pouco verbosa. Seria ótimo tornar essa sintáxe um pouco mais concisa e utilizar a sintáxe row value constructor:

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

Pode também ser útil especificar isto na cláusula select:

select p.name from Person p

Com o uso da sintáxe row value constructor, e que pode ser de benéfico, seria quando utilizar as subconsultas que precisem comparar com os valores múltiplos:

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

Ao decidir se você quer usar esta sintáxe ou não, deve-se considerar o fato de que a consulta será dependente da ordenação das sub-propriedades do componente no metadados.