Postgres crosstab query - sql

I have a table which has 7 different classes with an area value.
pid | class| area |
----+------+------+
2 | 1 | 10 |
2 | 2 | 10 |
2 | 6 | 20 |
4 | 1 | 30 |
4 | 2 | 40 |
4 | 3 | 50 |
4 | 4 | 60 |
4 | 5 | 70 |
9 | 6 | 80 |
11 | 1 | 90 |
11 | 4 | 10 |
11 | 7 | 20 |
However I want to present this data in a format that has each distinct pid as a column heading and then have each row correspond to a class area (i.e. first row is the area of class 1 for each pid).
2 | 4 | 9 | 11 |
---+-----+-----+----+
10 | 30 | 0 | 90 |
10 | 40 | 0 | 0 |
0 | 50 | 0 | 0 |
0 | 60 | 0 | 10 |
0 | 70 | 0 | 0 |
20 | 0 | 60 | 0 |
0 | 0 | 0 | 20 |
Is it possible to create an output like this in PostgreSQL?

Try this:
SELECT
SUM(CASE WHEN pid = 2 THEN area ELSE 0 END) As "2",
SUM(CASE WHEN pid = 4 THEN area ELSE 0 END) As "4",
SUM(CASE WHEN pid = 9 THEN area ELSE 0 END) As "9",
SUM(CASE WHEN pid = 11 THEN area ELSE 0 END) As "11"
FROM t
GROUP BY class
ORDER BY class

Related

Subtract values in each row of a column based on a WHERE and GROUP BY statement in SQL

I would like to subtract each row "Value" with the "Value" where Sub1=0 grouping by ID_1 and ID_2 using a SQL query.
This is the table structure:
------------------------------------
ID_1 |ID_2 | sub1 | Value
------------------------------------
1 | a | 0 | 20
1 | a | 50 | 30
1 | a | 100 | 40
1 | b | 0 | 25
1 | b | 50 | 30
1 | b | 100 | 50
2 | a | 0 | 5
2 | a | 50 | 10
2 | a | 100 | 30
2 | b | 0 | 25
2 | b | 50 | 50
2 | b | 100 | 70
I would like to group by ID_1 and ID_2 and subtract each row's value with the value where the Sub1=0
Output table should be :
------------------------------------
ID_1 |ID_2 | sub1 | Value | Diff
------------------------------------
1 | a | 0 | 20 | 0
1 | a | 50 | 30 | 10
1 | a | 100 | 40 | 20
1 | b | 0 | 25 | 0
1 | b | 50 | 30 | 5
1 | b | 100 | 50 | 25
2 | a | 0 | 5 | 0
2 | a | 50 | 10 | 5
2 | a | 100 | 30 | 25
2 | b | 0 | 25 | 0
2 | b | 50 | 50 | 25
2 | b | 100 | 70 | 45
Use a window function:
select t.*,
(value -
sum(case when sub1 = 0 then value else 0 end) over (partition by id_1, id_2)
) as diff
from t;
This should work:
select t1.*, t1.value - t2.value as diff
from t t1
left join t t2 on t2.id_1 = t1.id_1 and t2.id_2 = t1.id_2 and t2.sub1 = 0
See it here:
http://sqlfiddle.com/#!9/cab4d5/1

Sum of Time differences while having times in 6 different columns

