not a single-group group function (00937. 00000) - sql

SELECT p.pdept.dno,
MAX(SUM(p.budget)) AS max
FROM Proj111 p
GROUP BY p.pdept.dno
Although I have included all the necessary attributes in select statements to group by clause it will generate above issue? How to solve it?

You can't nest aggregate functions like that. If you want to get the maximum sum from each group, then you can try this:
WITH cte AS (
SELECT p.pdept.dno AS dept,
SUM(p.budget) AS budget
FROM Proj111 p
GROUP BY p.pdept.dno
)
SELECT t.dept,
t.budget
FROM cte t
WHERE t.budget = (SELECT MAX(budget) FROM cte)
The common table expression which I have named cte finds the budgets for each department. The query then restricts this result to the department with the maximum budget by again querying the cte for the maximum budget.

Related

Why does adding GROUP BY cause a seemingly unrelated error?

The following code works fine:
SELECT name, (SELECT count(item_id) FROM bids WHERE item_id = items.id)
FROM items;
However, when I add
SELECT name, (SELECT count(item_id) FROM bids WHERE item_id = items.id)
FROM items
GROUP BY name;
I get ERROR: subquery uses ungrouped column "items.id" from outer query
Can anyone tell me why this is happening? Thanks!
If you GROUP BY name then any other columns you select from items must have an aggregate function applied. That's what GROUP BY means.
In your case, you are using another column from items -- id -- in a correlated scalar subquery. That's not an aggregate function, and id is not in the GROUP BY clause, so you get an error.
You could instead GROUP BY name, id. That should give you the same results as the first query, and is probably pointless.
If you actually have multiple rows in items with the same value for name, and you want to group the results of the scalar subquery for those values, you need to specify how to group them. Perhaps you want the total of the subquery results for each value of name. If so, I think you could do:
SELECT name, SUM(SELECT count(item_id) FROM bids WHERE item_id = items.id))
FROM items
GROUP BY name;
(I'm not positive about the specific syntax as I don't have a Postgres instance to test against.)
A clearer way to express it might be:
SELECT name, SUM(bid_count)
FROM (
SELECT name, (SELECT count(item_id) FROM bids WHERE item_id = items.id) AS bid_count
FROM items
)
GROUP BY name
Join the tables then perform the GROUP BY:
select i.name, count(b.item_id)
from items i
inner join bids b
on b.item_id = i.id
group by i.name
db<>fiddle here

Use SUM and Average both on the same column in Group By statement

I have a table as shown in this screenshot:
I want to group by the Employees to calculate AssetEarned, but with the following logic:
Sum the same dates, then
Average the different dates
The output expected is like the following picture.
Can this be achieved using a group by? I can use SUM, or AVG in the group by statement individually, but not at the same time. How to achieve this?
You can achieve this by using simple the SUM and Count functions in SQL
SELECT ID,NAME,(SUM(ASSET_EARNED)/CAST(COUNT(DISTINCT DATE) AS FLOAT)) AS ASSET_EARNED
FROM (
VALUES (1,1,'Bruce',5,'10-Jan'),(2,1,'Bruce',7,'10-Jan'),(3,1,'Bruce',6,'12-Jan'),
(4,2,'Clark',4,'11-Jan'),(5,2,'Clark',3,'12-Jan')
)S(KeyID,ID,NAME,ASSET_EARNED,DATE)
GROUP BY ID,NAME
Another option to avoid the DISTINCT inside Count function
SELECT A.ID,A.NAME,SUM(A.ASSET_EARNED)/CAST(COUNT(DATE) AS FLOAT) AS ASSET_EARNED
FROM(
SELECT ID,NAME,DATE,(SUM(ASSET_EARNED)) AS ASSET_EARNED
FROM (
VALUES (1,1,'Bruce',5,'10-Jan'),(2,1,'Bruce',7,'10-Jan'),(3,1,'Bruce',6,'12-Jan'),
(4,2,'Clark',4,'11-Jan'),(5,2,'Clark',3,'12-Jan')
)S(KeyID,ID,NAME,ASSET_EARNED,DATE)
GROUP BY ID,NAME,DATE) A
GROUP BY ID,NAME
You could use a subquery in which you SUM the values first before using AVG:
SELECT t.EmployeeID,
t.EmployeeName,
AVG(t.AssetEarned) AssetEarned
FROM
(
SELECT t.EmployeeID,
t.EmployeeName,
SUM(CONVERT(FLOAT, t.AssetEarned)) AssetEarned
FROM yourTable t
GROUP BY t.EmployeeID, t.EmployeeName, t.[Date]
) t
GROUP BY t.EmployeeID, t.EmployeeName
ORDER BY t.EmployeeID
You just need to use Count of Unique Dates and divide sum of asset into them.
SELECT
e.EmployeeId,e.EmployeeName,cast(SUM(e.AssetEarned) AS FLOAT)/Cast(COUNT(DISTINCT e.Date) AS FLOAT) AssetEarned
FROM Employee AS e
GROUP BY e.EmployeeId,e.EmployeeName

