calculation of points for each student - sql

I want to calculate the remaining points.
in calculation formula : total points =TotalPoints - plenty + Bonus
total points 100
eg. 1
st_id
plenty
Bonus
remaining Points
1
5
0
95
1
3
1
93
1
2
0
91
1
3
2
90
2
1
0
99
2
3
1
96
2
2
0
94
first row calculation for st_id 1: 100 -5 +0= 95
second row calculation for st_id1: 95 - 3 +1=93 here i have to take the previous remaining points. and so on...
can i make a function or use cte to calculate remaining point.
or any other solution for this problem.

SQL tables represent unordered sets (technically "multisets"). There is no ordering unless a column specifies the ordering.
Your results depend on an ordering, so let me assume that you have such a column.
The solution is then cumulative sums:
select t.*,
100 - sum(plenty - bonus) over (partition by st_id order by <ordering col>) as remaining
from t;

I am bit confused, how did you get 100 for the first row in formula "first row calculation for st_id 1: 100 -5 +0= 95" and 95 for second formula "st_id1: 95 - 3 +1=93". Finally, I anticipated that, you might have a column named TotalPoint in your table, so, I write a code. Please Check and let me know->
DECLARE #Points table(st_id int,plenty int,Bonus int,TotalPoints int);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (1, 5,0,100);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (1, 3,1,95);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (1, 2,0,91);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (1, 3,0,90);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (2, 1,0,99);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (2, 3,1,96);
INSERT INTO #Points (st_id, plenty,Bonus,TotalPoints) VALUES (2, 2,0,94);
Select *,(TotalPoints-plenty-Bonus) AS RemainingPoint FROM #Points

Related

SQL own sort order

How to sort values (rows with 2 elements) whereby the second colum is the link to the previous row/first column whereby the values are numeric but not sortable on a normal numeric value order/calculated one, example:
1 - 0 no previous row
3 - 1 1 in second colum refers to 1 in previous row in first column
4 - 3 3 in second colum refers to 3 in previous row in first column
2 - 4 ...
9 - 2
7 - 9
5 - 7
6 - 5
8 - 6
sort result should be: 1 - 3 - 4 - 2 - 9 - 7 - 5 - 6 - 8
The tricky part is the unknown sequence, so case, decode, if, ... can't be included because the values are unknown (the only one who is known is the first row by 0 in the second column)
Does someone has already solved such a sortorder?
create table #t (
valor int null
, valor2 int null
, linha int not null identity
)
insert into #t (valor) values (1)
insert into #t (valor) values (3)
insert into #t (valor) values (4)
insert into #t (valor) values (2)
insert into #t (valor) values (9)
insert into #t (valor) values (7)
insert into #t (valor) values (5)
insert into #t (valor) values (6)
insert into #t (valor) values (8)
update #t set #t.valor2 = isnull(t2.valor, 0)
from (
select valor, linha +1 as linha
from #t
) t2
where #t.linha = t2.linha
select valor, valor2, linha from #t order by linha
select valor, isnull(valor2, 0) as valor2 from #t order by 2
drop table #t
The result is something like:

How to generate batch number in SQL Server

Any idea on how to generate batch numbers into a batch_number column in SQL Server table from 1 to 3 and repeat it again as shown below in the script?
Thanks in advance
Declare #mTestTable table
(
id int,
city varchar(50),
batch_number int
)
--Insert some sample data
Insert into #mTestTable values (1, 'London')
Insert into #mTestTable values (2, 'Karachi')
Insert into #mTestTable values (3, 'New York')
Insert into #mTestTable values (4, 'Melbourne')
Insert into #mTestTable values (5, 'Beijing')
Insert into #mTestTable values (6, 'Tokyo')
Insert into #mTestTable values (7, 'Moscow')
I need to generate batch number from 1 to 3 and repeat it again.
Id City Batch_Number
1 London 1
2 Karachi 2
3 New York 3
4 Melbourne 1
5 Beijing 2
6 Tokyo 3
7 Moscow 1
Use row_number() and some arithmetic:
with toupdate as (
select t.*, 1 + ((row_number() over (order by id) - 1) % 3) as new_batch_no
from testtable
)
update toupdate
set batch_number = new_batch_number;
update #mTestTable
set batch_number = (id-1) % 3 + 1
If the IDs won't be sequential, then one way to do it is using a cursor passing through it. Simply count the rows one by one and update the appropriate batch number.

