views:

1140

answers:

4

I'm trying to get a percentage of the itemid that are available in a certain area. Using my query, I get an error ORA-00937: not a single-group group function

All the details:

I have these two tables:

ALLITEMS
---------------
ItemId  | Areas
---------------
1       | EAST
2       | EAST
3       | SOUTH
4       | WEST

CURRENTITEMS
---------------
ItemId
---------------
1
2
3

and want this result:

---------------
Areas| Percentage
---------------
EAST   | 50  --because ItemId 1 and 2 are in currentitems, so 2 items divided by the total 4 in allitems = .5
SOUTH  | 25   --because there is 1 item in currentitems table that are in area SOUTH (so 1/4=.25)
WEST   | 0  --because there are no items in currentitems that are in area WEST

The DDL:

drop table allitems;
drop table currentitems;

Create Table Allitems(ItemId Int,areas Varchar2(20));
Create Table Currentitems(ItemId Int);
Insert Into Allitems(Itemid,Areas) Values(1,'east');
Insert Into Allitems(ItemId,areas) Values(2,'east');
insert into allitems(ItemId,areas) values(3,'south');
insert into allitems(ItemId,areas) values(4,'east');

Insert Into Currentitems(ItemId) Values(1);
Insert Into Currentitems(ItemId) Values(2);
Insert Into Currentitems(ItemId) Values(3);

My Query:

Select  
areas,
(
Select 
Count(Currentitems.ItemId)*100 / (Select Count(ItemId) From allitems inner_allitems Where inner_allitems.areas = outer_allitems.areas )
From 
Allitems Inner_Allitems Left Join Currentitems On (Currentitems.Itemid = Inner_Allitems.Itemid) 
Where inner_allitems.areas = outer_allitems.areas
***group by inner_allitems.areas***
***it worked by adding the above group by***
) "Percentage Result"
From 
allitems outer_allitems
Group By 
areas

The error:

Error at Command Line:81 Column:41 (which is the part `(Select Count(ItemId) From allitems inner_allitems Where inner_allitems.areas = outer_allitems.areas )`)
Error report:
SQL Error: ORA-00937: not a single-group group function

When I run the exact same query in SQL Server, it works fine. How do I fix this in Oracle?

+1  A: 

Here is a quick first pass:

select ai.areas,
       (sum(cnt) / max(tot)) * 100 "Percentage Result"
  from (select ai.itemid,
               ai.areas,
               count(ci.itemid) cnt,
               count(ai.areas) over () tot
          from allitems ai
               left outer join
               currentitems ci on (ai.itemid = ci.itemid)
        group by ai.itemid, ai.areas
       ) ai
group by ai.areas

Also, in your test data your itemid 4 needs to be changed to west.

Dougman
+1  A: 

A slight modification of your original query :

Select  
areas,
(
Select 
Count(*)
From 
Allitems Inner_Allitems Left Join Currentitems On (Currentitems.Itemid = Inner_Allitems.Itemid) 
Where inner_allitems.areas = outer_allitems.areas
) *100 / (Select Count(*) From allitems ) as percentage
From allitems outer_allitems
Group By areas
Lluis Martinez
+4  A: 

Analytics are your friend:

SELECT DISTINCT
       areas
      ,COUNT(currentitems.itemid)
       OVER (PARTITION BY areas) * 100
       / COUNT(*) OVER () Percentage
FROM allitems, currentitems
WHERE allitems.itemid = currentitems.itemid(+);
Jeffrey Kemp
He mentions SQL Server and Oracle, perhaps the query must be portable, that is vendor agnostic.
Lluis Martinez
portability shmortability :)
Jeffrey Kemp
+1  A: 

Just for the heck of it, a way of doing it without analytics.

Jeffrey's solution needed a DISTINCT because of the duplication of areas. The allitems table is actually an intersection table between currentitems and a putative areas table. In the following query this is represented by the inline view ai. There is another inline view tot which gives us the total number number of records in allitems. This count has to be included in the GROUP BY clause, as it is not an aggregating projector.

SQL> select ai.areas
  2         , (count(currentitems.itemid)/tot.cnt) * 100 as "%"
  3  from
  4      ( select count(*) as cnt from allitems ) tot
  5      , ( select distinct areas as areas from allitems ) ai
  6      , currentitems
  7      , allitems
  8  where allitems.areas = ai.areas
  9  and   allitems.itemid = currentitems.itemid(+)
 10  group by ai.areas, tot.cnt
 11  /

AREAS                         %
-------------------- ----------
east                         50
south                        25
west                          0

SQL>

I don't know whether this approach would perform better than Jeffrey's solution: it quite possibly will perform worse (the analytics query certainly has fewer consistent gets). It is interesting simply because it highlights the issues more clearly.

APC
Nevertheless, thank you for your input. I didn't get the time today, but I will be testing all the great answers tomorrow!
Liao