Display all rows into two columns - postgresql

I am having a View like this.
SemesterId Course Grade
1 course1 A
1 course2 B
1 course3 C
2 course4 D
2 course5 A
2 course6 B
2 course7 C
3 course8 D
3 course9 A
3 course10 B
4 course11 C
4 course12 D
I want to get result like below using postgre SQL.
SemesterId1 Course1 Grade1 SemesterId2 Course2 Grade2
1 course1 A 2 course4 D
1 course2 B 2 course5 A
1 course3 C 2 course6 B
2 course7 C
3 course8 D 4 course11 C
3 course9 A 4 course12 D
3 course10 B
How can I get this result?

This kind of tasks should be solved on client side. However, it's also an interesting issue to solve in SQL.
select
sid1, course1, grade1,
sid2, course2, grade2
from (
select
c1.semesterid sid1, c1.course course1, c1.grade grade1, c1.rk, c1.rn,
c2.semesterid sid2, c2.course course2, c2.grade grade2, c2.rk, c2.rn
from (
select *, dense_rank() over w rk, row_number() over w rn
from courses
where semesterid % 2 = 1
window w as (order by semesterid)
) c1
full join (
select *, dense_rank() over w rk, row_number() over w rn
from courses
where semesterid % 2 = 0
window w as (order by semesterid)
) c2
on c1.rk = c2.rk and c1.rn = c2.rn
order by coalesce(c1.rk, c2.rk), coalesce(c1.rn, c2.rn)
) sub
sid1 | course1 | grade1 | sid2 | course2 | grade2
------+----------+--------+------+----------+--------
1 | course1 | A | 2 | course4 | D
1 | course2 | B | 2 | course5 | A
1 | course3 | C | 2 | course6 | B
| | | 2 | course7 | C
3 | course8 | D | | |
3 | course9 | A | 4 | course11 | C
3 | course10 | B | 4 | course12 | D
(7 rows)

I don,t know why you do this this way, but you may use:
WITH tab_row_num AS (
SELECT row_number() over (PARTITION BY "SemesterId") rn, "SemesterId" , "Course" , "Grade" FROM table1
),
joint_tab AS (
select
t1_3."SemesterId" AS "SemesterId1", t1_3."Course" as "Course1" , t1_3."Grade" as "Grade1",
t2_4."SemesterId" AS "SemesterId2", t2_4."Course" as "Course2" , t2_4."Grade" as "Grade2"
from tab_row_num t1_3
full outer join tab_row_num t2_4
ON (t1_3."SemesterId" = 1 AND t2_4."SemesterId"=2 AND t1_3.rn=t2_4.rn)
OR (t1_3."SemesterId" = 3 AND t2_4."SemesterId"=4 AND t1_3.rn=t2_4.rn)
)
SELECT
COALESCE("SemesterId1"::TEXT, '') , COALESCE("Course1"::TEXT, '') , COALESCE("Grade1"::TEXT, ''),
COALESCE("SemesterId2"::TEXT, '') , COALESCE("Course2"::TEXT, '') , COALESCE("Grade2"::TEXT, '')
FROM (
SELECT *
FROM (
SELECT "SemesterId1" , "Course1" , "Grade1", "SemesterId2" , "Course2" , "Grade2"
FROM joint_tab t
WHERE (t."SemesterId1" = 1 OR t."SemesterId1" IS NULL) AND (t."SemesterId2" = 2 OR t."SemesterId2" IS NULL)
ORDER BY "SemesterId1" NULLS LAST, "Course1" ASC, "Grade1" ASC, "SemesterId2" NULLS LAST, "Course2" ASC, "Grade2" ASC
) sub1
UNION ALL
SELECT * FROM (
SELECT "SemesterId1" , "Course1" , "Grade1", "SemesterId2" , "Course2" , "Grade2"
FROM joint_tab t WHERE (t."SemesterId1" = 3 OR t."SemesterId1" IS NULL) AND (t."SemesterId2" = 4 OR t."SemesterId2" IS NULL)
ORDER BY "SemesterId1" NULLS LAST, "Course1" ASC, "Grade1" ASC, "SemesterId2" NULLS LAST, "Course2" ASC, "Grade2" ASC
) sub2
) sub

Related

Get different LIMIT on each group on postgresql rank

