SQL Aggregation depending on value of attribute in unselected column - sql

I've got a table TABLE1 like this:
|--------------|--------------|--------------|
| POS | UNIT | VOLUME |
|--------------|--------------|--------------|
| 1 | M2 | 20 |
| 1 | M2 | 30 |
| 1 | M3 | 40 |
| 2 | M2 | 100 |
| 2 | M3 | 20 |
| 3 | ST | 30 |
| 3 | M2 | 10 |
|--------------|--------------|--------------|
Depending on the value of the column UNIT I want to aggregate as follows (each UNIT becomes a new column with the sum of the according value):
|--------------|--------------|--------------|--------------|
| POS | VOLUME_M2 | VOLUME_M3 | VOLUME_ST |
|--------------|--------------|--------------|--------------|
| 1 | 50 | 40 | 0 |
| 2 | 100 | 20 | 0 |
| 3 | 10 | 0 | 30 |
|--------------|--------------|--------------|--------------|
My Solution is
SELECT POS,
CASE
WHEN UNIT = 'M2'
THEN SUM(VOLUME)
ELSE 0
END AS VOLUME_M2,
CASE
WHEN UNIT = 'M3'
THEN SUM(VOLUME)
ELSE 0
END AS VOLUME_M3,
CASE
WHEN UNIT = 'ST'
THEN SUM(VOLUME)
ELSE 0
END AS VOLUME_S
FROM TABLE1
GROUP BY POS, UNIT
My problem is, that my code does not work if I leave out UNIT in the GROUP BY statement (I either have to use it in my aggregation or in my GROUP BY statement)
Therefore I get something like this:
|--------------|--------------|--------------|--------------|
| POS | VOLUME_M2 | VOLUME_M3 | VOLUME_ST |
|--------------|--------------|--------------|--------------|
| 1 | 50 | 0 | 0 |
| 1 | 0 | 40 | 0 |
| 2 | 0 | 20 | 0 |
| 2 | 100 | 0 | 0 |
| 3 | 10 | 0 | 0 |
| 3 | 0 | 0 | 30 |
|--------------|--------------|--------------|--------------|
Besides, could anyone give me a hint, how it is possible to automatically get this type of result (especially if there are a lot of values for UNIT).

Close. For conditional aggregation, the case expression is an argument to the aggregation function:
SELECT POS,
SUM(CASE WHEN UNIT = 'M2' THEN VOLUME ELSE 0 END) AS VOLUME_M2,
SUM(CASE WHEN UNIT = 'M3' THEN VOLUME ELSE 0 END) AS VOLUME_M3,
SUM(CASE WHEN UNIT = 'ST' THEN VOLUME ELSE 0 END) AS VOLUME_ST
FROM TABLE1
GROUP BY POS;

Related

T-SQL - subqueries on rows grouped by ID to create a summary table

I have a table "MyTable" with an id and two int fields "A" and "B":
MyTable
+-------+-----+-----+
| ID | A | B |
+-------+-----+-----+
| 99 | 0 | 1 |
| 99 | 0 | 1 |
| 99 | 0 | 0 |
| 99 | 1 | 1 |
| 99 | 0 | 1 |
| 100 | 1 | 0 |
| 100 | 0 | 0 |
| 100 | 0 | 0 |
| 444 | 1 | 0 |
| 88 | 0 | 0 |
| 88 | 0 | 0 |
| 88 | 0 | 1 |
+-------+-----+-----+
I'd like a T-SQL query to return a single row for each distinct id, which contains:
each distinct ID
whether there exists a row for that ID with a non-zero value for "a"
whether there exists a row for that ID with a non-zero value for "b"
like so:
+-------+-----+-----+
| ID | A | B |
+-------+-----+-----+
| 99 | 1 | 1 |
| 100 | 1 | 0 |
| 444 | 1 | 0 |
| 88 | 0 | 1 |
+-------+-----+-----+
I can GROUP BY the ID, but I don't know how to create the joins or subqueries on each group to get the desired result.
select id, max(case when A<>0 then 1 else 0 end)A, max(case when B<>0 then 1 else 0 end)B
from mytable
group by id
Or you can just use since your value is 1 and 0. But if value is other than that please use first query.
select id, max(A)A, max(B)B
from mytable
group by id

How to set value based on value existence in SQL Server?