I have following database
Station|Status | Start_hour | Start_minute | Start_second | End_hour | End_minute | End_second
Is it possible to sum up times for every entry that has end_ fields filled between given time?
Example:
Station|Status|Start_hour|Start_minute|Start_second|End_hour|End_minute|End_second
8 | 0 | 10 | 5 | 0 | NULL | NULL | NULL
8 | 1 | 10 | 5 | 0 | 10 | 15 | 30
2 | 1 | 9 | 53 | 0 | 10 | 16 | 45
7 | 0 | 10 | 23 | 0 | NULL | NULL | NULL
So the output would look like this:
Sum of time
29:15 */sum of times for stations 8 and 2(5:30 + 23:45)/*
Problem? I have to do it in single query

Enumerating table partitions in Postgres table

Suppose I have a table like this:
id | part | value
----+-------+-------
1 | 0 | 8
2 | 0 | 3
3 | 0 | 4
4 | 1 | 6
5 | 0 | 13
6 | 0 | 4
7 | 1 | 2
8 | 0 | 11
9 | 0 | 15
10 | 0 | 3
11 | 0 | 2
I would like to enumerate groups that have part atribute 0.
Ultimately I want to get this:
id | part | value | number
----+-------+-----------------
1 | 0 | 8 | 1
2 | 0 | 3 | 2
3 | 0 | 4 | 3
4 | 1 | 6 | 0
5 | 0 | 13 | 1
6 | 0 | 4 | 2
7 | 1 | 2 | 0
8 | 0 | 11 | 1
9 | 0 | 15 | 2
10 | 0 | 3 | 3
11 | 0 | 2 | 4
Is it possible to solve this with Postgres window functions or is there another way?
Yes, that is simple:
SELECT id, part, value,
row_number() OVER (PARTITION BY grp ORDER BY id) - 1 AS number
FROM (SELECT id, part, value,
sum(part) OVER (ORDER BY id) AS grp
FROM mytable
) AS q;
id | part | value | number
----+------+-------+--------
1 | 0 | 8 | 0
2 | 0 | 3 | 1
3 | 0 | 4 | 2
4 | 1 | 6 | 0
5 | 0 | 13 | 1
6 | 0 | 4 | 2
7 | 1 | 2 | 0
8 | 0 | 11 | 1
9 | 0 | 15 | 2
10 | 0 | 3 | 3
11 | 0 | 2 | 4
(11 rows)

SQL how to force to display row with 0 if no data available?

My table returns results as following (skips row if HourOfDay does not have data for particular ID)
ID HourOfDay Counts
--------------------------
1 5 5
1 13 10
1 23 3
..........................HourOfDay up till 23
2 9 1
and so on.
What I am trying to achieve is to force showing rows displaying 0 for HoursOfDay, which don't have data, like following:
ID HourOfDay Counts
--------------------------
1 0 0
1 1 0
1 2 0
1......................
1 5 5
1 6 0
1......................
1 23 3
2 0 0
2 1 0
etc.
I have researched around about it. It looks like I can achieve this result if I create an extra table and outer join it. So I have created table variable in SP (as a temp workaround)
DECLARE #Hours TABLE
(
[Hour] INT NULL
);
INSERT INTO #Hours VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
,(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23);
However, no matter how I join it, it does not achieve desired result.
How do I proceed? Do I add extra columns to join on? Completely different approach? Any hint in the right direction is appreciated!
Using a derived table for the distinct Ids cross joined to #Hours, left joined to your table:
select
i.Id
, h.Hour
, coalesce(t.Counts,0) as Counts
from (select distinct Id from t) as i
cross join #Hours as h
left join t
on i.Id = t.Id
and h.Hour = t.HourOfDay
rextester demo: http://rextester.com/XFZYX88502
returns:
+----+------+--------+
| Id | Hour | Counts |
+----+------+--------+
| 1 | 0 | 0 |
| 1 | 1 | 0 |
| 1 | 2 | 0 |
| 1 | 3 | 0 |
| 1 | 4 | 0 |
| 1 | 5 | 5 |
| 1 | 6 | 0 |
| 1 | 7 | 0 |
| 1 | 8 | 0 |
| 1 | 9 | 0 |
| 1 | 10 | 0 |
| 1 | 11 | 0 |
| 1 | 12 | 0 |
| 1 | 13 | 10 |
| 1 | 14 | 0 |
| 1 | 15 | 0 |
| 1 | 16 | 0 |
| 1 | 17 | 0 |
| 1 | 18 | 0 |
| 1 | 19 | 0 |
| 1 | 20 | 0 |
| 1 | 21 | 0 |
| 1 | 22 | 0 |
| 1 | 23 | 3 |
| 2 | 0 | 0 |
| 2 | 1 | 0 |
| 2 | 2 | 0 |
| 2 | 3 | 0 |
| 2 | 4 | 0 |
| 2 | 5 | 0 |
| 2 | 6 | 0 |
| 2 | 7 | 0 |
| 2 | 8 | 0 |
| 2 | 9 | 1 |
| 2 | 10 | 0 |
| 2 | 11 | 0 |
| 2 | 12 | 0 |
| 2 | 13 | 0 |
| 2 | 14 | 0 |
| 2 | 15 | 0 |
| 2 | 16 | 0 |
| 2 | 17 | 0 |
| 2 | 18 | 0 |
| 2 | 19 | 0 |
| 2 | 20 | 0 |
| 2 | 21 | 0 |
| 2 | 22 | 0 |
| 2 | 23 | 0 |
+----+------+--------+

Combining 3 columns with boolean values into one

I've stumbled upon a table with three columns
id | isRed | isBlue | is Green
==============================
1 | 1 | 0 | 0
2 | 1 | 0 | 0
3 | 1 | 0 | 0
4 | 0 | 1 | 0
5 | 0 | 1 | 0
6 | 0 | 1 | 0
7 | 0 | 0 | 1
8 | 0 | 1 | 0
9 | 0 | 0 | 1
10 | 0 | 0 | 0
I want to create query as simple as possible to transform it into something like that:
id | Color
==============================
1 | red
2 | red
3 | red
4 | blue
5 | blue
6 | blue
7 | green
8 | blue
9 | green
10 | 0
The values can't be true in two different columns and I can't alter the database. I need it to append it to quite long query with as little complication as possible. Any ideas?
With a select and case:
select id,
(case when isRed = 1 then 'red'
when isBlue = 1 then 'blue'
when isGreen = 1 then 'green'
else '0'
end) as color
from t;