How to group by all but except one column - sql

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]

Related

Count() how many times a name shows up in a table with the rest of info

I have read in various websites about the count() function but I still cannot make this work.
I made a small table with (id, name, last name, age) and I need to retrieve all columns plus a new one. In this new column I want to display how many times a name shows up or repeats itself in the table.
I have made test and can retrieve but only COLUMN NAME with the count column, but I haven't been able to retrieve all data from the table.
Currently I have this
select a.n_showsup, p.*
from [test1].[dbo].[person] p,
(select count(*) n_showsup
from [test1].[dbo].[person])a
This gives me all data on output but on the column n_showsup it gives me just the number of rows, now I know this is because I'm missing a GROUP BY but then when I write group by NAME it shows me a lot of records. This is an example of what I need:
You can use window functions, if you RDBMS supports them:
select t.*, count(*) over(partition by name) n_showsup
from mytable t
Alternatively, you can join the table with an aggregation query that counts the number of occurences of each name:
select t.*, x.n_showsup
from mytable t
inner join (select name, count(*) n_showsup from mytable group by name) x
on x.name = t.name
While the window function approach (#GMB's answer) is the right way to go, thinking through this from a subquery approach (like you were headed towards) would look something like:
select p.*, a.n_showsup
from [test1].[dbo].[person] p
INNER JOIN (
select name, count(*) n_showsup
from [test1].[dbo].[person]
GROUP BY name
) a ON p.name = a.name
This is VERY close to what you had, the difference is that we are grouping that subquery by name (so we get a count by name) and we can use that in the join criteria which we do with the ON clause on that INNER JOIN.
You should really never ever use a comma in your FROM clause. Instead use a JOIN.

Get the difference of unique elements of a flag-based ID in SQL

I have an SQL table from which I want to extract unique elements by ID, comparing different groups, for example :
ID,Group,Product
a,2,33
a,1,83
b,3,51
c,2,33
b,1,20
a,3,20
b,2,51
a,2,83
If I have two products equals in different groups for the same ID, then I don't save them. Resulting this:
ID,Group,Unique
a,2,33
c,2,33
b,1,20
a,3,20
I'm trying this in SQL, but I don't know how to do it, please help me!
Remove all rows that have the same product and different groups:
select *
from yourtable a
where not exists(
select 1 from yourtable b where a.Product = b.Product and a.Group <> b.Group
)
select * from table_1
qualify count("product") over(partition by "group")=1
One method is aggregation:
select id, max(group) as group, product
from t
group by id, product
having min(group) = max(group);

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

Perform SUM, and CASE WHEN

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

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.