Find the Range from a Table

I have a table with the following values
id Level Threshold
1 1 5000
2 2 10000
3 3 15000
What i need to achieve is that when i pass 6000 , I need to get Level 1.
12000 Level 2 and 16000 Level 3?
6000 - Level 1
12000 - Level 2
16000 - Level 3
Can someone let me know how this can be achieved?
What I understood from your question is that when user will give 6000 then it should check which value is less than 6000 so it's 5000 and it's level is 1 same as when 12000 so it has two output as 5000 (level1) and 10000 (level2) but you need maximum one so it is 10000 (Level2). So according to this understanding the query is :
select max(LEVEL) from Table where Threshold< 6000;
How about an SQL question like this?
SELECT One.Level, One.Threshold
FROM
TableName AS One,
(SELECT MAX(Threshold) AS Maximum FROM TableName WHERE Threshold <= :value) AS Two
WHERE One.Threshold = Two.Maximum
Replace :value with 6000, 12000, 16000 or whatever value you are interested in. The inner query finds the maximum threshold that the value has reached. The outer query returns the level number for threshold.
Disclaimar: I have not tested this.
Try Below example, might be you want below one
create table #temp (id int, value int)
insert into #temp values (1, 6000)
insert into #temp values (2, 12000)
insert into #temp values (3, 15000)
insert into #temp values (4, 16000)
select * from #temp
select id, ceiling(convert(float,value)/6000) as level, value from #temp
Fiddle Demo
create table temp (id int, level int, Threshold int);
insert into temp values (1,1, 5000);
insert into temp values (2,2, 10000);
insert into temp values (3,3, 15000);
select max(LEVEL) from temp where Threshold<= 8000; (8000 or any other value)

SQL Query to return rows where a list of numbers is between start and end values