To get 2 rows from each group I can use ROW_NUMBER() with condition <= 2 at last but my question is what If I want to get different limits on each group e.g 3 rows for section_id 1, 1 rows for 2 and 1 rows for 3?
Given the following table:
db=# SELECT * FROM xxx;
id | section_id | name
----+------------+------
1 | 1 | A
2 | 1 | B
3 | 1 | C
4 | 1 | D
5 | 2 | E
6 | 2 | F
7 | 3 | G
8 | 2 | H
(8 rows)
I get the first 2 rows (ordered by name) for each section_id, i.e. a result similar to:
id | section_id | name
----+------------+------
1 | 1 | A
2 | 1 | B
5 | 2 | E
6 | 2 | F
7 | 3 | G
(5 rows)
Current Query:
SELECT
*
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY section_id ORDER BY name) AS r,
t.*
FROM
xxx t) x
WHERE
x.r <= 2;
Create a table to contain the section limits, then join. The big advantage being that as new sections are required or limits change maintenance is reduced to a single table update and comes at very little cost. See example.
select s.section_id, s.name
from (select section_id, name
, row_number() over (partition by section_id order by name) rn
from sections
) s
left join section_limits sl on (sl.section_id = s.section_id)
where
s.rn <= coalesce(sl.limit_to,2);
Just fix up your where clause:
with numbered as (
select row_number() over (partition by section_id
order by name) as r,
t.*
from xxx t
)
select *
from numbered
where (section_id = 1 and r <= 3)
or (section_id = 2 and r <= 1)
or (section_id = 3 and r <= 1);

Increment Row_Number Only Where Distinct

I have the following table, which I've made very simple because I do not know how to format it as a table on here (side note if anyone could link me to an easy tutorial on that I would be forever grateful).
id
1
1
1
2
2
2
I'd like to add another column which increments in number only on distinct IDs so the outcome should be
Id
1
1
1
2
2
2
rowNum
1
1
1
2
2
2
Currently all I can manage to get is:
id
1
1
1
2
2
2
rowNum
1
2
3
4
5
6
I'm missing something very simple here as I'm confident I should be able to solve this issue using either row_number or rank and a window function but I cannot figure it out.
Use DENSE_RANK() instead of ROW_NUMBER():
SELECT
id,
DENSE_RANK() OVER (ORDER BY id) dr
FROM yourTable
Demo
You can do this with a subquery self join, as well.
mysql> select id,
> (select count(distinct id)
> from
> testtest b
> where b.id < a.id)
> from testtest a;
+------+---------------------------------------------------------------+
| id | (select count(distinct id) from testtest b where b.id < a.id) |
+------+---------------------------------------------------------------+
| 1 | 0 |
| 1 | 0 |
| 1 | 0 |
| 2 | 1 |
| 2 | 1 |
| 2 | 1 |
+------+---------------------------------------------------------------+
6 rows in set (0.01 sec)
And one more way:
select a.id, b.idRank
from testtest a,
(
select id,
rank() over
(order by id) as idRank
from (
select distinct id
from testtest
) testtest2
) b
where a.id = b.id

How do I join multiple select results into a single table?

I have a query which returns monthly averages from the same table, but for different pressure_level's:
SELECT some_id, avg(exposure_value) monthly_avg_1000
FROM mytable
WHERE pressure_level = 1000
AND some_id = 7
GROUP BY some_id, date_trunc('month', measurement_time)
I then have the same query, but for a different pressure_level:
SELECT some_id, avg(exposure_value) monthly_avg_925
FROM mytable
WHERE pressure_level = 925
AND some_id = 7
GROUP BY some_id, date_trunc('month', measurement_time)
Both queries return 12 rows (1 per month) with the ID and the average value for the month:
some_id | monthly_avg_1000
--------------------------
1 | 0.000023
1 | 0.000051
1 | 0.000009
some_id | monthly_avg_925
--------------------------
1 | 0.000014
1 | 0.000007
1 | 0.000131
I would like to combine the two queries so that the monthly_avg_* columns all appear in the final table:
some_id | monthly_avg_1000 | monthly_avg_925
--------------------------
1 | 0.000023 | 0.000014
1 | 0.000051 | 0.000007
1 | 0.000009 | 0.000131
How can I do this?
if you have same id, then you can try join:
with a as (
SELECT some_id, avg(exposure_value) monthly_avg_1000,date_trunc('month', measurement_time) d
FROM mytable
WHERE pressure_level = 1000
AND some_id = 7
GROUP BY some_id, date_trunc('month', measurement_time)
)
, b as (
SELECT some_id, avg(exposure_value) monthly_avg_925, date_trunc('month', measurement_time) d
FROM mytable
WHERE pressure_level = 925
AND some_id = 7
GROUP BY some_id, date_trunc('month', measurement_time)
)
select distinct a.some_id, monthly_avg_1000,monthly_avg_925
from a
join b on a.some_id = b.some_id and a.d = b.d