I have the following T-SQL code:
select
id,
(case
when n in(Bla1', 'Bla2') then 1
when n = 'Bla3' then 99
else 0
end) as c
from
hello
Running this code outputs this result:
| id | c |
+--------+----+
| 577140 | 0 |
| 577140 | 1 |
| 577140 | 0 |
| 577140 | 0 |
| 577140 | 99 |
| 577141 | 0 |
| 577141 | 0 |
| 577141 | 0 |
| 577142 | 0 |
| 577142 | 0 |
| 577142 | 1 |
How can I modify the code to get the following output?
| id | c |
+--------+----+
| 577140 | 99 |
| 577141 | 0 |
| 577142 | 1 |
Rule
For each id: If 99 exists, then c becomes 99. If not, either 1 or 0, depending if any 1 exists.
You can use aggregation:
select id,
max(case when n in ('Bla1', 'Bla2') then 1
when n = 'Bla3' then 99
else 0
end) as c
from hello
group by id;

SQL Server Pivot table for survey responses

I have a sql server table called surveys with the following data
+------------+--------------+----+----+----+----+
| ModuleCode | SurveyNumber | Q1 | Q2 | Q3 | Q4 |
+------------+--------------+----+----+----+----+
| NME3519 | 1 | 5 | 4 | 5 | 3 |
| NME3519 | 2 | 3 | 3 | 2 | 1 |
| NME3519 | 3 | 4 | 3 | 2 | 1 |
| NME3520 | 1 | 4 | 3 | 2 | 1 |
| NME3519 | 4 | 4 | 2 | 2 | 1 |
+------------+--------------+----+----+----+----+
I'd like to be able to report on one module at a time and for the result to be something like this:
Count of scores
+----------+---+---+---+---+---+
| Question | 1 | 2 | 3 | 4 | 5 |
+----------+---+---+---+---+---+
| Q1 | 0 | 0 | 1 | 2 | 1 |
| Q2 | 0 | 1 | 2 | 1 | 0 |
| Q3 | 0 | 3 | 0 | 0 | 1 |
| Q4 | 3 | 0 | 1 | 0 | 0 |
+----------+---+---+---+---+---+
I'm pretty sure from other examples I need to unpivot and then pivot but I can't get anywhere with my own data.
Many thanks
Richard
Unpivot and aggregate:
select v.question,
sum(case when v.score = 1 then 1 else 0 end) as score_1,
sum(case when v.score = 2 then 1 else 0 end) as score_2,
sum(case when v.score = 3 then 1 else 0 end) as score_3,
sum(case when v.score = 4 then 1 else 0 end) as score_4,
sum(case when v.score = 5 then 1 else 0 end) as score_5
from responses r cross apply
( values ('Q1', r.q1), ('Q2', r.q2), ('Q3', r.q3), ('Q4', r.q4), ('Q5', r.q5)
) v(question, score)
group by v.question;
This version uses a lateral join for unpivoting. I find the syntax simpler and lateral joins more powerful. Why bother learning unpivot when something else does the same thing more concisely, more powerfully, and has the same performance?
As for the pivoting, it uses conditional aggregation. In my experience with SQL Server, this has pretty much the same performance as pivot.

Encapsulated Group by or conditional aggregation in Vertica DB

I have the following table in a Vertica DB:
+---------+-------+
| Readout | Event |
+---------+-------+
| 1 | A |
| 1 | B |
| 1 | A |
| 2 | B |
| 2 | A |
+---------+-------+
I would like to group each readout and count the frequency of the events, resulting in a table like this:
+---------+----------------+----------------+-----------------+
| Readout | Count(Readout) | Count(Event A) | Count (Event B) |
+---------+----------------+----------------+-----------------+
| 1 | 3 | 2 | 1 |
| 2 | 2 | 1 | 1 |
+---------+----------------+----------------+-----------------+
I am sure there is an easy GROUP BY command, but I can't wrap my head around it.
You want conditional aggregation:
select readout, count(*),
sum(case when event = 'A' then 1 else 0 end) as num_a,
sum(case when event = 'B' then 1 else 0 end) as num_b
from t
group by readout;

SQL Renaming NULL for null values and rollup

I'm trying to make a dashboard to show open tickets and who they are assigned to, by priority. The data set looks like:
|-------------------------|
| Assigned | Priority |
|-------------------------|
| JOE | Low |
| JOE | Medium |
| MARY | High |
| | Medium |
| TIM | Low |
| Mary | High |
The report I'm trying to get is:
|---------------------------------------------------------|
| Employee | Low | Medium | High | Total |
|---------------------------------------------------------|
| Total | 2 | 2 | 2 | 6 |
| Unassigned | 0 | 1 | 0 | 1 |
| MARY | 0 | 0 | 2 | 2 |
| JOE | 1 | 1 | 0 | 2 |
| TIM | 1 | 0 | 0 | 1 |
I can get it all, with this query
SELECT
CASE WHEN Assigned is null then 'Unassigned' Else Assigned End Employee
, SUM(CASE WHEN Priority = 'Low' THEN 1 ELSE 0 END) AS Low
, SUM(CASE WHEN Priority = 'Medium' THEN 1 ELSE 0 END) AS Medium
, SUM(CASE WHEN Priority = 'High' THEN 1 ELSE 0 END) AS High
, count(Priority) AS Total
FROM table
GROUP BY Assigned WITH ROLLUP
ORDER BY Assigned ASC
This way however, it is renaming anything that was NULL to Unassigned. This is perfect to give me the Unassigned values that don't have anyone in the Assigned field, however my Total column is also showing Unassigned. How do I rename that to Total?
Thanks in advance!
You need to use GROUPING, like this:
SQL Fiddle
SELECT
CASE WHEN (GROUPING(Assigned) = 1) THEN 'Total'
When Assigned is null then 'Unassigned'
Else Assigned End Employee
, SUM(CASE WHEN Priority = 'Low' THEN 1 ELSE 0 END) AS Low
, SUM(CASE WHEN Priority = 'Medium' THEN 1 ELSE 0 END) AS Medium
, SUM(CASE WHEN Priority = 'High' THEN 1 ELSE 0 END) AS High
, count(Priority) AS Total
FROM table1
GROUP BY Assigned WITH ROLLUP
--DER BY Assigned ASC
Results:
| EMPLOYEE | LOW | MEDIUM | HIGH | TOTAL |
|------------|-----|--------|------|-------|
| Unassigned | 0 | 1 | 0 | 1 |
| JOE | 1 | 1 | 0 | 2 |
| MARY | 0 | 0 | 2 | 2 |
| TIM | 1 | 0 | 0 | 1 |
| Total | 2 | 2 | 2 | 6 |