Reputation: 87
I get error "ORA-00937: not a single-group group function" when I try to use a case expression:
SELECT CASE
WHEN EXTRACT_TABLE IS NOT NULL
THEN
'SELECT '||LISTAGG((EXTRACT_TABLE||'.'||EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " '),',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
ELSE 'SELECT '||LISTAGG((EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " '),',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
END
It works OK without the case expression, as:
SELECT
'SELECT '||LISTAGG((EXTRACT_TABLE||'.'||EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " '),',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
But I really need to check if EXTRACT_TABLE
is null or not. How can I fix this?
Thanks
Upvotes: 0
Views: 103
Reputation: 191265
The error means you need to include extract_table
in your group-by clause; or add a group-by if you don't have one already, which might involve identifying other columns, and could affect the output. It depends on the rest of your query and the data, and the result you expect.
Your original non-case approach leaves unwanted periods in the output. As an example, with a CTE for some sample data, your query gives:
-- CTE for sample data
with cte (extract_table, extract_colonne, extract_libelle, extract_ordre) as (
select null, 'col1', 'label1', 2 from dual
union all
select 'tab42', 'col2', 'label2', 3 from dual
union all
select 'tab42', 'col3', 'label3', 1 from dual
)
-- actual query
SELECT
'SELECT '||LISTAGG((EXTRACT_TABLE||'.'||EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " '),',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
from cte;
SELECT tab42.col3 AS " label3 " ,.col1 AS " label1 " ,tab42.col2 AS " label2 "
Your case-expression approach, with an added group by EXTRACT_TABLE
clause, would split that data into two results:
-- CTE for sample data
with cte (extract_table, extract_colonne, extract_libelle, extract_ordre) as (
select null, 'col1', 'label1', 2 from dual
union all
select 'tab42', 'col2', 'label2', 3 from dual
union all
select 'tab42', 'col3', 'label3', 1 from dual
)
-- actual query
SELECT
CASE
WHEN EXTRACT_TABLE IS NOT NULL THEN
'SELECT '||LISTAGG((EXTRACT_TABLE||'.'||EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " '),',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
ELSE
'SELECT '||LISTAGG((EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " '),',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
END
from cte
group by EXTRACT_TABLE;
SELECT tab42.col3 AS " label3 " ,tab42.col2 AS " label2 "
SELECT col1 AS " label1 "
which probably isn't what you want. However, if you actually have a filter and are sure that all selected rows either have or do not have a table name - and not a mix as above - then this would still work too.
Another approach is to just skip the table name, or more importantly the .
that separates the table and column names, further into the query; i.e. conditionally skip that period by replacing '.'
with CASE WHEN EXTRACT_TABLE IS NOT NULL THEN '.'
. When that column is null there will be no name part anyway, and that expression will suppress the period, so you only get the column name.
SELECT
'SELECT ' || LISTAGG((EXTRACT_TABLE
|| CASE WHEN EXTRACT_TABLE IS NOT NULL THEN '.' END
|| EXTRACT_COLONNE || ' AS " ' || EXTRACT_LIBELLE || ' " '), ',')
WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
With the same sample data that gets a single row, without the invalid leading period:
-- CTE for sample data
with cte (extract_table, extract_colonne, extract_libelle, extract_ordre) as (
select null, 'col1', 'label1', 2 from dual
union all
select 'tab42', 'col2', 'label2', 3 from dual
union all
select 'tab42', 'col3', 'label3', 1 from dual
)
-- actual query
SELECT
'SELECT ' || LISTAGG((EXTRACT_TABLE
|| CASE WHEN EXTRACT_TABLE IS NOT NULL THEN '.' END
|| EXTRACT_COLONNE || ' AS " ' || EXTRACT_LIBELLE || ' " '), ',')
WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
from cte;
SELECT tab42.col3 AS " label3 " ,col1 AS " label1 " ,tab42.col2 AS " label2 "
Upvotes: 0
Reputation: 366
Use CASE
inside LISTAGG
:
WITH sample AS (SELECT 1 AS EXTRACT_ORDRE, 'tab1' AS EXTRACT_TABLE, 'col1' AS EXTRACT_COLONNE, 'name1' AS EXTRACT_LIBELLE FROM DUAL
UNION ALL
SELECT 2, 'tab2', 'col2', 'name2' FROM DUAL
UNION ALL
SELECT 3, NULL, 'col3', 'name3' FROM DUAL
)
select 'SELECT '||LISTAGG(CASE WHEN EXTRACT_TABLE IS NOT NULL THEN (EXTRACT_TABLE||'.'||EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " ')
ELSE EXTRACT_COLONNE ||' AS " '|| EXTRACT_LIBELLE || ' " ' END ,',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE) AS result
from sample;
Output:
RESULT
-------------------
SELECT tab1.col1 AS " name1 " ,tab2.col2 AS " name2 " ,col3 AS " name3 "
Upvotes: 1
Reputation: 1269703
This is your query:
SELECT (CASE WHEN EXTRACT_TABLE IS NOT NULL
THEN <expression with LISTAGG()>
THEN <expression with LISTAGG()>
END)
FROM t;
Because of the LISTAGG()
, this is an aggregation query. However, there is no GROUP BY
. In such a case, all column references need to be arguments to aggregation functions.
I assume you mean one of these:
SELECT (CASE WHEN EXTRACT_TABLE IS NOT NULL
THEN <expression with LISTAGG()>
THEN <expression with LISTAGG()>
END)
FROM t
GROUP BY EXTRACT_TABLE;
Or:
SELECT <expression with LISTAGG()>
FROM t
WHERE EXTRACT_TABLE IS NOT NULL
UNION ALL
SELECT <expression with LISTAGG()>
FROM t
WHERE EXTRACT_TABLE IS NULL;
It is also possible that you intend conditional aggregation:
SELECT 'SELECT '|| LISTAGG((EXTRACT_TABLE || '.' || col_lib ||' AS " '|| EXTRACT_LIBELLE || ' " '), ',') WITHIN GROUP(ORDER BY EXTRACT_ORDRE)
FROM (SELECT (CASE WHEN EXTRACT_TABLE IS NOT NULL
THEN EXTRACT_COLONNE
ELSE EXTRACT_LIBELLE
END) as col_lib
t.*
FROM t
) t
Upvotes: 0