Getting percentage change between selected data within a column in PostgreSQL - postgresql

I am using PostgreSQL and I am trying to calculate the percentage change for two values in the same column and group them by the name column and I am having trouble.
Suppose I have the following table:
name
day
score
Allen
1
87
Allen
2
89
Allen
3
95
Bob
1
64
Bob
2
68
Bob
3
75
Carl
1
71
Carl
2
77
Carl
3
80
I want the result to be the name and the percentage change for each person between day 3 and day 1. So Allen would be 9.2 because from 87 to 95 is a 9.2 percent increase.
I want the result to be:
name
percent_change
Allen
9.2
Bob
17.2
Carl
12.7
Thanks for your help.

Try this...
with dummy_table as (
select
name,
day,
score as first_day_score,
lag(score, 2) over (partition by name order by day desc) as last_day_score
from YOUR_TABLE_NAME
)
select
name,
(last_day_score - first_day_score) / first_day_score::decimal as percentage_change
from dummy_table where last_day_score is not null
Just replace YOUR_TABLE_NAME. There are likely more performant and fancier solutions, but this works.

You can try with lag function, something like this:
select name, day, score, 100*(score - lag(score, 1) over (partition by name order by day))/(lag(score, 1) over (partition by name order by day)) as growth_percentage

Related

PostgreSQL: calculation between first and last

I'm trying to write a query that calculates the number of days between the first and last score per id.
The data sample:
id date score
11 1/1/2017 25.34
4 1/2/2017 34.34
25 1/2/2017 15.78
4 3/2/2017 47.2
25 7/3/2017 65.21
11 9/3/2017 96.09
25 10/3/2017 11.3
4 10/3/2017 27.12
Which is far from what I need, but I'm really lost. Clueless to be honest. Any idea?
Thanks
Try this:
SELECT
customer_id,
date(last_score) - date(first_score) AS days_between_last_and_first_score,
total_score::float/(date(last_score) - date(first_score)) AS score_per_day
FROM
(
select customer_id,
MAX(date(purchase_date)) as last_score,
MIN(date(purchase_date)) as first_score,
SUM(score) AS total_score
FROM candidate_test_q1
group by customer_id
) AS sub_query

GROUP BY name and ORDER BY point & time MYSQLi

