Perform SUM, and CASE WHEN - sql

I'm using SQL Server 2008.
I get this error:
is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I would like to:
Select all columns from table b
When there is the same numberID I would like to perform SUM and return one row with result
Also, if there is just one numberID I would like to return result
I would like to create one more column Case with the following output “Percentage is…” (value from column Percentage)
Code so far:
select distinct
b.*,
case
when b.NumberID = b.NumberID then SUM(d.Percentage)
end as Percentage,
NULL as LName,
NULL as FName,
'Percentage is' + convert(VARCHAR(20),
CASE WHEN b. NumberID = b.NumberID
THEN SUM(d.Percentage) END) as Case
from
Accounting b
join (
select b.*
from Accounting b
join (
select number
, MAX(id) id
from Accounting
where Date < '2012-12-01'
group by number
) a
on b.number = a.number
and b.Id = a.id
) b2
on b.number = b2.number
where b.Date > '2012-12-01'
group by b.NumberID

Use distinct in the SELECT under join as well.
//etc.
join
(
select DISTINCT b.* from
Accounting b
//etc.

You case syntax is incorrect. Check the manual.
It should go like this:
CASE case_value
WHEN when_value
so in your case (jeje)
select distinct b.*,
case b.NumberID when b.NumberID then ...
which is strange indeed. b.NumberID is always b.NumberID :o

SELECT b.NumberID, SUM(b.Percentage) AS Percentage,
(
SELECT MAX(s.id)
FROM Accounting s
WHERE s.Date < '2012-12-01' AND s.number = b.number
) AS MaxID
FROM Accounting b
WHERE b.Date > '2012-12-01'
GROUP BY b.NumberID

You need all of the columns in your select statement included in the Group By clause. Let's say I have a table of ice cream favors and people that ordered them. I have data that says
Joe ordered chocolate
Katie ordered Strawberry
Mary ordered chocolate
If I tell sql to group by the ice cream flavor, I am telling it that I only want one row to be returned for that flavor. By including the customer's name in my select, I am asking sql who ordered each of the flavors. How can I show (in one row) who ordered chocolate? In order to ask sql for the information that I want, I need a row for each customer and each ice cream flavor (Maybe Mary ordered strawberry yesterday). So, I need to group by both customer and ice cream flavor.
Agregate functions don't need to be in the group by clause because you are asking sql a specific question regarding the data. Maybe I want to know the person whose name comes first alphabetically. Then, I could select the max(customer) and group by just the ice cream flavor.
Select b.id, b.number, b.firmname, b.date, sum(...)
From
Group by b.id, b.number, b.firmname, b.date
Notes:
You can still use the b.* syntax, as long as you name all the columns
in your group by
The distinct keyword is no longer necessary in your select, since
group by does essentially the same thing

Related

Ordering the results of Inner join

I currently have the following query:
select X.sellerID, x.Category, y.Award_Year, Y.Award
from Y
inner join
X on Y.Seller_ID=X.sellerID
Which give me this results:
How can I write a query to get Top 3 seller and the amount of awards recieved in 2017. Following is expected result:
I guess you want the top 3 sellers by descending order (i.e. most awards in top) and you could do a flashy pivot thing with filtering on year to get a more general query, but I suspect something like this one would be more suitable at this level..
The top 3 clause will differ between different DB Engines
MSSQL : TOP (nr) directly after SELECT
Oracle: FETCH FIRST nr ROWS ONLY last in statement
mysql: LIMIT nr last in statement
etc....
FOR MSSQL it would be something like
SELECT TOP (3) sellerID, Category, Award_Year, COUNT(Award_Year) Awards
from Y y
inner join
X x on y.Seller_ID=x.sellerID
WHERE Award_Year = 2017
GROUP BY x.sellerID, x.Category, y.Award_Year
Order By COUNT(Award_Year) DESC
Try this:
SELECT TOP(3) A.Id, A.category, count(A.Award) AS 'Award in 2017'
FROM (
SELECT X.sellerID AS ID, x.Category, y.Award_Year, Y.Award
FROM Y
INNER JOIN X on Y.Seller_ID=X.sellerID
) A
GROUP BY A.Id, A.category
ORDER BY count(A.Award) DESC
Depending on your actual DBMS version, you may need to amend it.

How to group by all but except one column

i want to group my results except one column:
Select Name,Title,Date from Books group by Name,Title
i don't want to group by date because books might have same date
you can use
Select Name,Title,Date from Books group by Name,Title,Date;
or
Select distinct Name,Title,Date from Books;
for the same purpose. It wont make any difference as the dates are same.
if dates are not same then you can use any aggregate function to achieve the same. like:
Select Name,Title,max(Date)--any aggregate function based on logic
from Books group by Name,Title,Date;
Quick and dirty without knowing what type of ordering or specific date values you want to include or exclude:
Select b.Name, b.Title, b.Date From Books b
Join (Select Name, Title From Books Group By Name, Title) sub
On sub.Name = b.Name And sub.Title = b.Title
These one is working as expected
SELECT [Name]
,[Title]
,max(Date)
FROM [Books].[dbo].[myBooks] group by [Name],[Title]

SQL query scenario.

Suppose I have the following table:
My goal is to display a select resultset that looks like this:
The tricky part here is to display the AverageCostPerType column for every single book.
I know how to get the AverageCostPerType, it's simply the following:
SELECT avg(bookcost) as AverageCostPerType FROM BOOK GROUPBY BookType;
This will display 3 rows since I have 3 distinct types. How can I display an averagecostpertype for each book ?
I'll appreciate your help.
you need use analytic functions
AVG per BookType
select b.*, avg(bookcost) over (PARTITION BY BookType)
from book b
AVG for all books
select b.*, avg(bookcost) over ()
from book b
You can use aggregate functions with an analytic partition window to get average cost by booktype.
This allows you to perform the query without joining the table to itself, or using nested select statements.
Oracle Aggregate Functions
select Book_num,
BookType,
avg(BookCost) over(partition by BookType) as AverageCostPerType,
BookCost,
BookCost - avg(BookCost) over(partition by BookType) as Difference
from YourBookTable
You can calculate the average per booktype in a derived table and join it to the original table to get the result.
select book_num, t.booktype, x.avgcost, bookcost, x.avgcost-bookcost
from tablename t join
(select booktype, avg(bookcost) as avgcost from tablename group by booktype) x
on t.booktype = x.booktype
select b.*,
b2.AverageCostPerType,
b2.AverageCostPerType - b.BookCost as difference
from book b
join
(
SELECT BookType, avg(bookcost) as AverageCostPerType
FROM BOOK
GROUP BY BookType
) b2 on b.BookType = b2.BookType

Oracle SQL use of subquery simulateneously in group by & select clauses in conjunction with CASE operator

Long title and strange problem:
I want to use the with-statement in oracle SQL to reuse a sub-query as well in the select as group by clause. Additionally, I use a case statement in order to create more information and group the results. This statement however throws following error: ORA-00979: not a GROUP BY expression.
Example query that is not working: I define a query containing the sum of the sales per product family. I sort this query and select the best selling product family out if it. As main result, I want to compare this top selling family to the sum of the sales of other product families (not one by one but all other product families grouped together). I do this following way:
WITH
top_family AS (
SELECT *
FROM (SELECT c.family
FROM products c, sales d
WHERE c.product_id= d.product_id
GROUP BY c.family
ORDER BY SUM(d.quantity) DESC)
WHERE ROWNUM = 1)
SELECT CASE
WHEN a.family IN (SELECT * FROM top_family)
THEN 'Most sold category'
ELSE 'Other categories'
END Family, SUM(a.price*b.quantity) "Total monetary sales"
FROM products a, sales b
WHERE a.product_id = b.product_id
GROUP BY CASE
WHEN a.family IN (SELECT * FROM top_family)
THEN 'Most sold category'
ELSE 'Other categories'
END
ORDER BY 1;
An interesting fact is that if I replace the sub-query 'top_family' as defined in the code above directly into the code (so replace every every place containing top_family with the select * from (select ...) statement), it works and gives the desired result.
The problem should probably be caused by using the sub-query defined in a with statement. Although I realize there are (better and more elegant) solutions than this one, I'd like to find out why I can't use the table alias "top_family" in the group by and select statement.
The problem is in the GROUP BY CASE WHEN statement.
This statement is only compiled in the final step of execution. This way that sub-clause is witheld from the SELECT CASE WHEN. This null operation is returning errors.
It is also described in the SQL manual.
After reading your requirements properly, I suggest you use something like this:
WITH
top_product AS (
SELECT s1.product_id
FROM sales s1
GROUP BY s1.product_id
HAVING sum(s1.quantity)
= (SELECT total_sale
FROM (SELECT SUM(s.quantity) AS total_sale
FROM sales s
GROUP BY s.product_id
GROUP BY SUM(s.quantity))
WHERE rownum = 1))
SELECT CASE t.product_id
WHEN null THEN 'Other categories'
ELSE 'Most sold category'
END Family,
SUM(a.price*b.quantity) "Total monetary sales"
FROM products a JOIN sales b
ON a.product_id = b.product_id
LEFT JOIN top_product t ON a.product_id = t.product_id
GROUP BY CASE t.product_id
WHEN null THEN 'Other categories'
ELSE 'Most sold category'
END
ORDER BY 1;

Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

I'm trying to select the latest date and group by name and keep other columns.
For example:
name status date
-----------------------
a l 13/19/04
a n 13/09/05
a dd 13/18/03
b l 13/01/01
b dd 13/01/02
b n 13/01/03
and I want the result like:
name status date
-----------------
a n 13/09/05
b n 13/01/03
Here's my code
SELECT
Name,
MAX(DATE) as Date,
Status
FROM
[ST].[dbo].[PS_RC_STATUS_TBL]
GROUP BY
Name
I know that I should put max(status) because There are a lot of possibilities in each case, and nothing in the query makes it clear which value to choose for status in each group. Is there anyway to use inner join ?
It's not clear to me you want the max or min status. Rather it seems to me you want the name and status as of a date certain. That is, you want the rows with the lastest date for each name. So ask for that:
select * from PS_RC_STATUS_TBL as T
where exists (
select 1 from PS_RC_STATUS_TBL
where name = T.name
group by name
having max(date) = T.date
)
Another way to think about it is
select T.*
from PS_RC_STATUS_TBL as T
join (
select name, max(date) as date
from PS_RC_STATUS_TBL
group by name
) as D
on T.name = D.name
and T.date = D.date
SQL Server needs to know what to do with the rows that you are not grouping on (it has multiple rows to show on 1 line - so how?). If you have aggregated on them (MIN, MAX, AVG, etc) then you are telling it what to do with these rows. If not it will not know what to do - and will give you an error like the one you are getting.
From what you are saying though - it sounds like you do not want to group by the status. It sounds like you are not interested in that column at all. Let me know If that assumption is wrong.
SELECT
Name,
MAX(Date) AS 'Date',
FROM
PS_RC_STATUS_TBL
GROUP BY
Name
If you really do want the status, but don't want to group on it - try this:
SELECT
MyTable1.Name,
MyTable2.Status,
MyTable1.Date
FROM
(SELECT Name, MAX(Date) AS 'Date' FROM PS_RC_STATUS_TBL GROUP BY Name) MyTable1
INNER JOIN
(SELECT Name, Date, Status FROM PS_RC_STATUS_TBL) MyTable2
ON MyTable1.Name = MyTable2.Name
AND MyTable1.Date = MyTable2.Date
That gives the exact results you've asked for - so does the method below using a CTE.
OR
WITH cte AS (
SELECT Name, MAX(Date) AS Date
FROM PS_RC_STATUS_TBL
GROUP BY Name)
SELECT cte.Name,
tbl.Status,
cte.Date
FROM cte INNER JOIN
PS_RC_STATUS_TBL tbl ON cte.Name = tbl.Name
AND cte.Date = tbl.Date
SQLFiddle example.
It just means that you need to put all non-aggregated columns in the GROUP BY clause, so in the case you need to put the other one
Select Name ,
MAX(DATE) as Date ,
Status
FROM [ST].[dbo].[PS_RC_STATUS_TBL] PS
Group by Name, Status
This is a common problem with text fields in SQL aggregation scenarios. Using either MAX(Status) or MIN(Status) in your field list is a solution, usually MAX(Status) because of the lexical ordering:
"" < " " < "a"
In cases where you really need a more detailed ordering:
Join to a StatusOrder relation (*Status, OrderSequence) in your main query;
select Max(OrderSequence) in your aggregated query; and
Join back to your StatusOrder relation on OrderSequence to select the correct Status value for display.
Whatever fields you're selecting other than aggregation function, need to mention in group by clause.
SELECT
gf.app_id,
ma.name as name,
count(ma.name) as count
FROM [dbo].[geo_fen_notification_table] as gf
inner join dbo.mobile_applications as ma on gf.app_id = ma.id
GROUP BY app_id,name
Here im accessing app_id and name in select, so i need to mention that after group by clause. otherwise it will throw error.