Eu estou tentando expressar o seguinte SQL em JOOQ. No entanto, eu quer ter grandes problemas com tipos usando tabelas derivadas ou recebo algo que compila mas de qualquer falha a nível SQL ou mesmo em JAVA. Alguém pode me dar uma idéia de como usar tabelas derivadas corretamente neste contexto?
SELECT
id,
ROUND(num_realized / num_requirements, 2) AS realized_percent,
ROUND(num_requirements / max_req, 2) AS activity_percent
FROM (
SELECT
requirement.project_id AS id,
COUNT(requirement.id) AS num_requirements,
COUNT(requirement.realized) AS num_realized
FROM
requirement
GROUP BY
requirement.project_id) AS stats
CROSS JOIN (
SELECT
MAX(num_requirements) AS max_req
FROM (
SELECT
requirement.project_id AS id,
COUNT(requirement.id) AS num_requirements,
COUNT(requirement.realized) AS num_realized
FROM
requirement
GROUP BY
requirement.project_id) AS stats) AS req_max
A declaração funciona bem quando aplicado em SQL, mas não posso obter essa expressão em JOOQ.
Minha última tentativa foi usar
Table<Record3<Integer, Integer, Integer>> stats =
DSL.select(
REQUIREMENT.PROJECT_ID.as("id"),
DSL.count(REQUIREMENT.ID).as("num_requirements"),
DSL.count(REQUIREMENT.REALIZED).as("num_realized")
).from(REQUIREMENT).groupBy(REQUIREMENT.PROJECT_ID).asTable("stats");
Table<Record2<Integer, Integer>> req_max =
DSL.select(
stats.field(0),
DSL.min(stats.field(1))
)
.from(stats).asTable("req_max");
No entanto, eu estou recebendo erro: tipos incompatíveis:
Table<Record2<CAP#1,CAP#2>> cannot be converted to Table<Record2<Integer,Integer>>
Eu tentei um monte de diferentes técnicas, incluindo a definição do tipo de dados e usando .field (String, tipo de dados) em vez de usar "Registros", mas o que eu estou fazendo, sua ou não compilar ou falhar durante a execução em um erro desconhecido.
Eu ficaria feliz por qualquer ajuda.
Utilizar as funções da janela em alternativa
Em geral, a auto-junta deve ser evitado sempre que possível. Em muitos casos, funções da janela pode resolver um problema muito mais elegante do que agregações em consultas aninhadas. Se você estiver usando MySQL 8, sua consulta pode ser re-escrita como:
SELECT
requirement.project_id AS id,
ROUND(COUNT(requirement.realized) / COUNT(requirement.id), 2) AS realized_percent,
ROUND(COUNT(requirement.id) / MAX(COUNT(requirement.id)) OVER(), 2) AS activity_percent
FROM
requirement
GROUP BY
requirement.project_id
Observe a MAX(..) OVER()
função de janela, que pode agregar funções de agregação comuns como explicado aqui . Eu sei que você está usando o MySQL 5.7, que não tem apoio função de janela ainda, mas para a integralidade amor, esta resposta precisa de uma solução baseada em função de janela - talvez como uma motivação para atualizar :-)
Um monte de consultas jOOQ complexos podem ser mais simples, fazendo a consulta SQL subjacente mais simples em primeiro lugar.
O problema tabela derivada você correu para dentro
O problema é o uso de stats.field(0)
e stats.field(1)
. A assinatura do método é
Field<?> field(int index)
Não há nenhuma maneira jOOQ poderia fornecê-lo com segurança de tipo quando você acessar colunas de uma tabela pelo índice, portanto, o tipo de retorno é Field<?>
, onde o tipo de coluna é um wild card genérico. Existem algumas soluções aqui:
- Evitar o tipo de segurança se você não precisa dele. Você sempre pode declarar a sua mesa
Table<?> req_max
. A partir do seu exemplo só, eu não tenho certeza se você precisar de qualquer tipo de segurança aqui Extrair suas referências de campo como variáveis locais. Em vez de embutir por exemplo, a
id
coluna em suastats
mesa, por que não:Field<Integer> id = REQUIREMENT.PROJECT_ID.as("id"); Field<Integer> numRequirements = DSL.count(REQUIREMENT.ID).as("num_requirements"); Field<Integer> numRealized = DSL.count(REQUIREMENT.REALIZED).as("num_realized");
e então usá-lo como este:
var stats = DSL.select(id, numRequirements, numRealized) .from(REQUIREMENT) .groupBy(REQUIREMENT.PROJECT_ID) .asTable("stats"); var reqMax = DSL.select(stats.field(id), DSL.max(stats.field(numRequirements))) .from(stats) .asTable(reqMax);