How to use SUM over GROUP BY in SQL Server? - sql

I currently have:
SELECT Name, COUNT(*) as Total
FROM DataTable
WHERE Name IN ('A', 'B', 'C')
GROUP BY Name
Resulting output:
Name Total
--------------
A 2
B 5
C 3
Instead I want this:
Name Total
--------------
A 10
B 10
C 10
Here 10 is a total of 2 + 5 + 3 (total number of records with name = A/B/C)
How do I do this?

To get your desired result you can use SUM() OVER () on the grouped COUNT(*). Demo
SELECT Name,
SUM(COUNT(*)) OVER () as Total
FROM DataTable
WHERE Name IN ('A', 'B', 'C')
GROUP BY Name

Get rid of the group by and use distinct:
select distinct Name, count(*) over() as Total
from t
where name in ('A', 'B', 'C')
rextester demo: http://rextester.com/WDMT68119
returns:
+------+-------+
| name | Total |
+------+-------+
| A | 10 |
| B | 10 |
| C | 10 |
+------+-------+

If you count all of the records and then do a cross join on all the different names
SELECT a.NAME
,x.Total
FROM DataTable a
CROSS JOIN (
COUNT(*) AS Total FROM DataTable
) x
GROUP BY a.NAME
,x.Total

Related

Get the min of one column but select multiple columns

I have a table as following:
ID NAME AMOUNT
______________________
1 A 3
1 B 4
2 C 18
4 I 2
4 P 9
And I want the min(Amount) for each ID but I still want to display its Name. So I want this:
ID NAME min(AMOUNT)
______________________
1 A 3
2 C 18
4 I 2
ID's can occur multiple times, Names too. I tried this:
SELECT ID, NAME, min(AMOUNT) FROM TABLE
GROUP BY ID
But of course its an error because I have to
GROUP BY ID, NAME
But then I get
ID NAME AMOUNT
______________________
1 A 3
1 B 4
2 C 18
4 I 2
4 P 9
And I understand why, it looks for the min(AMOUNT) for each combination of ID + NAME. So my question is basically, how can I select multiple column (ID, NAME, AMOUNT) and get the minimum for only one column, still displaying the others?
Im new to SQL but I cant seem to find an answer..
If you are using PostgreSQL, SQL Server, MySQL 8.0 and Oracle then try the following with window function row_number().
in case you have one id with similar amount then you can use dense_rank() instead of row_number()
Here is the demo.
select
id,
name,
amount
from
(
select
*,
row_number() over (partition by id order by amount) as rnk
from yourTable
) val
where rnk = 1
Output:
| id | name | amount |
| --- | ---- | ------ |
| 1 | A | 3 |
| 2 | C | 18 |
| 4 | I | 2 |
Second Option without using window function
select
val.id,
t.name,
val.amount
from myTable t
join
(
select
id,
min(amount) as amount
from myTable
group by
id
) val
on t.id = val.id
and t.amount = val.amount
You did not specify your db vendor. If it is luckily Postgres, the problem can be also solved without nested subquery using proprietary distinct on clause:
with t(id,name,amount) as (values
(1, 'A', 3),
(1, 'B', 4),
(1, 'W', 3),
(2, 'C', 18),
(4, 'I', 2),
(4, 'P', 9)
)
select distinct on (id, name_of_min) id
, first_value(name) over (partition by id order by amount) as name_of_min
, amount
from t
order by id, name_of_min
Just for widening knowledge. I don't recommend using proprietary features. first_value is standard function but to solve problem in simple query is still not enough. #zealous' answer is perfect.
In many databases, the most efficient method uses a correlated subquery:
select t.*
from t
where t.amount = (select min(t2.amount) from t t2 where t2.id = t.id);
In particular, this can take advantage of an index on (id, amount).

How to groupby by aggregating different keys in SQL

