How to do sum of different values without duplicate - postgresql

How to do a sum of different values but same ID without duplicate different values on a column?
My Input in SQL Command.
SELECT
students.id AS student_id,
students.name,
COUNT(*) AS enrolled,
c2.price AS course_price,
(COUNT(*) * price) AS paid
FROM students
LEFT JOIN enrolls e on students.id = e.student_id
LEFT JOIN courses c2 on e.course_id = c2.id
WHERE student_id NOTNULL
GROUP BY students.id, students.name, c2.price
ORDER BY student_id ASC;
My result.
student_id | name | enrolled | paid
------------+---------------------+----------+------
1001 | Gulbadan Bálint | 1 | 90
1002 | Hanna Adair | 5 | 450
1003 | Taddeo Bhattacharya | 1 | 90
1004 | Persis Havlíček | 1 | 75
1004 | Persis Havlíček | 5 | 450
1005 | Tory Bateson | 1 | 90
1007 | Dávid Fèvre | 1 | 90
1008 | Masuyo Stoddard | 1 | 90
1009 | Iiris Levitt | 1 | 75
1009 | Iiris Levitt | 2 | 180
1013 | Artair Kovač | 1 | 30
1013 | Artair Kovač | 1 | 90
1015 | Matilda Guinness | 2 | 180
1017 | Margarita Ek | 1 | 90
1018 | Misti Zima | 3 | 270
1019 | Conall Ventura | 1 | 90
1020 | Vivian Monday | 2 | 180
My expected result.
student_id | name | enrolled | paid
------------+---------------------+----------+------
1001 | Gulbadan Bálint | 1 | 90
1002 | Hanna Adair | 5 | 450
1003 | Taddeo Bhattacharya | 1 | 90
1004 | Persis Havlíček | 6 | 525
1005 | Tory Bateson | 1 | 90
1007 | Dávid Fèvre | 1 | 90
1008 | Masuyo Stoddard | 1 | 90
1009 | Iiris Levitt | 3 | 255
1013 | Artair Kovač | 2 | 120
1015 | Matilda Guinness | 2 | 180
1017 | Margarita Ek | 1 | 90
1018 | Misti Zima | 3 | 270
1019 | Conall Ventura | 1 | 90
1020 | Vivian Monday | 2 | 180
I think that the cause come from a GROUP BY command but it will throw an error if I do not write a GROUP BY price.

Perhaps you can use SUM() function.
Please see link below, maybe it's same case with you:
how to group by and return sum row in Postgres

You have excluded course_price column both in your current and expected result. It seems you had wrongly included that in group by.
SELECT
students.id AS student_id,
students.name,
COUNT(*) AS enrolled,
--c2.price AS course_price, --exclude this in o/p?
(COUNT(*) * price) AS paid
FROM students
LEFT JOIN enrolls e on students.id = e.student_id
LEFT JOIN courses c2 on e.course_id = c2.id
WHERE student_id NOTNULL
GROUP BY students.id, students.name --,c2.price --and remove it from here
ORDER BY student_id ASC;

Related

postgrest retreive ranked results

I made a game, with level and scores saved into an sql table like this :
create table if not exists api.scores (
id serial primary key,
pseudo varchar(50),
level int,
score int,
created_at timestamptz default CURRENT_TIMESTAMP
);
I want to display the scores in the ui with the rank of each score, based on the score column, ordered by desc.
Here is a sample data :
id | pseudo | level | score | created_at
----+----------+-------+-------+-------------------------------
1 | test | 1 | 1 | 2020-05-01 11:25:20.446402+02
2 | test | 1 | 1 | 2020-05-01 11:28:11.04001+02
3 | szef | 1 | 115 | 2020-05-01 15:45:06.201135+02
4 | erg | 1 | 115 | 2020-05-01 15:55:19.621372+02
5 | zef | 1 | 115 | 2020-05-01 16:14:09.718861+02
6 | aa | 1 | 115 | 2020-05-01 16:16:49.369718+02
7 | zesf | 1 | 115 | 2020-05-01 16:17:42.504354+02
8 | zesf | 2 | 236 | 2020-05-01 16:18:07.070728+02
9 | zef | 1 | 115 | 2020-05-01 16:22:23.406013+02
10 | zefzef | 1 | 115 | 2020-05-01 16:23:49.720094+02
Here is what I want :
id | pseudo | level | score | created_at | rank
----+----------+-------+-------+-------------------------------+------
31 | zef | 7 | 730 | 2020-05-01 18:40:42.586224+02 | 1
50 | Cyprien | 5 | 588 | 2020-05-02 14:08:39.034112+02 | 2
49 | cyprien | 4 | 438 | 2020-05-01 23:35:13.440595+02 | 3
51 | Cyprien | 3 | 374 | 2020-05-02 14:13:41.071752+02 | 4
47 | cyprien | 3 | 337 | 2020-05-01 23:27:53.025475+02 | 5
45 | balek | 3 | 337 | 2020-05-01 19:57:39.888233+02 | 5
46 | cyprien | 3 | 337 | 2020-05-01 23:25:56.047495+02 | 5
48 | cyprien | 3 | 337 | 2020-05-01 23:28:54.190989+02 | 5
54 | Cyzekfj | 2 | 245 | 2020-05-02 14:14:34.830314+02 | 9
8 | zesf | 2 | 236 | 2020-05-01 16:18:07.070728+02 | 10
13 | zef | 1 | 197 | 2020-05-01 16:28:59.95383+02 | 11
14 | azd | 1 | 155 | 2020-05-01 17:53:30.372793+02 | 12
38 | balek | 1 | 155 | 2020-05-01 19:08:57.622195+02 | 12
I want to retreive the rank based on the full table whatever the result set.
I'm using the postgrest webserver.
How do I do that ?
You are describing window function rank():
select t.*, rank() over(order by score desc) rnk
from mytable t
order by score desc

