Not a group expression: troubleshooting an Oracle query - sql

Oracle query
I have a column value with hardcoded value 'N/A' and other char values as well. I need to write a select query to get the min of this column grouping the other set of columns.. but the challenge is i need to replace the hard coded value of 'N/A' with another character 'Abc' along with min function
Option 1: nvl won't work as the value is hardcoded
Option 2: decode in the select statement along with min clause in the decode list, and group by clause with the other columns used in the select list
However, getting an error
ORA-00979 : not a group expression.
Example :
Select a, b, decode(z,'N/A','abc',min(z))
From table 1, table 2
Where table 1.p=table2.q
Group by a,b
Having c.table1 >= table2.d

You should be using DECODE inside the MIN function, not the other way around. But, I would probably just use a single CASE expression here:
SELECT
a,
b,
MIN(CASE WHEN z = 'N/A' THEN 'abc' ELSE z END) AS min_value
FROM table1 t1
INNER JOIN table2 t2
ON t1.p = t2.q
GROUP BY
a,
b;
The above CASE expression is just taking the minimum value of z for each group, with the only difference between MIN(z) being that should the value be N/A, it would be treated as abc.

Related

Conditional view in oracle

Below is the table from which I need to create conditionalize view.
and I am getting one flag from different table. So based on the flag values i.e. if flag=1 then I need to display actual column values from table, and if flag=0 then show all column values as null values.
I know we can handle it using CASE statement but here, in may case column count is very big so need to handle it in better way.
You could try using a CASE expression along with REGEXP_REPLACE to mask the value column:
SELECT ID,
CASE WHEN flag = 1 THEN Value ELSE REGEXP_REPLACE(Value, '.', '*') END AS Value
FROM yourTable;
You can use left join:
select t.*
from (select 1 as flag from dual) x left join
t
on x.flag = :flag;
If :flag = 1, then all columns will be shown. If :flag is anything else, then all values will be NULL.
If you actually want to show the ids, it is a little more complicated:
select i.*, t.*
from (select 1 as flag from dual) x left join
(select id, . . . -- columns you want to keep
from t
) i left join
t
on x.flag = :flag and t.id = i.id;
You will probably need to list out the columns with the NULL values.

COUNT inside CASE WHEN is causing Invalid Column error