Group rows into two types depending on a value in column

I have a table:
------------------------------------------
Uid | mount | category
-----------------------------------------
1 | 10 | a
1 | 3 | b
3 | 7 | a
4 | 1 | b
4 | 12 | a
4 | 5 | b
1 | 2 | c
2 | 5 | d
I want to have one result like this:
------------------------------------------
Uid | suma | sumnota
-----------------------------------------
1 | 10 | 5
2 | 0 | 5
3 | 7 | 0
4 | 12 | 6
Group by uid;
Suma is sum(mount) where catagory = 'a';
Sumnota is sum(mount) where catagory <> 'a';
Any ideas how to do it?
Use conditional aggregation with CASE statements in SUM() function:
SELECT
uid
, SUM(CASE WHEN category = 'a' THEN mount ELSE 0 END) AS suma
, SUM(CASE WHEN category IS DISTINCT FROM 'a' THEN mount ELSE 0 END) AS sumnota
FROM
yourtable
GROUP BY uid
ORDER BY uid
I'm using IS DISTINCT FROM clause to properly handle NULL values in category column. If that's not your case you could simply use <> operator.
From documentation (bold emphasis mine):
Ordinary comparison operators yield null (signifying "unknown"), not
true or false, when either input is null.
For non-null inputs, IS DISTINCT FROM is the same as the <> operator. However, if both inputs are null it returns false, and if only one input is null it returns true.
Here's a solution more "verbosed" than accepted answer.
WITH
t_suma AS ( SELECT uid, SUM(mount) AS suma
FROM your_table
WHERE category = 'a'
GROUP BY uid ),
t_sumnota AS ( SELECT uid, SUM(mount) AS sumnota
FROM your_table
WHERE category <> 'a' or category is NULL
GROUP BY uid )
SELECT distinct y.uid, COALESCE( suma, 0) AS suma, COALESCE( sumnota, 0 ) AS sumnota
FROM your_table y LEFT OUTER JOIN t_suma ON ( y.uid = t_suma.uid )
LEFT OUTER JOIN t_sumnota ON ( y.uid = t_sumnota.uid )
ORDER BY uid;

pl sql query recuresive looping

i have only one table "tbl_test"
Which have table filed given below
tbl_test table
trx_id | proj_num | parent_num|
1 | 14 | 0 |
2 | 14 | 1 |
3 | 14 | 2 |
4 | 14 | 0 |
5 | 14 | 3 |
6 | 15 | 0 |
Result i want is : when trx_id value 5 is fetched
it's a parent child relationship. so,
trx_id -> parent_num
5 -> 3
3 -> 2
2 -> 1
That means output value:
3
2
1
Getting all parent chain
Query i used :
SELECT * FROM (
WITH RECURSIVE tree_data(project_num, task_num, parent_task_num) AS(
SELECT project_num, task_num, parent_task_num
FROM tb_task
WHERE project_num = 14 and task_num = 5
UNION ALL
SELECT child.project_num, child.task_num, child.parent_task_num
FROM tree_data parent Join tb_task child
ON parent.task_num = child.task_num AND parent.task_num = child.parent_task_num
)
SELECT project_num, task_num, parent_task_num
FROM tree_data
) AS tree_list ;
Can anybody help me ?
There's no need to do this with pl/pgsql. You can do it straight in SQL. Consider:
WITH RECURSIVE my_tree AS (
SELECT trx_id as id, parent_id as parent, trx_id::text as path, 1 as level
FROM tbl_test
WHERE trx_id = 5 -- start value
UNION ALL
SELECT t.trx_id, t.parent_id, p.path || ',' || t.trx_id::text, p.level + 1
FROM my_tree p
JOIN tbl_text t ON t.trx_id = p.parent
)
select * from my_tree;
If you are using PostgresSQL, try using a WITH clause:
WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
), top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
product,
SUM(quantity) AS product_units,
SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;