DEVLOGIN
DEVLOGIN

Reputation: 87

"not a single-group group function" with case expression

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

Answers (3)

Alex Poole
Alex Poole

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

btpys
btpys

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

Gordon Linoff
Gordon Linoff

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

Related Questions