I'm new to this forum and I hope to find my solution about my problem.
I have this table :
name time points car date
Daniel | 55s | 210 | red |20/01/2018
Daniel | 45s | 250 | green |21/01/2018
Julie | 54s | 220 | red |19/01/2018
Julie | 33s | 150 | yellow|22/01/2018
and I wish to sort it like this
name time points car date
Daniel | 45s | 250 |green |21/01/2018
Julie | 54s | 220 |red |19/01/2018
first sorting by points, than sorting by time and group by name (optional the count)
I use this
SELECT NAME, MAX(POINTS) POINTS, MAX(TIME) TIME, MAX(CAR) CAR, MAX(DATE) DATE
FROM ( SELECT A.* FROM test A LEFT OUTER JOIN test B ON A.NAME=B.NAME AND
A.POINTS<B.POINTS AND A.TIME>B.TIME WHERE B.NAME IS NULL ) as sub GROUP BY NAME
and I get this :
name POINTS TIME CAR DATE
Daniel 250 45 green 2018-01-21
Julie 220 54 yellow 2018-01-22
Julie should have car=red & 2018-01-19
For Daniel it looks good
how can I get thise values (car & date) ?
thanks
Nico
You could give this a shot. It contains the table against itself and gets only records with the highest points and lowest time.
SELECT NAME, MAX(POINTS) POINTS, MAX(TIME) TIME
FROM
(
SELECT A.* FROM test A
LEFT OUTER JOIN test B ON A.NAME=B.NAME AND A.POINTS<B.POINTS AND A.TIME>B.TIME
WHERE B.NAME IS NULL
) GROUP BY NAME
For additional, try this:
SELECT * FROM
(
SELECT OUTERTEST.*,
#row_num := IF(#prev_value=OUTERTEST.name,#row_num+1,1) AS RowNumber,
#prev_value := OUTERTEST.name
FROM (SELECT * FROM TEST ORDER BY NAME, TEST.POINTS DESC, TEST.TIME ASC) OUTERTEST, (SELECT #row_num := 1, #prev_value := '') x
) A
WHERE A.ROWNUMBER=1
I did more test with this table
id name time points
1 Daniel 55 1140
2 Judie 54 1144
3 Judie 33 1028
4 Daniel 45 1180
5 Judie 53 1148
I apply this request
SELECT NAME, MAX(POINTS) POINTS, sub.TIME FROM (SELECT * FROM Testpoint ORDER BY POINTS DESC, TIME ASC) AS sub
GROUP BY sub.name
I have the max point for each name but the time is not the right one
name POINTS time
Daniel 1180 55
Judie 1148 54
Judie should have 53 for time and not 54
What I did wrong?
thankyou
Nico

Not getting desired format from Oracle query

I am trying to fetch data from data base in below format,
Month Count
----- -----
201208 124
201209 0
201210 56
201211 25
201212 0
201301 184
201302 0
In database I have entries like,
Month Count
----- -----
201206 56
201208 124
201210 56
201211 25
201301 184
201304 49
Below is my query,
SELECT MONTH, Count
FROM TABLE_NAME
WHERE MONTH BETWEEN 201208 AND 201302
AND ID = 'X'
Output :
Month Count
----- -----
201208 124
201210 56
201211 25
201301 184
Can anyone help me getting data in desired format.
First you should generate full month's sequence between these dates. You can do it with CONNECT BY LEVEL in Oracle. then just JOIN this sequence with your table:
SELECT MonthSeq.MONTH,
NVL(Count,0) Count
FROM TABLE_NAME
RIGHT JOIN
(
SELECT
TO_CHAR(ADD_MONTHS(TO_DATE('201208','YYYYMM'),
(ROWNUM-1))
,'YYYYMM') MONTH
FROM DUAL
CONNECT BY LEVEL<=
MONTHS_BETWEEN(TO_DATE('201302','YYYYMM') ,
TO_DATE('201208','YYYYMM'))+1
) MonthSeq
ON TABLE_NAME.MONTH=MonthSeq.MONTH
ORDER BY MonthSeq.MONTH
SQLFiddle demo
UPD:
Your query from the comment should looks like the following. You should move WHERE condition to the JOIN ON. If you use it in WHERE you don't get rows with zero counts.
SELECT MonthSeq.MONTH,
NVL(SUM(TOTAL_SESSIONS),0) AS SESSIONS
FROM X
RIGHT JOIN
(
SELECT
TO_CHAR(ADD_MONTHS(TO_DATE('201208','YYYYMM'),
(ROWNUM-1))
,'YYYYMM') MONTH
FROM DUAL
CONNECT BY LEVEL<=
MONTHS_BETWEEN(TO_DATE('201302','YYYYMM') ,
TO_DATE('201208','YYYYMM'))+1
) MonthSeq
ON X.MONTH=MonthSeq.MONTH and X.acct_id = 'ABCD'
ORDER BY MonthSeq.MONTH
You need to use TO_DATE function to convert the month field to DATE format. Refer here for more in detail. Try like this,
SELECT TO_CHAR(TO_DATE(MONTH, 'YYYYMM'), 'YYYYMM') month, count
FROM TABLE_NAME
WHERE month BETWEEN TO_DATE('201208', 'YYYYMM') AND TO_DATE('201302', 'YYYYMM')
AND id = 'X'
ORDER BY TO_DATE(month, 'YYYYMM');

select two maximum values per person based on a column partition

Hi if I have the following table:
Person------Score-------Score_type
1 30 A
1 35 A
1 15 B
1 16 B
2 74 A
2 68 A
2 40 B
2 39 B
Where for each person and score type I want to pick out the maximum score to obtain a table like:
Person------Score-------Score_type
1 35 A
1 16 B
2 74 A
2 40 B
I can do this using multiple select statements, but this will be cumbersome, especially later on. so I was wondering if there is a function which can help me do this. I have used the parititon function before but only to label sequences in a table....
select person,
score_type,
max(score) as score
from scores
group by person, score_type
order by person, score_type;
With "partition function" I guess you mean window functions. They can indeed be used for this as well:
select person
score_type,
score
from (
select person,
score_type,
score,
row_number() over (partition by person, score_type order by score desc) as rn
from scores
) t
where rn = 1
order by person, score_type;
Using the max() aggregate function along with the grouping by person and score_type should do the trick.

SELECT record based upon dates

Assuming data such as the following:
ID EffDate Rate
1 12/12/2011 100
1 01/01/2012 110
1 02/01/2012 120
2 01/01/2012 40
2 02/01/2012 50
3 01/01/2012 25
3 03/01/2012 30
3 05/01/2012 35
How would I find the rate for ID 2 as of 1/15/2012?
Or, the rate for ID 1 for 1/15/2012?
In other words, how do I do a query that finds the correct rate when the date falls between the EffDate for two records? (Rate should be for the date prior to the selected date).
Thanks,
John
How about this:
SELECT Rate
FROM Table1
WHERE ID = 1 AND EffDate = (
SELECT MAX(EffDate)
FROM Table1
WHERE ID = 1 AND EffDate <= '2012-15-01');
Here's an SQL Fiddle to play with. I assume here that 'ID/EffDate' pair is unique for all table (at least the opposite doesn't make sense).
SELECT TOP 1 Rate FROM the_table
WHERE ID=whatever AND EffDate <='whatever'
ORDER BY EffDate DESC
if I read you right.
(edited to suit my idea of ms-sql which I have no idea about).