group by in sql - sql

Here i dont want to group by on amount and i also dont want to remove amount from select statement
so how to modify query
select min(id),userid,calcu.amount from usertable ut
inner join calcu co on ut.userid=co.userid
group by userid,amount
how i modify above query ...
i try to achieved records which user have min id.. let say user have 1,2,3,4 id in calcu table now i want 1 id and against that there is amount column so i want that amount ..
when i remove amount i got 10 records but when i write amount column in select and group by i got 15 records

You can achieve it using the following nested query.
SELECT (SELECT MIN(ID),USERID FROM USERTABLE ut WHERE ut.USERID = co.USERID GROUP BY USERID), co.AMOUNT FROM CALCU co

Can you try this please? Your question is not very clear (pls post sample data and a table with desired output so I can test my query)
SELECT A.MINID, A.USERID, co.AMOUNT
FROM (SELECT MIN(id) AS MINID, USERID
FROM usertable ut
GROUP BY userid
) A
INNER JOIN calcu co ON A.USERID=co.userid

When you just select amount without grouping by it, a random row for each group will be selected (if your DBMS allows this at all).
In most DBMSs you have to specify which row for each group you want to display. This happens with aggregate functions. Choose the one that fits your needs
max()
min()
avg()
count()
If you want to see the row that corresponds to a row with a min()/max() value you can use one of these approaches (they are standard sql, so not limited to mysql): The Rows Holding the Group-wise Maximum of a Certain Column

Try something around these lines:
select calcu.id,ut.userid,calcu.amount
from usertable ut
inner join
(
select id,userid,amount from calc where id in
(
select min(id) from calc group by userid
)
) calcu
on ut.userid = calcu.userid

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.

How do I ensure that the Number column has values greater than one?

I have tried adding "HAVING Number>1" However this doesn't work correctly it only outputs 1 row
SELECT
T.EmployeeID,
COUNT(*) AS "Number"
FROM (
SELECT
Skill.Title,
Skill.SkillID,
EmployeeSkill.EmployeeID
FROM Skill
RIGHT JOIN EmployeeSkill ON EmployeeSkill.SkillID = Skill.SkillID
) T
GROUP BY T.EmployeeID
I believe it should work if you will use "having count(*)>1" instead of "having number>1"
If you want from the query only the rows where Number > 1
then you need to add a HAVING clause to the statement:
HAVING COUNT(*) > 1
I suspect that you are looking to pull out employees that have at least two skills. If so, it looks like that's a simple aggregate query on table EmployeeSkill, like:
SELECT EmployeeID, COUNT(*)
FROM EmployeeSkill
GROUP BY EmployeeID
HAVING COUNT(*) > 1

Highest Record for a set user

Hope someone can help.
I have been trying a few queries but I do not seem to be getting the desired result.
I need to identify the highest ‘’claimed’’ users within my table without discarding the columns from the final report.
The user can have more than one record in the table, however the data will be completely different as only the user will match.
The below query only provides me the count per user without giving me the details.
SELECT User, count (*) total_record
FROM mytable
GROUP BY User
ORDER BY count(*) desc
Table:
mytable
Column 1 = User Column 2 = Ref Number Column 3 = Date
The first column will be the unique identifier, however the data in the other columns will differ, therefore it needs to descend the highest claimed user with all the relevant rows to the user to the least claimed user.
User|Ref Num|Date
1|a|20150317
1|b|20150317
2|c|20150317
3|d|20150317
4|e|20150317
1|f|20150317
4|e|20150317
The below data is how the values should be returned.
User|Ref Num|Date|Count
1|a|20150317|3
1|b|20150317|3
1|f|20150317|3
2|c|20150317|1
3|d|20150317|1
4|e|20150317|2
4|e|20150317|2
Hope it makes sense.
Thank you
As you're using MSSQL you can use the OVER() clause like so:
SELECT [user], mt.ref_num, mt.[date], COUNT(mt.[user]) OVER(PARTITION BY mt.[user])
FROM myTable mt
More about the OVER clause can be found here: https://msdn.microsoft.com/en-us/library/ms189461.aspx
As per your comment you can use the wildcard * like so:
SELECT mt.*, COUNT(mt.[user]) OVER(PARTITION BY mt.[user])
FROM myTable mt
This would get you every column as well as the result of the count.
If you want to order by the number of record for each user, then use window functions instead of aggregation:
SELECT t.*
FROM (SELECT t., count(*) OVER (partition by user) as cnt
FROM mytable t
) t
ORDER BY cnt DESC, user;
Note that I added user to the order by so users with the same count will appear together in the list.
You could use an outer apply if your version of SQL Server supports it:
SELECT [User], [Ref Num], Date, total_record
FROM mytable M
OUTER APPLY (
SELECT count(*) total_record
FROM mytable
WHERE [user] = M.[user]
GROUP BY [user]
) oa
ORDER BY total_record desc, [user]
Note that user is a reserved keyword in MSSQL and you need to enclose it in either brackets [user] or double-quotes "user".
This would produce an output like:
user Ref Num Date total_record
1 a 2015-03-17 3
1 b 2015-03-17 3
1 f 2015-03-17 3
4 e 2015-03-17 2
4 e 2015-03-17 2
2 c 2015-03-17 1
3 d 2015-03-17 1
Note that the answers using the count(*) OVER (partition by [user]) construct are more efficient though.
Most simple way would be to use window fuction.
SELECT table.*, COUNT(*) OVER (PARTITION BY user)
FROM nameoftable table -- this is an alias
ORDER BY user, ref_num
This also seem to fit your need.
This is the old way of doing it. Where possible you should use OVER but as other people have answered with that I thought I'd throw this one into the mix.
SELECT
T.[User]
,T.[Ref Num]
,T.[Date]
,(SELECT count(*) from [myTable] T2 where T2.[User] = T.[USER]) as [Count]
FROM [mytable] T
ORDER BY [Count] DESC

Adding count in select query

I am trying to find a query that would give me a count of another table in the query. The problem is that I have no idea what to set where in the count part to. As it is now it will just give back a count of all the values in that table.
Select
ID as Num,
(select Count(*) from TASK where ID=ID(Also tried Num)) as Total
from ORDER
The goal is to have a result that reads like
Num Total
_________________
1 13
2 5
3 22
You need table aliases. So I think you want:
Select ID as Num,
(select Count(*) from TASK t where t.ID = o.ID) as Total
from ORDER o;
By the way, ORDER is a terrible name for a table because it is a reserved work in SQL.
You can do it as a sub query or a join (or an OVER statement.)
I think the join is clearest when you are first learning SQL
Select
ID as Num, count(TASK.ID) AS Total
from ORDER
left join TASK ON ORDER.ID=TASK.ID
GROUP BY ORDER.ID

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.