select case
when COUNT(*)>0 THEN (select TOP 1 A.a1)
else 'none'
end
from A
where A.a1 > 10
order by A.a1
Code above is causing the following error:
Column 'A.a1' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
I could not understand why.
My intention is as follows: If there are rows where A.a1 is greater than 10, order them and take the top rows's a1 value. If there is no such row, select 'none'.
EDIT: It is a simplified version of the actual code that is going to be used in a subquery. So, I cannot use IF..ELSE statements.
You have an aggregation query because you have count(*). These are tricky with subqueries. Instead, you can use:
select (case when count(*) > 0 then A1.a1 else 'none'
end)
from A left join
(select top 1 A.a1) A1
on 1 = 1
where A1.a1 > 10
The logic of this is a bit non-sensical. I assume your actual query is more useful.
I suggest you try with the UNION ALL to solve your Issue. Below Code is for your ref.
DECLARE #T TABLE(ID INT,NAME VARCHAR(200))
INSERT INTO #T VALUES(11,'HAI')
INSERT INTO #T VALUES(12,'H')
SELECT * FROM #T
DECLARE #VAL INT=10
SELECT TOP 1 T.NAME
FROM #T T
WHERE T.ID>#VAL
UNION ALL
SELECT 'None'
WHERE NOT EXISTS (SELECT TOP 1 ID FROM #T WHERE ID>#VAL)
RE: Column 'A.a1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
COUNT(*) is an aggregation. SQL Server expects all columns to be in a GROUP BY.
RE: If there are rows where A.a1 is greater than 10, order them and take the top rows's a1 value. If there is no such row, select 'none'.
RE: EDIT: It is a simplified version of the actual code that is going to be used in a subquery. So, I cannot use IF..ELSE statements.
ORDER by a1 value and take TOP 1 == MAX(a1).
Assuming a1 is int orfloat, then the value must be converted to
varchar to handle the 'none' output. LOWER works and requires less typing than CAST or CONVERT.
Code:
SELECT ISNULL(LOWER(MAX(a1)), 'none') FROM A WHERE A.a1 > 10

Condition while aggregation in Spark

This question is related to conditional aggregation on SQLs. Normally we put conditions using 'case' statement in select clause but that case condition checks only the row under consideration. Consider the below data:
BEGIN TRANSACTION;
/* Create a table called NAMES */
CREATE TABLE NAMES(M CHAR, D CHAR, A INTEGER);
/* Create few records in this table */
INSERT INTO NAMES VALUES('M1','Y',2);
INSERT INTO NAMES VALUES('M1','Y',3);
INSERT INTO NAMES VALUES('M2','Y',2);
INSERT INTO NAMES VALUES('M2',null,3);
INSERT INTO NAMES VALUES('M3',null,2);
INSERT INTO NAMES VALUES('M3',null,3);
COMMIT;
This query groups using column 'M' and checks if column 'D' is null or not (separately for each record) and put a sum aggregation on column 'A'.
select sum(case when D = 'Y' then 0 else A end) from NAMES group by M;
Output for this query is:
M1|0
M2|3
M3|5
But if we want to check column 'D' for each record in the group if it is null. If any of the records is 'Y' in the group, do not perform 'sum' aggregation at all.
In brief, the expected output for the above scenario is:
M1|0
M2|0
M3|5
Answers in Spark SQL are highly appreciated.
You can use another case expression:
select (case when max(D) = min(D) and max(D) = 'Y' -- all the same
then sum(case when D = 'Y' then 0 else A end)
else 0
end)
from NAMES
group by M;

Filter if values provided otherwise return everything

Say I have a table t with 2 columns:
a int
b int
I can do a query such as:
select b
from t
where b > a
and a in(1,2,3)
order by b
where 1,2,3 is provided from the outside.
Obviously, the query can return no rows. In that case, I'd like to select everything as if the query did not have the and a in(1,2,3) part. That is, I'd like:
if exists (
select b
from t
where b > a
and a in(1,2,3)
)
select b
from t
where b > a
and a in(1,2,3)
order by b
else
select b
from t
where b > a
order by b
Is there a way to do this:
Without running two queries (one for exists, the other one the actual query)
That is less verbose than repeating queries (real queries are quite long, so DRY and all that stuff)
Using NOT EXISTS with a Sub Query to Determine if condition exists
SELECT b
FROM
t
WHERE
b > a
AND (
NOT EXISTS (SELECT 1 FROM #Table WHERE a IN (1,2,3))
OR a IN (1,2,3)
)
ORDER BY
b
The reason this works is because if the condition exists then the OR statement will include the rows and if the condition does not exist then the NOT EXISTS will include ALL rows.
Or With Common Table Expression and window Function with Conditional Aggregation.
WITH cte AS (
SELECT
b
,CASE WHEN a IN (1,2,3) THEN 1 ELSE 0 END as MeetsCondition
,COUNT(CASE WHEN a IN (1,2,3) THEN a END) OVER () as ConditionCount
FROM
t
)
SELECT
b
FROM
cte
WHERE
(ConditionCount > 0 AND MeetsCondition = 1)
OR (ConditionCount = 0)
ORDER BY
b
I find it a bit "ugly". Maybe it would be better to materialize output from your query within a temp table and then based on count from temp table perform first or second query (this limits accessing the original table from 3 times to 2 and you will be able to add some flag for qualifying rows for your condition not to repeat it). Other than that, read below . . .
Though, bear in mind that EXISTS query should execute pretty fast. It stops whether it finds any row that satisfies the condition.
You could achieve this using UNION ALL to combine resultset from constrained query and full query without constraint on a column and then decide what to show depending on output from first query using CASE statement.
How CASE statement works: when any row from constrained part of your query is found, return resultset from constrainted query else return everything omitting the constraint.
If your database supports using CTE use this solution:
with tmp_data as (
select *
from (
select 'constraint' as type, b
from t
where b > a
and a in (1,2,3) -- here goes your constraint
union all
select 'full query' as type, b
from t
where b > a
) foo
)
SELECT b
FROM tmp_data
WHERE
CASE WHEN (select count(*) from tmp_data where type = 'constraint') > 0
THEN type = 'constraint'
ELSE type = 'full query'
END
;

Error in sql query when using contains in aggregate expression

Query:
SELECT
c,
COUNT
( WHEN a='11' AND contains(b,'aa') THEN 1 ELSE NULL END
) as total
from x
group by c
Error:
Full-text predicates cannot appear in an aggregate expression. Place
the aggregate expression in a subquery.
I am using contains because, it uses the index when searching in text, also the query is like that only, so i have to put it in aggregate expression only.... please suggest..
Try this one -
SELECT total = COUNT(
CASE WHEN a = '11'
AND CONTAINS(b, 'aa') THEN 1
END)
FROM x
CONTAINS can appear only in a WHERE clause
SELECT
c,
COUNT(ContainsResult) AS total
FROM
(
SELECT
c, CASE WHEN a='11' THEN 1 ELSE NULL END AS ContainsResult
from
x
WHERE
contains(b,'aa')
UNION ALL
SELECT
c, NULL
from
x
WHERE
NOT contains(b,'aa')
) X1
GROUP BY
c