There is a table in Oracle with the columns:
id | start_number | end_number
---+--------------+------------
1 | 100 | 200
2 | 151 | 200
3 | 25 | 49
4 | 98 | 99
5 | 49 | 100
There is a list of numbers (50, 99, 150).
I want an sql statement that returns all the ids where any of the numbers in the list of numbers is found equal to or between the start_number and the end_number.
Using the above example; 1, 4 and 5 should be returned.
1 - 150 is between or equal to 100 and 200
2 - none of the numbers are between or equal to 151 and 200
3 - none of the numbers are between or equal to 25 and 49
4 - 99 is between or equal to 98 and 99
5 - 50 and 99 are between or equal to 49 and 100
drop table TEMP_TABLE;
create table TEMP_TABLE(
THE_ID number,
THE_START number,
THE_END number
);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (1, 100, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (2, 151, 200);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (3, 25, 49);
insert into TEMP_TABLE(THE_ID, THE_START, THE_END) values (4, 98, 99);
insert into TEMP_TABLE(the_id, the_start, the_end) values (5, 49, 100);
The following is the solution I came up with based on the comments and answers below plus some additional research:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(SYS.DBMS_DEBUG_VC2COLL(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
This works too:
SELECT
*
from
TEMP_TABLE
where
EXISTS (select * from(
select column_value as id
from table(sys.ku$_vcnt(50,99,150))
)
where id
BETWEEN TEMP_TABLE.the_start AND TEMP_TABLE.the_end
)
Here is a full example:
create table #list (
number int
)
create table #table (
id int,
start_number int,
end_number int
)
insert into #list values(50)
insert into #list values(99)
insert into #list values(150)
insert into #table values(1,100,200)
insert into #table values(2,151,200)
insert into #table values(3,25,49)
insert into #table values(4,98,99)
insert into #table values(5,49,100)
select distinct a.* from #table a
inner join #list l --your list of numbers
on l.number between a.start_number and a.end_number
drop table #list
drop table #table
You'll simply need to remove the code about #table (create, insert and drop) and put your table in the select.
It partly depends on how your are storing your list of numbers. I'll assume that they're in another table for now, as even then you have many options.
SELECT
*
FROM
yourTable
WHERE
EXISTS (SELECT * FROM yourList WHERE number BETWEEN yourTable.start_number AND yourTable.end_number)
Or...
SELECT
*
FROM
yourTable
INNER JOIN
yourList
ON yourList.number BETWEEN yourTable.start_number AND yourTable.end_number
Both of those are the simplest expressions, and work well for small data sets. If your list of numbers is relatively small, and your original data is relatively large, however, this may not scale well. This is because both of the above scan the whole of yourTable and then check each record against yourList.
What may be preferable is to scan the list, and then attempt to use indexes to check against the original data. This would require you to be able to reverse the BETWEEN statement to yourTable.start_number BETWEEN x and y
This can only be done if you know the maximum gap between start_number and end_number.
SELECT
*
FROM
yourList
INNER JOIN
yourTable
ON yourTable.end_number >= yourList.number
AND yourTable.start_number <= yourList.number
AND yourTable.start_number >= yourList.number - max_gap
To achieve this I would store the value of max_gap in another table, and update it as the values in yourTable change.
You will want to create a temporary table to hold your numbers, if the numbers aren't already in one. Then it becomes relatively simple:
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN TempTable tt --your list of numbers
ON tt.number Between mt.start_number and mt.end_number
To create the table based on an array of passed values, you can use table definitions in your procedure. I'm light on Oracle syntax and don't have TOAD handy, but you should be able to get something like this to work:
CREATE OR REPLACE PROCEDURE FindIdsFromList
AS
DECLARE
TYPE NumberRecord IS RECORD (Number int NOT NULL)
TYPE NumberList IS TABLE OF NumberRecord;
NumberList myNumberList;
BEGIN
myNumberList := (50,99,150);
SELECT DISTINCT mt.ID FROM MyTable mt
INNER JOIN myNumberList nt --your list of numbers
ON nt.Number Between mt.start_number and mt.end_number
END

SQL: Test if Number Contains Digits of Another Number

I'm interested in using SQL to test if one number, stored in column DOW, contains the digits of another number, also stored in DOW, regardless of separation by other digits. Here are the current numbers with which I'm working, although I may have more to deal with in the future:
23
236
1234
12346
123456
67
If the query checks 123456 against 236, it needs to return true. Vice versa, 236 against 123456 returns false. Another example is 1234 returns true when checked against 23, but 67 returns false when checked against 12346. If I've not provided enough information in this question, please ask for clarification.
Simplified Version of my Query:
SELECT t1.DOW, t2.DOW
FROM table t1, table t2
WHERE /* t2.DOW contains all digits regardless of separation in t1.DOW */
Thanks!
I assume DOW is Day of Week and only has numbers 1-7
SELECT T1.DOW,T2.DOW
FROM T1,T2
WHERE NOT EXISTS(SELECT *
FROM (VALUES('%1%'),
('%2%'),
('%3%'),
('%4%'),
('%5%'),
('%6%'),
('%7%')) Nums(N)
WHERE (T2.DOW LIKE N AND T1.DOW NOT LIKE N))
SQL Server 2008 answer though AFAIK this is standard SQL.
This can be handled as a stored procedure which turns a value into a string and inspects the string for each of the digits in the pattern value.
The guts of the stored procedure would have something like
function has_digits (pattern, value)
string s = string (value)
count = 0
for each char in pattern
if instr (s, char) > 0 // for mysql
count = count + 1
return count == pattern.length()
(The specifics of this greatly depend on which SQL flavor is used.)
This would be used like
SELECT *
FROM sometable
WHERE has_digits (236, somefield);
First, I want to make sure everybody understands that I firmly believe this should be in a stored procedure. Also, that I will probably spend most of tomorrow trying to figure out a suitable punishment for what I'm about to write.
Let's assume we have two tables. The first table contains the digits we want to search for.
CREATE TABLE search_digits (
digit integer NOT NULL
);
INSERT INTO search_digits VALUES (2), (3);
We're going to search for the digits 2 and 3.
The second table contains the values we want to search within.
CREATE TABLE dow_digits (
dow integer NOT NULL,
digit integer NOT NULL,
CONSTRAINT dow_digits_pkey PRIMARY KEY (dow, digit),
);
INSERT INTO dow_digits VALUES (23, 2);
INSERT INTO dow_digits VALUES (23, 3);
INSERT INTO dow_digits VALUES (236, 2);
INSERT INTO dow_digits VALUES (236, 3);
INSERT INTO dow_digits VALUES (236, 6);
INSERT INTO dow_digits VALUES (1234, 1);
INSERT INTO dow_digits VALUES (1234, 2);
INSERT INTO dow_digits VALUES (1234, 3);
INSERT INTO dow_digits VALUES (1234, 4);
INSERT INTO dow_digits VALUES (12346, 1);
INSERT INTO dow_digits VALUES (12346, 2);
INSERT INTO dow_digits VALUES (12346, 3);
INSERT INTO dow_digits VALUES (12346, 4);
INSERT INTO dow_digits VALUES (12346, 6);
INSERT INTO dow_digits VALUES (123456, 1);
INSERT INTO dow_digits VALUES (123456, 2);
INSERT INTO dow_digits VALUES (123456, 3);
INSERT INTO dow_digits VALUES (123456, 4);
INSERT INTO dow_digits VALUES (123456, 5);
INSERT INTO dow_digits VALUES (123456, 6);
INSERT INTO dow_digits VALUES (67, 6);
INSERT INTO dow_digits VALUES (67, 7);
We can find at least some of the values for dow that contain the digits 2 and 3 with a simple query.
select d1.dow from dow_digits d1
inner join search_digits d2 on d1.digit = d2.digit
group by dow
having count(distinct d1.digit) = (select count(distinct digit)
from search_digits);
dow
--
23
236
1234
12346
123456
That seems to work. It's not clear what the OP expects if the search integer is 233, so I'm going to ignore that case for now. I want to finish this quickly, then step in front of a truck.
The next question is, can we build search_digits on the fly? In PostgreSQL, sort of.
SELECT UNNEST(ARRAY[2,3]) as digit;
digit
--
2
3
Drop the table search_digits, and wrap that in a CTE.
with search_digits as (
select unnest(array[2,3]) as digit
)
select d1.dow from dow_digits d1
inner join search_digits d2 on d1.digit = d2.digit
group by dow
having count(distinct d1.digit) = (select count(distinct digit)
from search_digits);
dow
--
23
236
1234
12346
123456
Next question. Can we build dow_digits on the fly? In PostgreSQL, sort of. Need to know how many digits in the longest number. Let's say no more than six.
select dow, digit
from (select dow, unnest(array[substring((dow)::text from 1 for 1),
substring((dow)::text from 2 for 1),
substring((dow)::text from 3 for 1),
substring((dow)::text from 4 for 1),
substring((dow)::text from 5 for 1),
substring((dow)::text from 6 for 1)]) digit
from dow ) d
where d.digit <> '';
dow digit
--
23 2
23 3
236 2
236 3
236 6
1234 1
1234 2
1234 3
1234 4
12346 1
12346 2
12346 3
12346 4
12346 6
123456 1
123456 2
123456 3
123456 4
123456 5
123456 6
67 6
67 7
233 2
233 3
233 3
Pulling all that together into a single statement . . .
with search_digits as (
select unnest(array[1,2,3,4,6]) digit
)
select dow
from (select dow, digit
from (select dow, unnest(array[substring((dow)::text from 1 for 1),
substring((dow)::text from 2 for 1),
substring((dow)::text from 3 for 1),
substring((dow)::text from 4 for 1),
substring((dow)::text from 5 for 1),
substring((dow)::text from 6 for 1)]) digit
from dow
) arr
where arr.digit <> ''
) d
inner join (select distinct digit from search_digits) sd
on sd.digit = d.digit::integer
group by dow
having count(distinct d.digit) = (select count(distinct digit)
from search_digits)
dow
--
12346
123456
Oh, I can feel karma points slipping away . . . where's that truck?
For MySQL only:
CREATE TABLE num --- a help table
( i INT PRIMARY KEY
) ;
INSERT INTO num
VALUES
(1), (2), (3), (4)
, (5), (6), (7), (8)
, (9),(10),(11),(12)
,(13),(14),(15),(16)
,(17),(18),(19),(20) ; --- maximum number of digits = 20
Then, you can use this (horrible) query, which first converts numbers to strings and add % between each digit (23 would be converted to '%2%3%') and then tests WHERE 23456 LIKE '%2%3% to match if 23456 contains 23:
SELECT t1.DOW
, t2.DOW
, (t1.DOW LIKE test) AS t1_contains_t2
FROM t1
JOIN
( SELECT t2.DOW
, CONCAT( '%'
, GROUP_CONCAT( SUBSTRING( t2.DOW, num.i, 1 )
ORDER BY num.i
SEPARATOR '%' )
, '%'
) AS test
FROM t2
JOIN num
ON (SUBSTRING(t2.DOW,num.i,1) != "" )
GROUP BY t2.DOW
) AS temp ;
The t1_contains_t2 column will have 1 or 0 depending on whether t1.DOW contains t2.DOW
Or you can use WHERE (t1.DOW LIKE test) in the query.