T-SQL Query with SUM, DATEADD logic

I’m looking for a query that can calculate the Calculation table from the Value table.
The query had to take the Date value, like ‘2017-03-01’ but I need to take the values from the records 2 months before, but it must be the record with the same ID. In this scenario It must take the values from 2017-03-01 , 2017-02-01, 2017-01-01 (993, 492, 312) and sum together (1,797) and store it in the 2017-03-01 record like below from the customer where CustomerID = 1001.
|1001 | 2017-02-01 | 492 | |
|1001 | 2017-03-01 | 993 | |
|1002 | 2017-01-01 | 838 | 1797 |
This need to be done of all records.
Of course, some record cannot go back minus 2 months, but those values can stay null.
I really don’t know to write this query.
Got some test queries to make some steps like:
SELECT SUM(Value) FROM TestTable WHERE Date BETWEEN Date AND DATEADD(month, -2, Date);
+------------+------------+-------+-------------+
| CustomerID | Date | Value | Calculation |
+------------+------------+-------+-------------+
| 1001 | 2016-08-01 | 123 | |
| 1001 | 2016-09-01 | 434 | |
| 1001 | 2016-10-01 | 423 | |
| 1001 | 2016-11-01 | 235 | |
| 1001 | 2016-12-01 | 432 | |
| 1001 | 2017-01-01 | 312 | |
| 1001 | 2017-02-01 | 492 | |
| 1001 | 2017-03-01 | 993 | |
| 1002 | 2017-01-01 | 838 | |
| 1002 | 2017-02-01 | 234 | |
| 1002 | 2017-03-01 | 453 | |
| 1002 | 2017-04-01 | 838 | |
| 1003 | 2017-01-01 | 746 | |
| 1003 | 2017-02-01 | 242 | |
| 1003 | 2017-03-01 | 432 | |
| 1004 | 2017-01-01 | 431 | |
| 1004 | 2017-02-01 | 113 | |
+------------+------------+-------+-------------+
I want my table like below
+------------+------------+-------+-------------+
| CustomerID | Date | Value | Calculation |
+------------+------------+-------+-------------+
| 1001 | 2016-08-01 | 123 | NULL |
| 1001 | 2016-09-01 | 434 | NULL |
| 1001 | 2016-10-01 | 423 | 980 |
| 1001 | 2016-11-01 | 235 | 1092 |
| 1001 | 2016-12-01 | 432 | 1090 |
| 1001 | 2017-01-01 | 312 | 979 |
| 1001 | 2017-02-01 | 492 | 1236 |
| 1001 | 2017-03-01 | 993 | 1797 |
| 1002 | 2017-01-01 | 838 | NULL |
| 1002 | 2017-02-01 | 234 | NULL |
| 1002 | 2017-03-01 | 453 | 1525 |
| 1002 | 2017-04-01 | 838 | 1525 |
| 1003 | 2017-01-01 | 746 | NULL |
| 1003 | 2017-02-01 | 242 | NULL |
| 1003 | 2017-03-01 | 432 | 1420 |
| 1004 | 2017-01-01 | 431 | NULL |
| 1004 | 2017-02-01 | 113 | NULL |
+------------+------------+-------+-------------+
I hope you can help me with this! 😉
--First Create Table
create table Testtable(
CustomerID int,
Date date,
Value int
)
--Insert test values
insert into Testtable VALUES(1001,'2016-08-01',123),
(1001,'2016-09-01',434),
(1001,'2016-10-01',423),
(1001,'2016-11-01',235),
(1001,'2016-12-01',432),
(1001,'2017-01-01',312),
(1001,'2017-02-01',492),
(1001,'2017-03-01',993),
(1002,'2017-01-01',838),
(1002,'2017-02-01',234),
(1002,'2017-03-01',453),
(1002,'2017-04-01',838),
(1003,'2017-01-01',746),
(1003,'2017-02-01',242),
(1003,'2017-03-01',432),
(1004,'2017-01-01',431),
(1004,'2017-02-01',113);
--Select Query
SELECT
CustomerID,
Date,
Value,
CASE WHEN (SELECT COUNT(*) FROM Testtable T4 WHERE T4.CustomerID = T3.CustomerID AND T4.Date < T3.Date) < 2 THEN NULL
ELSE Calculation END AS Calculation
FROM
(SELECT
*,
(SELECT SUM(T2.Value) FROM Testtable T2 WHERE T.CustomerID = T2.CustomerID AND T2.Date BETWEEN DATEADD(month,-2,T.Date) AND T.Date) AS Calculation
FROM Testtable T) AS T3
This might take some trial and error to get completely correct but I'll give it a shot, try below:
SELECT CustomerID
, Date
, Value
, Value +
(SELECT Value from table_name where CustomerID = x.CustomerID and Date =
DATEADD(m,-1,x.Date)) +
(SELECT Value from table_name where CustomerID = x.CustomerID and Date =
DATEADD(m,-2,x.Date)) as Calculation
FROM table_name x
Note that this will only work it the CustomerID/Date are a composite key in your table.
Hope this helps!