SQL: Finding the Minimum aggregate using only count

I'm using DB2 for a project and looking to find which Group has the fewest members without using the min feature. My idea is to find all the groups and then subtract out any group which has more members from some other group thus leaving me with the group with that has no more members than any other group, i.e. the min.
So far I have
SELECT DISTINCT P.group as Group, count(P.id) as Count
FROM People P
EXCEPT
SELECT P.group, count(P.id)
FROM People P, People O
WHERE count(P.cid) > count(O.cid);
With a schema for People like
create table People (
group varchar(25) not null,
id smallint not null,
);
I am getting the following error:
SQL0119N An expression starting with "CLUB" specified in a SELECT clause,
HAVING clause, or ORDER BY clause is not specified in the GROUP BY clause or
it is in a SELECT clause, HAVING clause, or ORDER BY clause with a column
function and no GROUP BY clause is specified. SQLSTATE=42803
If you could help point out what I am doing wrong or the correct format for such a query it would be greatly appreciated!
find which Group has the fewest members
You can aggregate by group, order by member count, and fetch the top row only:
select p.group as grp, count(*) as cnt
from people p
group by p.group
order by count(*)
fetch first 1 rows only
You should try to use min(). Very straight forward. From your Error message, it seems like your HAVING clause is wrong so it would look into that.

Is there any optimal way to find the count of rows

I wrote SQL query in which I have one inner query and one outer query, My outer query produces the result on behalf of inner query, now I need to find the no of rows returning by my outer query, so what I did, I enclosed it inside another select statement and use count() function which produces the result, but i need to know more precise way to calculate the row count, please see my below query and suggest me the best way to do the same.
SELECT count(*) FROM (
SELECT
COUNT(*) NO_OF_EMP
,SUM(tbl.AMOUNT) TOTAL_AMOUNT
,tbl.YYYYMM
,tbl.DATA_PICKED_BY_NAME
,MIN(DATA_PICKED_DATE) DATA_PICKED_DATE
,ROW_NUMBER() OVER (ORDER BY tbl.REFERENCE_ID) AS ROW_NUM
FROM (
SELECT
SALARY_REPORT_ID
,EMP_NAME
,EMP_CODE
,PAY_CODE
,PAY_CODE_NAME
,AMOUNT
,PAY_MODE
,PAY_CODE_DESC
,YYYYMM
,REMARK
,EMP_ID
,PRAN_NUMBER
,PF_NUMBER
,PRAN_NO
,ATTOFF_EMPCODE
,DATA_PICKED_DATE
,DATA_PICKED_BY
,DATA_PICKED_BY_NAME
,SUBSTR(REFERENCE_ID,0,3) REFERENCE_ID
FROM SALARY_DETAIL_REPORT_HISTORY
WHERE PAY_CODE=999
AND REFERENCE_ID LIKE '202%'
) tbl
GROUP BY tbl.REFERENCE_ID,tbl.YYYYMM,tbl.DATA_PICKED_BY_NAME
order by tbl.YYYYMM
)mytbl1
Select count distinct of the most abbreviated version of a single value of your group values from your original query:
SELECT count(distinct SUBSTR(REFERENCE_ID,0,3) || YYYYMM || DATA_PICKED_BY_NAME)
FROM SALARY_DETAIL_REPORT_HISTORY
WHERE PAY_CODE=999
AND REFERENCE_ID LIKE '202%'

