I'm having a problem with expressing the following query in jOOQ
SELECT s.season_name, l.league_name,
COUNT(CASE WHEN m.full_time_result = 'H' THEN 'H' END) AS home_wins,
COUNT(CASE WHEN m.full_time_result = 'D' THEN 'D' END) AS draws,
COUNT(CASE WHEN m.full_time_result = 'A' THEN 'A' END) AS away_wins,
COUNT(m.full_time_result) AS all_matches
FROM football_stats.matches AS m
JOIN football_stats.seasons AS s USING (season_id)
JOIN football_stats.leagues AS l USING (league_id) GROUP BY s.season_name, l.league_name
My java code for this is:
final Field<String> homeWins = DSL.decode().when(MATCHES.FULL_TIME_RESULT.eq(HOME_WIN), HOME_WIN).as("home_wins");
final Field<String> draws = DSL.decode().when(MATCHES.FULL_TIME_RESULT.eq(DRAW), DRAW).as("draws");
final Field<String> awayWins = DSL.decode().when(MATCHES.FULL_TIME_RESULT.eq(AWAY_WIN), AWAY_WIN).as("away_wins");
final Field<?>[] fields = {SEASONS.SEASON_NAME, LEAGUES.LEAGUE_NAME, DSL.count(homeWins), DSL.count(draws), DSL.count(awayWins), DSL.count()};
final SelectQuery<Record> query = dslContext.selectQuery();
query.addSelect(fields);
query.addFrom(MATCHES);
query.addJoinOnKey(SEASONS, JoinType.JOIN, Keys.MATCHES__FK_MATCHES_SEASONS);
query.addJoinOnKey(LEAGUES, JoinType.JOIN, Keys.MATCHES__FK_MATCHES_LEAGUES);
query.addGroupBy(SEASONS.SEASON_NAME,LEAGUES.LEAGUE_NAME);
final Record result = query.fetchOne();
And the query generated by jOOQ is:
select
"football_stats"."seasons"."season_name",
"football_stats"."leagues"."league_name",
count("home_wins"),
count("draws"),
count("away_wins"),
count(*)
from "football_stats"."matches"
join "football_stats"."seasons"
on "football_stats"."matches"."season_id" = "football_stats"."seasons"."season_id"
join "football_stats"."leagues"
on "football_stats"."matches"."league_id" = "football_stats"."leagues"."league_id"
group by
"football_stats"."seasons"."season_name",
"football_stats"."leagues"."league_name"
it results in
ERROR: column "home_wins" does not exist
However, when I omit .as("alias_name"), then jOOQ generates
select
"football_stats"."seasons"."season_name",
"football_stats"."leagues"."league_name",
count(case when "football_stats"."matches"."full_time_result" = 'H' then 'H' end),
count(case when "football_stats"."matches"."full_time_result" = 'D' then 'D' end),
count(case when "football_stats"."matches"."full_time_result" = 'A' then 'A' end),
count(*)
from "football_stats"."matches"
join "football_stats"."seasons"
on "football_stats"."matches"."season_id" = "football_stats"."seasons"."season_id"
join "football_stats"."leagues"
on "football_stats"."matches"."league_id" = "football_stats"."leagues"."league_id"
group by
"football_stats"."seasons"."season_name",
"football_stats"."leagues"."league_name"
How can I make jOOQ to generate it like count(case when "football_stats"."matches"."full_time_result" = 'A' then 'A' end) as 'away_wins'?
Also, can I make it generate USING instead of ON (addJoinUsing method also generates ON)?
You aliased the wrong expression
In SQL, you wrote:
COUNT(CASE WHEN m.full_time_result = 'H' THEN 'H' END) AS home_wins
With jOOQ, you wrote:
homeWins = decode().when(MATCHES.FULL_TIME_RESULT.eq(HOME_WIN), HOME_WIN).as("home_wins");
and then:
DSL.count(homeWins);
So, jOOQ generated exactly the SQL you wrote.
You intended, however, to write this:
homeWins = decode().when(MATCHES.FULL_TIME_RESULT.eq(HOME_WIN), HOME_WIN);
and then:
DSL.count(homeWins).as("home_wins");
Side-note:
In the upcoming jOOQ 3.6, you will be able to simplify your COUNT(CASE...) expression by using the SQL standard FILTER clause on aggregate functions:
DSL.count().filterWhere(MATCHES.FULL_TIME_RESULT.eq(HOME_WIN)).as("home_wins")
Related
How to concat results from column in Oracle SQL. same function as GROUP_CONCAT().
I have data like this
I want to get the result like this.
I already used listagg()
SELECT LISTAGG(S.MEASURE_WEIGHT, '/') WITHIN GROUP (ORDER BY S.Plan_id)
result are:
Without zeroes :(
can someone help me?
It seems like you want conditional aggregation:
select
measure_id,
max(case when plan_id = 1050 then measure_weight else 0 end)
|| '/'
|| max(case when plan_id = 1055 then measure_weight else 0 end) weight
from mytable
group by measure_id
Possibly this query using two subqueries may work:
SELECT
measure_id,
TO_CHAR(NVL((
SELECT weight FROM table t1
WHERE t1.measure_id = t.measure_id AND t1.plan_id = t.min_id AND t.min_id = 1050), 0))
|| '/' ||
TO_CHAR(NVL((
SELECT weight FROM table t2
WHERE t2.measure_id = t.measure_id AND t2.plan_id = t.max_id AND t.max_id = 1055), 0))
FROM (
SELECT measure_id, min(plan_id) min_id, max(plan_id) max_id
FROM table
GROUP BY measure_id
) t;
DECLARE #source as NVARCHAR(MAX) = (SELECT md.[source] FROM sports_service.meet.meet_detail md WHERE md.meet_id = #{meetId})
WHERE reg.is_waitlist = 0 AND reg.cancelled_dt IS NULL
AND NOT EXISTS (
SELECT 1 FROM sports_service.meet.meet_invitations i
WHERE i.meet_id = pmm.meet_id AND i.sports_person_id = reg.sports_person_id)
IF #source != 'MANUAL'
AND EXISTS (
SELECT 1
FROM sports_service.meet.session session
INNER JOIN sports_service.meet.event event ON event.session_id = session.id
WHERE pmm.meet_id = session.meet_id
AND sports_service.dbo.fnGetMeetAge(p.birth_dt, detail.age_up_date, detail.id_format) <![CDATA[>=]]> event.low_age
AND sports_service.dbo.fnGetMeetAge(p.birth_dt, detail.age_up_date, detail.id_format) <![CDATA[<=]]> event.high_age
AND (event.eligible_gender IS NULL OR event.eligible_gender = p.gender))
If you want to check some condition (AND EXISTS ... clause) only if some other condition holds (source is MANUAL) and you want to do that in one query instead of:
WHERE (...some condition ...) AND (.. some other condition ... )
IF #source != 'MANUAL'
AND EXISTS (
SELECT 1 ... )
which is not supported as it is not a correct sql syntax you can do:
WHERE (...some condition ...) AND (.. some other condition ... )
AND (
(
(SELECT md.[source]
FROM sports_service.meet.meet_detail md
WHERE md.meet_id = #{meetId}
) = 'MANUAL'
)
OR EXISTS (
SELECT 1
FROM ...
)
)
You need to check if the performance of the resulting query would be satisfactory.
It would be probably better to execute the query that gets the source first and then execute either query with the additional condition or without it depending on the source value. To do that you can generate the query in mybatis using dynamic SQL (including or excluding this additional condition).
As you can't use If inside expression, you can write your sql like this:
DECLARE #source as NVARCHAR(MAX) = (SELECT md.[source] FROM sports_service.meet.meet_detail md WHERE md.meet_id = #{meetId})
WHERE reg.is_waitlist = 0 AND reg.cancelled_dt IS NULL
AND NOT EXISTS (
SELECT
1
FROM
sports_service.meet.meet_invitations i
WHERE
i.meet_id = pmm.meet_id
AND i.sports_person_id = reg.sports_person_id)
AND ( #source <> 'MANUAL'
AND
EXISTS ( SELECT 1 FROM
sports_service.meet.session session
INNER JOIN sports_service.meet.event event ON
event.session_id = session.id
WHERE
pmm.meet_id = session.meet_id
AND sports_service.dbo.fnGetMeetAge(p.birth_dt,
detail.age_up_date,
detail.id_format) >= event.low_age
AND sports_service.dbo.fnGetMeetAge(p.birth_dt,
detail.age_up_date,
detail.id_format) <= event.high_age
AND (event.eligible_gender IS NULL
OR event.eligible_gender = p.gender)))
You can't use IF inside expression.
I have several SQL queries like this (about 10.000):
SELECT (COLUMN BODY)
FROM (TABLES BODY)
WHERE...
The where part is optional.
I need to extract the "COLUMN BODY" AND "TABLES BODY" from the query, but those are dynamic (I have found SQL functions and subqueries a lot)
Think as getting an vector with the divided query or something like that:
String result = [SELECT, "COLUMN BODY", FROM, "TABLES BODY", WHERE, ... ]
The purpose, I'm working in an "SQL translator" and I need extract those parts from that dynamic queries. I have tried string splitting and other but the result is so frustrating, so I'm looking for an alternative to achieve that. I'm working on a java project.
What is the best way to get that?
Edit
Those are examples of the queries:
SELECT COMPANIA, ANO, TIPO_COBRO, CODIGO, DESCRIPCION FROM SF_DESCRIPCIONES_ESTAND
SELECT AUT_FAMILIARES.COMPANIA, AUT_FAMILIARES.DCTO_EMPLEADO, AUT_FAMILIARES.SUCURSAL_EMPLEADO, AUT_FAMILIARES.IDENTIFICACION, AUT_FAMILIARES.SUCURSAL, AUT_FAMILIARES.CONSECUTIVO, AUT_FAMILIARES.DCTO_IDENTIDAD, AUT_FAMILIARES.PARENTESCO, AUT_FAMILIARES.NOMBRE, AUT_FAMILIARES.FECHANCTO, AUT_FAMILIARES.SEXO, AUT_FAMILIARES.OBSERVACIONES, AUT_FAMILIARES.POLIZA, AUT_FAMILIARES.SALUD, AUT_FAMILIARES.PORCENTAJE, AUT_FAMILIARES.OCUPACION, AUT_FAMILIARES.TELEFONO, AUT_FAMILIARES.POS, AUT_FAMILIARES.AUXILIO_EDUCATIVO, AUT_FAMILIARES.APELLIDO1, AUT_FAMILIARES.APELLIDO2, AUT_FAMILIARES.DIRECCION, AUT_FAMILIARES.ESTADO_ACTUAL, AUT_FAMILIARES.DCTO_IDENTIDAD_A, AUT_FAMILIARES.PARENTESCO_A, AUT_FAMILIARES.NOMBRE_A, AUT_FAMILIARES.SEXO_A, AUT_FAMILIARES.OBSERVACIONES_A, AUT_FAMILIARES.POLIZA_A, AUT_FAMILIARES.SALUD_A, AUT_FAMILIARES.PORCENTAJE_A, AUT_FAMILIARES.OCUPACION_A, AUT_FAMILIARES.TELEFONO_A, AUT_FAMILIARES.POS_A, AUT_FAMILIARES.AUXILIO_EDUCATIVO_A, AUT_FAMILIARES.APELLIDO1_A, AUT_FAMILIARES.APELLIDO2_A, AUT_FAMILIARES.DIRECCION_A, AUT_FAMILIARES.ESTADO_ACTUAL_A, AUT_FAMILIARES.ESTADO, AUT_FAMILIARES.DESTINATARIO, AUT_FAMILIARES.SUCURSAL_DESTINATARIO, AUT_FAMILIARES.TIPO_SOLICITUD, AUT_FAMILIARES.FECHA_APROBACION, AUT_FAMILIARES.ENVIADO, AUT_FAMILIARES.ACTUALIZADO, AUT_FAMILIARES.RUTA, AUT_FAMILIARES.CREATED_BY, AUT_FAMILIARES.DATE_CREATED, AUT_FAMILIARES.MODIFIED_BY, AUT_FAMILIARES.DATE_MODIFIED, AUT_FAMILIARES.N_IDENTIFICACION, AUT_FAMILIARES.EDAD, AUT_FAMILIARES.EDAD_A FROM AUT_FAMILIARES
SELECT DISTINCT CARGOS.ID_DE_CARGO, CARGOS.NOMBRE_DEL_CARGO FROM EV_EVALUADOR_EVALUADO EVAL INNER JOIN CARGOS ON EVAL.COMPANIA = CARGOS.COMPANIA AND EVAL.CARGO_EVALUADO = CARGOS.ID_DE_CARGO WHERE EVAL.COMPANIA = :COMPANIA AND EVAL.CLASE_EVALUACION = :CLASEEVALUACION AND CARGOS.ID_DE_CARGO >= :CODIGO ORDER BY CARGOS.ID_DE_CARGO
SELECT ROWNUM CODIGO, DECODE (ROWNUM, 1,'Activo',2,'Pensionado',3,'Retirado',4,'Suspensión Preventiva',5,'Activarlo para Ajustes Liquidación',6,'Comisión',7,'Encargos')NOMBRE FROM DUAL CONNECT BY ROWNUM < 8
SELECT * FROM ( SELECT A.*, ROWNUM RNUM FROM(SELECT * FROM (SELECT INVENTARIO.CODIGOELEMENTO, INVENTARIO.NOMBRELARGO FROM INVENTARIO WHERE INVENTARIO.COMPANIA = :COMPANIA AND INVENTARIO.TIPO NOT IN ('C') ORDER BY INVENTARIO.COMPANIA, INVENTARIO.CODIGOELEMENTO) WHERE (CASE WHEN CODIGOELEMENTO IS NULL THEN ' ' ELSE UPPER(CODIGOELEMENTO) END) LIKE UPPER(:CODIGOELEMENTO || '%') AND (CASE WHEN NOMBRELARGO IS NULL THEN ' ' ELSE UPPER(NOMBRELARGO) END) LIKE UPPER( '%' || :NOMBRELARGO || '%') ) A WHERE ROWNUM <= (:PAGINICIO + :PAGTAMANIO)) WHERE RNUM > :PAGINICIO
I'm using SQL Case in my select and in group by clause and I'm working in JAVA. Whenever I execute my java program it says:
Column 'dbo.JOHN_Dashboard.Log_Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My Query is:
SELECT EP.Site_Code AS [Site_Code], DB.[Site] AS [Site], DB.[Utility] AS [Utility],
CASE ? WHEN 'Raw' THEN dateadd(mi,datediff(mi,0,DB.[log_date]),0)
WHEN 'Hour' THEN dateadd(hh,datediff(hh,0,DB.[log_date]),0)
WHEN 'Day' THEN dateadd(dd,datediff(dd,0,DB.[log_date]),0)
WHEN 'Week' THEN dateadd(wk,datediff(wk,0,DB.[log_date]),0)
WHEN 'Month' THEN dateadd(mm,datediff(mm,0,DB.[log_date]),0)
WHEN 'Year' THEN dateadd(yy,datediff(yy,0,DB.[log_date]),0)
ELSE DB.[log_date]
END AS [log_date],
SUM(CASE WHEN DB.[value] >= 0 THEN DB.[value] ELSE 0 END) AS [value],
SUM(CASE WHEN DB.[Cost] >=0 THEN DB.[cost] ELSE 0 END) AS [Cost],
SUM(CASE WHEN DB.[CO2] >=0 THEN DB.[CO2] ELSE 0 END) AS [CO],
MT.[Meter_type_name] AS [Meter Type],
MN.[Meter_Name] AS [Meter Name],
U.[Unit_Name] AS [Units],
EP.EnergyPoint_ID AS [Meter_ID],
EP.Parent_ID AS [Parent],
EP.Meter_Description AS [Meter_Description]
FROM [dbo].[JOHN_Dashboard] DB
INNER JOIN [dbo].[EnergyPoints] EP ON DB.[EnergyPoint_ID] = EP.[EnergyPoint_ID]
INNER JOIN [dbo].[Meter_Types] MT ON MT.[Meter_Type_ID] = EP.[Meter_Type_ID]
INNER JOIN [dbo].[Meter_Names] MN ON MN.[Meter_Name_ID] = EP.[Meter_Name_ID]
INNER JOIN [dbo].[Units] U ON U.[Unit_ID] = EP.[Unit_id]
WHERE [log_date] >= ? AND [Log_Date] < DATEADD(DAY, 1, ?)
AND ( ? IS NULL OR EP.Energypoint_ID = ?)
GROUP BY EP.Site_Code, DB.[Site], DB.[Utility], MT.[Meter_type_name],
MN.[Meter_Name], U.[Unit_Name], EP.[EnergyPoint_ID],
EP.[Parent_ID], EP.[Meter_Description],
CASE ? WHEN 'Raw' THEN dateadd(mi,datediff(mi,0,DB.[log_date]),0)
WHEN 'Hour' THEN dateadd(hh,datediff(hh,0,DB.[log_date]),0)
WHEN 'Day' THEN dateadd(dd,datediff(dd,0,DB.[log_date]),0)
WHEN 'Week' THEN dateadd(wk,datediff(wk,0,DB.[log_date]),0)
WHEN 'Month' THEN dateadd(mm,datediff(mm,0,DB.[log_date]),0)
WHEN 'Year' THEN dateadd(yy,datediff(yy,0,DB.[log_date]),0)
ELSE DB.[log_date] END ;
The parameters i'm passing are:
'Week'
'2016-05-16'
'2016-05-22'
6044
6044
'Week'
Note: This query runs without error in SQL Management Studio.
As requested here is a reworked version of your code using a sub-query before grouping. Since I don't have your database I can't guarantee that I have everything exactly right but give this a try.
I recommend always using a sub-query when your group by has complicated logic that will be repeated in the select. Some people would probably drop the second criteria and just say whenever the group by has complicated logic.
SELECT sub.Site_Code, sub.[Site], sub.[Utility], sub.[Meter Type],
sub.[log_date],
SUM(sub.[value]) as [value],
SUM(sub.[Cost]) as [cost],
SUM(sub.[CO]) as [CO],
sub.[Meter Name], sub.[Units], sub.[Meter_ID],
sub.[Parent], sub.[Meter_Description]
FROM (
SELECT EP.Site_Code AS [Site_Code], DB.[Site] AS [Site], DB.[Utility] AS [Utility],
CASE ? WHEN 'Raw' THEN dateadd(mi,datediff(mi,0,DB.[log_date]),0)
WHEN 'Hour' THEN dateadd(hh,datediff(hh,0,DB.[log_date]),0)
WHEN 'Day' THEN dateadd(dd,datediff(dd,0,DB.[log_date]),0)
WHEN 'Week' THEN dateadd(wk,datediff(wk,0,DB.[log_date]),0)
WHEN 'Month' THEN dateadd(mm,datediff(mm,0,DB.[log_date]),0)
WHEN 'Year' THEN dateadd(yy,datediff(yy,0,DB.[log_date]),0)
ELSE DB.[log_date]
END AS [log_date],
CASE WHEN DB.[value] >= 0 THEN DB.[value] ELSE 0 END AS [value],
CASE WHEN DB.[Cost] >=0 THEN DB.[cost] ELSE 0 END AS [Cost],
CASE WHEN DB.[CO2] >=0 THEN DB.[CO2] ELSE 0 END AS [CO],
MT.[Meter_type_name] AS [Meter Type],
MN.[Meter_Name] AS [Meter Name],
U.[Unit_Name] AS [Units],
EP.EnergyPoint_ID AS [Meter_ID],
EP.Parent_ID AS [Parent],
EP.Meter_Description AS [Meter_Description]
FROM [dbo].[JOHN_Dashboard] DB
INNER JOIN [dbo].[EnergyPoints] EP ON DB.[EnergyPoint_ID] = EP.[EnergyPoint_ID]
INNER JOIN [dbo].[Meter_Types] MT ON MT.[Meter_Type_ID] = EP.[Meter_Type_ID]
INNER JOIN [dbo].[Meter_Names] MN ON MN.[Meter_Name_ID] = EP.[Meter_Name_ID]
INNER JOIN [dbo].[Units] U ON U.[Unit_ID] = EP.[Unit_id]
WHERE [log_date] >= ? AND [Log_Date] < DATEADD(DAY, 1, ?)
AND ( ? IS NULL OR EP.Energypoint_ID = ?)
) sub
GROUP BY sub.Site_Code, sub.[Site], sub.[Utility], sub.[Meter Type],
sub.[Meter Name], sub.[Units], sub.[Meter_ID],
sub.[Parent], sub.[Meter_Description], sub.[log_date];
I wrote this mysql query and tried to convert it to JOOQ query but not succeeded , this is mysql query
SELECT `P`.`phone_number`, `A`.`emp_no`,
SUM(CASE WHEN VCG.vid IN (
SELECT gv.vid FROM `grvas` gv JOIN gprs gr ON gr.id=gv.grid
WHERE gr.id=G.id AND gv.stdate < '2011-08-15' AND gv.enddate > '2011-09-14')
THEN VCG.amount END) AS allow,
... etc...
How can i convert this query to JOOQ query ?
Thanks,
This:
SUM(
CASE
WHEN VCG.vid IN (
SELECT gv.vid
FROM `grvas` gv
JOIN gprs gr ON gr.id=gv.grid
WHERE gr.id=G.id
AND gv.stdate < '2011-08-15'
AND gv.enddate > '2011-09-14'
)
THEN VCG.amount
END
) AS allow
Would translate to something like this:
import static org.jooq.impl.DSL.*;
// And then
sum(
decode()
.when(VCG.VID.in(
select(GRVAS.VID)
.from(GRVAS)
.join(GPRS).on(GPRS.ID.eq(GRVAS.GRID))
.where(GPRS.ID.eq(G.ID))
.and(GRVAS.STDATE.lt(Date.valueOf("2011-08-15")))
.and(GRVAS.ENDDATE.gt(Date.valueOf("2011-09-14")))
)), VCG.AMOUNT)
).as("allow")
The above example omitted table aliasing in the subquery of the IN predicate, to simplify things. Of course, you could go on and alias all those tables as well.
I couldn't find a proper way to convert above sql to JOOQ but i used
String sql="SELECT `P`.`phone_number`, `A`.`emp_no`,
SUM(CASE WHEN VCG.vid IN (
SELECT gv.vid FROM `grvas` gv JOIN gprs gr ON gr.id=gv.grid
WHERE gr.id=G.id AND gv.stdate < '2011-08-15' AND gv.enddate > '2011-09-14')
THEN VCG.amount END) AS allow,
... etc... " ;
Result<Record> res = f.fetch(sql);
This work fine
Thanks