Return Only Duplicate Rows with PostgreSQL

I have a bookings table and I am trying show unique guests who booked the same room.
booking_id | check_in_date | check_out_date | guest_id | room_number
------------+---------------+----------------+----------+-------------
1001 | 2018-01-01 | 2018-01-03 | 1 | 702
1002 | 2018-01-01 | 2018-01-05 | 2 | 1104
1003 | 2018-01-05 | 2018-01-07 | 4 | 509
1004 | 2018-01-05 | 2018-01-07 | 4 | 511
1005 | 2018-01-07 | 2018-01-08 | 2 | 404
1006 | 2018-01-07 | 2018-01-09 | 1 | 1104
1007 | 2018-01-10 | 2018-01-12 | 2 | 509
1008 | 2018-01-15 | 2018-01-18 | 6 | 404
1009 | 2018-01-15 | 2018-01-18 | 6 | 406
1010 | 2018-01-15 | 2018-01-17 | 4 | 511
1011 | 2018-01-20 | 2018-01-22 | 2 | 509
1012 | 2018-01-23 | 2018-01-25 | 4 | 511
So I'm attemping to return:
room_number 404 (room booked by unique guest_id 2 & 6)
room_number 509 (room booked by unique guest_id 2 & 4)
room_number 1104 (room booked by unique guest_id 1 & 2)
The closest I got was with this statement:
SELECT room_number, guest_id, COUNT(room_number)
FROM bookings
GROUP BY room_number, guest_id
ORDER BY room_number;
Which returns:
room_number | guest_id | count
-------------+----------+-------
404 | 2 | 1
404 | 6 | 1
406 | 6 | 1
509 | 4 | 1
509 | 2 | 2
511 | 4 | 3
702 | 1 | 1
1104 | 2 | 1
1104 | 1 | 1
I need to remove the room_number that appear once (room_number 406, 511 & 702).
Try This.
SQL Fiddle
Query 1:
select a.room_number,a.guest_id, b.count
FROM bookings a JOIN
(
SELECT room_number, count (*) as count
FROM bookings
GROUP BY room_number
HAVING count ( DISTINCT guest_id ) > 1
) b ON a.room_number = b.room_number
ORDER BY room_number
Results:
| room_number | guest_id | count |
|-------------|----------|-------|
| 404 | 6 | 2 |
| 404 | 2 | 2 |
| 509 | 2 | 3 |
| 509 | 4 | 3 |
| 509 | 2 | 3 |
| 1104 | 2 | 2 |
| 1104 | 1 | 2 |
Maybe you can try something like this. (This query works in SQL SERVER)
SELECT a_tabla.guest_id,COUNT(a_tabla.room_number)
FROM Resultados a_tabla
INNER JOIN (SELECT new_table.guest_id
FROM Resultados new_table
GROUP BY new_table.guest_id
HAVING count(*) > 1) R ON (a_tabla.guest_id = new_table.guest_id)
ORDER BY a_tabla.guest_id
This querys will return the guest_id which has 1 or more rooms, if you change 1 for 2 this will return the guest_id which has more than 2 rooms.
I hope this will be helpful

If a users record on column x is not null how do I count how many records that user has after the first time it is not null?