Aggregate function in SQL WHERE-Clause

In a test at university there was a question; is it possible to use an aggregate function in the SQL WHERE clause.
I always thought this isn't possible and I also can't find any example how it would be possible. But my answer was marked false and now I want to know in which cases it is possible to use an aggregate function in the WHERE. Also if it isn't possible it would be nice to get a link to the specification where it is described.
HAVING is like WHERE with aggregate functions, or you could use a subquery.
select EmployeeId, sum(amount)
from Sales
group by Employee
having sum(amount) > 20000
Or
select EmployeeId, sum(amount)
from Sales
group by Employee
where EmployeeId in (
select max(EmployeeId) from Employees)
You haven't mentioned the DBMS. Assuming you are using MS SQL-Server, I've found a T-SQL Error message that is self-explanatory:
"An aggregate may not appear in the
WHERE clause unless it is in a
subquery contained in a HAVING clause
or a select list, and the column being
aggregated is an outer reference"
http://www.sql-server-performance.com/
And an example that it is possible in a subquery.
Show all customers and smallest order for those who have 5 or more orders (and NULL for others):
SELECT a.lastname
, a.firstname
, ( SELECT MIN( o.amount )
FROM orders o
WHERE a.customerid = o.customerid
AND COUNT( a.customerid ) >= 5
)
AS smallestOrderAmount
FROM account a
GROUP BY a.customerid
, a.lastname
, a.firstname ;
UPDATE.
The above runs in both SQL-Server and MySQL but it doesn't return the result I expected. The next one is more close. I guess it has to do with that the field customerid, GROUPed BY and used in the query-subquery join is in the first case PRIMARY KEY of the outer table and in the second case it's not.
Show all customer ids and number of orders for those who have 5 or more orders (and NULL for others):
SELECT o.customerid
, ( SELECT COUNT( o.customerid )
FROM account a
WHERE a.customerid = o.customerid
AND COUNT( o.customerid ) >= 5
)
AS cnt
FROM orders o
GROUP BY o.customerid ;
You can't use an aggregate directly in a WHERE clause; that's what HAVING clauses are for.
You can use a sub-query which contains an aggregate in the WHERE clause.
UPDATED query:
select id from t where id < (select max(id) from t);
It'll select all but the last row from the table t.
SELECT COUNT( * )
FROM agents
HAVING COUNT(*)>3;
See more below link:
http://www.w3resource.com/sql/aggregate-functions/count-having.php#sthash.90csRM4I.dpuf]
http://www.w3resource.com/sql/aggregate-functions/count-having.php
Another solution is to Move the aggregate fuction to Scalar User Defined Function
Create Your Function:
CREATE FUNCTION getTotalSalesByProduct(#ProductName VARCHAR(500))
RETURNS INT
AS
BEGIN
DECLARE #TotalAmount INT
SET #TotalAmount = (select SUM(SaleAmount) FROM Sales where Product=#ProductName)
RETURN #TotalAmount
END
Use Function in Where Clause
SELECT ProductName, SUM(SaleAmount) AS TotalSales
FROM Sales
WHERE dbo.getTotalSalesByProduct(ProductName) > 1000
GROUP BY Product
References:
1.
2.
Hope helps someone.
If you are using an aggregate function in a where clause then it means you want to filter data on the basis of that aggregation function. In my case, it's SUM(). I'll jump to the solution.
(select * from(select sum(appqty)summ,oprcod from pckwrk_view group by oprcod)AS asd where summ>500)
The inner query is used to fetch results that need to be filtered.
The aggregate function which has to filter out must be given an ALIAS name because the actual name of the column inside an aggregate function is not accessible or recognized by the outer query.
Finally, the filter can be applied to the aliased name of the column in the inner query
Try this one
select SUM(RecQty) RecQty,ItemCode from
CostLedger group by ItemCode
having sum(RecQty) > 2000