I have tables like below.
I would like to groupby by generating new keys like D,Dmeans AorB
In this case,countin D is 2 becauseAandBhas 1 record each.
Are there any way to generate new keys and groupby by using this?
product sex age
A M 10
B M 20
C F 30
My desired result is like below.
product count
C 1
D (A orB) 2
If you have same experience please let me know.
Thanks
Instead of the column product you must group by a derived column that matches your condition:
select
case when product in ('A', 'B') then 'D' else product end product,
count(*)
from tablename
group by case when product in ('A', 'B') then 'D' else product end
See the demo.
Results:
| newproduct | count(*) |
| ---------- | -------- |
| C | 1 |
| D | 2 |
ANSI SQL compliant query, use a case expression in a derived table to put A and B into D. GROUP BY its result:
select product, count(*)
from
(
select case when product in ('A', 'B') then 'D' else product end product
from tablename
) dt
group by product

SQL query for fetching a single column with multiple values

Consider the below table:
Table1
id | status
------------
1 | A
2 | B
3 | C
1 | B
4 | B
5 | C
4 | A
Desired output is 1 and 4 as they are having status as both 'A' and 'B'.
Can we write an query for this? I tried to query it using conditions like 'AND', 'UNION' and 'OR', but it did not return me the desired result.
If you want the ids with more than 1 statuses:
select id
from tablename
group by id
having count(distinct status) > 1
You can use aggregation:
select id
from t
where status in ('A', 'B')
group by id
having count(*) = 2;
If the table allows duplicates, then use count(distinct status) = 2.
Try this one, you can do without using having() as well
select
id
from
(
select
id,
count(distinct status) as cnt
from yourTable
group by
id
) val
where cnt > 1

Query to identify records without certain values

I have a table of data that looks something like this:
ID Num | Code
-------------
1 | A
1 | B
1 | C
1 | D
2 | A
2 | B
3 | A
3 | B
3 | D
4 | B
5 | A
5 | B
5 | E
And I need to be able to write an SQL query to show me all ID Numbers that do not have Codes C or D associated with them. (Which in this example would be ID Numbers 2, 4, & 5.)
Thanks in advance for any help you can provide!
I would use NOT IN:
SELECT DISTINCT ID_Num
FROM t
WHERE ID_Num NOT IN
(SELECT ID_Num
FROM t
WHERE code = 'C'
OR code = 'D')
I like to approach this type of question using group by and having:
select id_num
from t
group by id_num
having sum(case when code in ('C', 'D') then 1 else 0 end) = 0;
you can use 'not exists' (often more performant of not in)
SELECT DISTINCT ID_Num
FROM yourtable f1
WHERE not exists
(
SELECT * FROM yourtable f2
WHERE f2.code in ('C', 'D') and f2.ID_Num=f1.ID_Num
)
you can use 'left outer join lateral' and take not founded row like this:
SELECT DISTINCT f1.ID_Num
FROM yourtable f1
LEFT OUTER JOIN LATERAL
(
SELECT f2.ID_Num FROM yourtable f2
WHERE f2.code in ('C', 'D') AND f2.ID_Num=f1.ID_Num
FETCH FIRST ROWS ONLY
) f3 on 1=1
WHERE f3.ID_Num is null

MS Access Group Specific Values Together

So I am working on a problem in MS Access where I need some aggregated values grouped by certain values.
ExTable1:
Type TotalHours
+-------+------------+
| A 10 |
| A 20 |
| A 30 |
| B 10 |
| C 10 |
| D 10 |
| E 10 |
| F 10 |
+-------+------------+
And I have this query:
SELECT Type, SUM(TotalHours)
FROM ExTable1
GROUP BY Type
This groups all of the A's, B's, C's, etc.. together, but I also want to group E and F's values together in the same query. How would I do this?
Consider using a Union Query:
SELECT Type, Sum(TotalHours) As TotalHours
FROM ExTable1
WHERE Type IN ('A', 'B', 'C', 'D')
GROUP BY Type;
UNION SELECT 'E & F', Sum(TotalHours)
FROM ExTable1
WHERE Type IN ('E', 'F');
Use Conditional Aggregate
SELECT Type,
SUM(TotalHours),
SUM(case when Type in ('E','F') then TotalHours else 0 END) as 'E&F total'
FROM ExTable1
GROUP BY Type