I would like to create a count per user of number of records after the first time that x is not null for that user.
I have a table that is similar to the following:
id | user_id | completed_at | x
----+---------+--------------+---
1 | 1001 | 2017-06-01 | 1
20 | 1001 | 2017-06-01 | 2
21 | 1001 | 2017-06-02 | 4
22 | 1001 | 2017-06-03 |
24 | 1001 | 2017-06-03 |
25 | 1001 | 2017-06-04 |
23 | 1001 | 2017-06-04 |
12 | 1001 | 2017-06-06 |
13 | 1001 | 2017-06-07 |
14 | 1001 | 2017-06-08 |
2 | 1002 | 2017-06-02 | 3
27 | 1002 | 2017-06-02 | 7
15 | 1002 | 2017-06-09 |
3 | 1003 | 2017-06-03 |
4 | 1004 | 2017-06-04 |
5 | 1005 | 2017-06-05 |
33 | 1005 | 2017-06-20 | 8
34 | 1006 | 2017-07-10 | 9
6 | 1006 | 2017-10-06 |
7 | 1007 | 2017-10-07 |
8 | 1008 | 2017-10-08 |
9 | 1009 | 2017-10-09 |
10 | 1010 | 2017-10-10 |
16 | 1011 | 2017-06-01 |
11 | 1011 | 2017-07-01 | 5
17 | 1012 | 2017-06-02 |
26 | 1012 | 2017-07-02 | 6
18 | 1013 | 2017-06-03 |
19 | 1014 | 2017-06-04 |
31 | 1014 | 2017-06-24 |
32 | 1014 | 2017-06-24 |
30 | 1014 | 2017-06-24 |
29 | 1014 | 2017-06-24 |
28 | 1014 | 2017-06-24 |
The expected output would look like this:
+------+------------+---------------+
| user | first_x | records_after |
+------+------------+---------------+
| 1001 | 2017-06-01 | 9 |
| 1002 | 2017-06-02 | 2 |
| 1005 | 2017-06-20 | 0 |
| 1011 | 2017-07-01 | 0 |
| 1012 | 2017-07-02 | 0 |
+------+------------+---------------+
Using running count, and then conditional count for running count > 0
Sample
WITH flags AS (
SELECT
user_id,
completed_at,
sum(CASE WHEN x IS NULL THEN 0 ELSE 1 END) OVER (PARTITION BY user_id ORDER BY completed_at ROWS BETWEEN UNBOUNDED PRECEDING AND 0 FOLLOWING) AS flag
FROM users
),
completed AS (
SELECT DISTINCT ON (user_id)
user_id,
completed_at AS first_x
FROM flags
WHERE flag > 0
ORDER BY user_id, completed_at
)
SELECT DISTINCT
user_id AS user,
first_x,
count(flag) FILTER (WHERE flag>0) - 1 AS records_after
FROM flags
NATURAL JOIN completed
GROUP BY 1, 2
ORDER BY 1

AVG didn't give the correct value - Postgresql

I have a table in which there many redundant points, I want to select distinct points using (distinct) and to select the average of some row (eg. rscp).
Here we have an example :
| id | point | rscp | ci
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| 1 | POINT(10.1192 36.8018) | 10 | 701
| 2 | POINT(10.1192 36.8018) | 11 | 701
| 3 | POINT(10.1192 36.8018) | 12 | 701
| 4 | POINT(10.4195 36.0017) | 30 | 701
| 5 | POINT(10.4195 36.0017) | 44 | 701
| 6 | POINT(10.4195 36.0017) | 55 | 701
| 7 | POINT(10.9197 36.3014) | 20 | 701
| 8 | POINT(10.9197 36.3014) | 22 | 701
| 9 | POINT(10.9197 36.3014) | 25 | 701
What i want to get is this table below : (rscp_avg is the average of rscp of the redundant points)
| id | point | rscp_avg | ci
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| * | POINT(10.1192 36.8018) | 11 | *
| * | POINT(10.4195 36.0017) | 43 | *
| * | POINT(10.9197 36.3014) | 22.33 | *
I tried this, but it gave me a false average !!!!
select distinct on(point)
id,st_astext(point),avg(rscp) as rscp_avg,ci
from mesures
group by id,point,ci;
Thanks for your help (^_^)
Hamdoulah ! Thanks God !
I find the solution just now :
select on distinct(point)
id,st_astext(point),rscp_avg,ci
from
(select id,point,avg(rscp) over w as rscp_avg,ci
from mesures
window w as (partition by point order by id desc)
) ss
order by point,id asc;
The websites that help me are :
http://www.postgresql.org/docs/9.1/static/tutorial-window.html
http://www.w3resource.com/PostgreSQL/postgresql-avg-function.php