J'essaie d'exprimer l'instruction SQL suivante dans JOOQ. Cependant, je soit d'énormes problèmes avec des types à l'aide de tables dérivées ou je reçois quelque chose qui compile, mais échoue soit au niveau SQL ou même en JAVA. Quelqu'un peut-il me donner une idée comment utiliser correctement les tables dérivées dans ce contexte?
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
L'énoncé fonctionne très bien lorsqu'elle est appliquée dans SQL mais je ne peux pas obtenir cette expression dans JOOQ.
Mon dernier essai utilisait
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");
Cependant, je reçois l'erreur: types incompatibles:
Table<Record2<CAP#1,CAP#2>> cannot be converted to Table<Record2<Integer,Integer>>
J'ai essayé un tas de différentes techniques, y compris la définition du type de données et en utilisant .field (String, type de données) au lieu d'utiliser « Records », mais tout ce que je fais, soit son pas compiler ou échoue lors de l'exécution d'une erreur inconnue.
Je serais heureux pour toute aide.
Utilisation des fonctions de fenêtre à la place
En général, Autojointures doit être évitée autant que vous le pouvez. Dans de nombreux cas, les fonctions de fenêtre peut résoudre un problème beaucoup plus élégante que agrégations dans les requêtes imbriquées. Si vous utilisez MySQL 8, votre requête peut être réécrite comme:
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
Notez la MAX(..) OVER()
fonction de fenêtre, qui peut regrouper les fonctions d'agrégation ordinaires comme expliqué ici . Je sais que vous utilisez MySQL 5.7, qui n'a pas de support de la fonction de fenêtre encore, mais pour être complet, cette réponse a besoin d' une solution basée sur la fonction de fenêtre - peut - être comme une motivation pour améliorer :-)
Beaucoup de requêtes complexes jOOQ peut être rendu plus simple en faisant la requête SQL sous-jacente plus simple en premier lieu.
Le problème de la table dérivée laquelle vous avez été
Le problème est votre utilisation stats.field(0)
et stats.field(1)
. La signature de la méthode est
Field<?> field(int index)
Il n'y a aucun moyen jOOQ pourrait vous fournir la sécurité de type lorsque vous accédez à des colonnes d'une table par index, d' où le type de retour est Field<?>
, où le type de colonne est une carte sauvage générique. Il y a quelques solutions ici:
- Évitez la sécurité de type si vous ne avez pas besoin. Vous pouvez toujours déclarer votre table
Table<?> req_max
. A partir de votre seul exemple, je ne sais pas si vous avez besoin d'ici la sécurité de type Extrait vos références sur le terrain en tant que variables locales. Au lieu d'incorporer par exemple la
id
colonne de votrestats
table, pourquoi ne pas: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");
puis l'utiliser comme ceci:
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);