I am having problem to fetch query in which i have a check user score in range to display the grade if user score between 75 and 100 then its A. If user score between 60- 75 then its B and .. so on .
I am getting this values
CASE users.points_earned
WHEN 75-100 THEN
'A+'
WHEN 60-75 THEN
'A'
WHEN 40-60 THEN
'B+'
WHEN 1--40 THEN
'B'
ELSE
'Absent'
end as rank
buts not working how to check range in case statement of postgresql
You can use BETWEEN for check ranges.
WITH users(points_earned) as(
select 75
union all
select 90
union all
select 200
)
SELECT CASE
WHEN users.points_earned BETWEEN 40 AND 75 THEN 'A+'
WHEN users.points_earned BETWEEN 76 AND 100 THEN 'A'
ELSE 'Absent'
END as rank
FROM users
Related
I have a table containing data that has a column named id that looks like below:
id
value 1
value 2
value 3
1
244
550
1000
1
251
551
700
1
540
60
1200
...
...
...
...
2
19
744
2000
2
10
903
100
2
44
231
600
2
120
910
1100
...
...
...
...
I want to take 50 sample rows per id that exists but if less than 50 exist for the group to simply take the entire set of data points.
For example I would like a maximum 50 data points randomly selected from id = 1, id = 2 etc...
I cannot find any previous questions similar to this but have tried taking a stab at at least logically working through the solution where I could iterate and union all queries by id and limit to 50:
SELECT * FROM (SELECT * FROM schema.table AS tbl WHERE tbl.id = X LIMIT 50) UNION ALL;
But it's obvious that you cannot use this type of solution because UNION ALL requires aggregating outputs from one id to the next and I do not have a list of id values to use in place of X in tbl.id = X.
Is there a way to accomplish this by gathering that list of unique id values and union all results or is there a more optimal way this could be done?
If you want to select a random sample for each id, then you need to randomize the rows somehow. Here is a way to do it:
select * from (
select *, row_number() over (partition by id order by random()) as u
from schema.table
) as a
where u <= 50;
Example (limiting to 3, and some row number for each id so you can see the selection randomness):
setup
DROP TABLE IF EXISTS foo;
CREATE TABLE foo
(
id int,
value1 int,
idrow int
);
INSERT INTO foo
select 1 as id, (1000*random())::int as value1, generate_series(1, 100) as idrow
union all
select 2 as id, (1000*random())::int as value1, generate_series(1, 100) as idrow
union all
select 3 as id, (1000*random())::int as value1, generate_series(1, 100) as idrow;
Selection
select * from (
select *, row_number() over (partition by id order by random()) as u
from foo
) as a
where u <= 3;
Output:
id
value1
idrow
u
1
542
6
1
1
24
86
2
1
155
74
3
2
505
95
1
2
100
46
2
2
422
33
3
3
966
88
1
3
747
89
2
3
664
19
3
In case you are looking to get 50 (or less) from each group of IDs then you can use windowing -
From question - "I want to take 50 sample rows per id that exists but if less than 50 exist for the group to simply take the entire set of data points."
Query -
with data as (
select row_number() over (partition by id order by random()) rn,
* from table_name)
select * from data where rn<=50 order by id;
Fiddle.
Your description of trying to get the UNION ALL without specifying all the branches ahead of time is aiming for a LATERAL join. And that is one way to solve the problem. But unless you have a table of all distinct ids, you would have to compute one on the fly. For example (using the same fiddle as Pankaj used):
with uniq as (select distinct id from test)
select foo.* from uniq cross join lateral
(select * from test where test.id=uniq.id order by random() limit 3) foo
This could be either slower or faster than the Window Function method, depending on your system and your data and your indexes. In my hands, it was quite a bit faster even with the need to dynamically compute the list of distinct ids.
I use PostgreSQL database and have a cards table.
Each record(card) in this table have card_drop_rate integer value.
For example:
id | card_name |card_drop_rate
-------------------------------
1 |card1 |34
2 |card2 |16
3 |card3 |54
max drop rate is 34 + 16 + 54 = 104.
In accordance to my application logic I need to find a random value between 0 and 104 and then retrieve card according to this number, for example:
random value: 71
card1 range: 0 - 34(0 + 34)
card2 range: 34 - 50(34 + 16)
card3 range: 50 - 104(50 + 54)
So, my card is card3 because 71 is placed in the range 50 - 104
What is the proper way to reflect this structure in PostgreSQL ? I'll need to query this data often under so the performance is a criterion number one for this solution.
Following query works fine:
SELECT
b.id,
b.card_drop_rate
FROM (SELECT a.id, sum(a.card_drop_rate) OVER(ORDER BY id) - a.card_drop_rate as rate, card_drop_rate FROM cards as a) b
WHERE b.rate < 299 ORDER BY id DESC LIMIT 1
You can do this using cumulative sums and random. The "+ 1"s might be throwing me off, but it is something like this:
with c as (
select c.*,
sum(card_drop_rate + 1) - card_drop_rate as threshhold
from cards c
),
r as (
select random() * (sum(card_drop_rate) + count(*) - 1) as which_card
from cards c
)
select c.*
from c cross join
r
where which_card >= threshhold
order by threshhold
limit 1;
For performance, I would simply take the cards and generate a new table with 106 slots. Assign the card value to the slots and build an index on the slot number. Then get a value using:
select s.*
from slots s
where s.slotid = floor(random() * 107);
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.
First image is my query output. Now I want to group the subject so that it become like the second image. Is it possible? Thanks for the help.
select Subject, Grade,
case when Grade >= 50
Then '1'
else '0'
end as Pass,
case when Grade < 50
Then '1'
else '0'
end as Fail
from Grade_report
OUTPUT:
what I want is:
Your specification is not very exact; what number do you expect in the grade on the merged record, and should it just write 1 or 0 or aggregate the sum of the pass and fail columns?
The following will generate your output, it takes the MAX for the grade and SUM for pass/fail information:
WITH GradePassFail AS (
SELECT
Subject,
Grade,
CASE WHEN Grade >= 50 THEN 1 ELSE 0 END AS Pass,
CASE WHEN Grade < 50 THEN 1 ELSE 0 END AS Fail
FROM Grade_report
)
SELECT Subject, MAX(Grade) AS Grade, SUM(Pass) AS Pass, SUM(Fail) AS Fail
FROM GradePassFail
GROUP BY Subject
How do I write a query to update my rows 'S'. I would like to update the current Primary key to change the number of it. I am not sure if I need a sub-query.
Would updating score, use the same method to update "S"?
"S" indicates student number, "C" indicates course number.
My tried but failed query is
update grades set s = 114 where s = 100;
The 'set s' is what I want to update into and the 'where s' is looking for the row with that set 's' number. Right?
Here is my query, trigger, and select * from grades looks like.
create table grades (
S varchar2(12),
C varchar2(10),
Score number(3,0),
Letter_Grade char(1),
Constraint pk_grades primary key (S),
Constraint CK_grades check (score between 0 and 100)
constraint lg_grades check (letter_grade in ('A','B','C','D','F'))
);
SET SERVEROUTPUT ON
CREATE OR REPLACE TRIGGER MARK_BU
BEFORE UPDATE OF score ON grades
FOR EACH ROW
DECLARE
BEGIN
:NEW.letter_grade :=
CASE
WHEN :NEW.score >= 80 THEN 'A'
WHEN :NEW.score >= 70 THEN 'B'
WHEN :NEW.score >= 60 THEN 'C'
WHEN :NEW.score >= 50 THEN 'D'
ELSE 'F'
END ;
DBMS_OUTPUT.PUT_LINE ('Numeric_grade was updated to: ' || :NEW.score);
DBMS_OUTPUT.PUT_LINE ('Letter_grade was calculated to be:' || :NEW.letter_grade);
END;
S C SCORE L
------------ ---------- ---------- -
100 CST8255 49 F
101 CST8255 59 D
102 CST8255 69 C
103 CST8255 79 B
104 CST8255 89 A
update grades set s = 114 where s = 100;
Is correct it will work.