Table structure of a student - sql

I want a table structure which can store the details of the student like the below format.
If the student is in
10 th standard -> I need his aggregate % from 1st standard to 9th standard.
5 th standard -> I need his aggregate % from 1st standard to 4th standard.
1 st standard -> No aggregate % has to be displayed.
And the most important thing is ' we need to use only one table'. Please form a table structure with no redundant values.
Any ideas will be greatly appreciated......
No friends this is not a home work. This is asked in Oracle interview, conducted in Hyderabad day before yesterday '24th July, 2010',. He asked me the table structure.
He even did not asked me the query. He asked me how I will design the table. Please advice me.

id | name | grade | aggregate
This would do the trick, id is your primary key, name is students first last name, grade is what grade he is in and aggregate is aggregate % based on the grade.
Fro example some rows might be:
10 | Bill Cosby | 10 | 90
11 | Jerry Seinfeld | 4 | 60
Bill Cosby would have aggregate percent of 90 in grades 1-9, and jerry would have 60 in grades 1-3. In this case it is one table and boils down to you managing the rule of aggregation for this table, since it has to be one table.

If this is an interview question, it looks like they would like to check your knowledge on Nested Tables. Essentially you would have one column as roll number, and other column which is a nested table as Class and Percentage.

Related

Keeping an updated tally of changing records

I have a list of students and their subjects:
id | student | subject
---|---------|--------
1 | adam | math
2 | bob | english
3 | charlie | math
4 | dan | english
5 | erik | math
And I create a tally from the above list aggregating how many students are there in each subject:
id | subject | students
---|---------|--------
1 | math | 3
2 | english | 2
The student list will keep on expanding and this aggregation will be done at regular intervals.
The reason I'm keeping the Tally in a separate table in the first place is because the original table is supposed to be massive (this is just a simplification of my original problem) and so querying the original table for a current tally on-the-fly is unfeasible to do quickly enough.
Anyways, so the aggregating is pretty straight forward as long as the students don't change their subject.
But now I want to add a feature to allow students to change their subject.
My previous approach was this: while updating the Tally, I keep a counter variable up to which row of students I've already accounted for. Next time I only consider records added after that row.
Also the reason why I keep a counter is because the Students table is massive, and I don't want to scan the whole table every time as it won't scale well.
It works fine if all students are unique and no one changes their subject.
But it breaks apart now because I can no longer account for rows that come before the counter and were updated.
My second approach was using a updated_at field (instead of counter) and keep track of newly modified rows that way.
But still I don't know how to actually update the Tally accurately.
Say, Erik changes his subject from "math" to "english" in the above scenario. When I run the script to update the Tally, while it does finds the newly updated row but it simply says {"erik": "english"}. How would I know what it changed from? I need to know this to correctly decrement "math" in the Tally table while incrementing "english".
Is there a way this can be solved?
To summarize my question again, I want to find a way to be able to update the Tally table accurately (a process that runs at regular interval) with the updated/modified rows in the Student table.
I'm using NodeJS and PostgreSQL if it matters.
Why don't you do it when student add subject, remove subject, or change subject.
When student add new subject: Just increase UPDATE tbl_tally SET student = student + 1 WHERE subject = :subject;
When student remove subject: Just decrease UPDATE tbl_tally SET student = student - 1 WHERE subject = :subject;
When student change subject: Just increase new subject by one and decrease old subject by one
UPDATE tbl_tally SET student = student - 1 WHERE subject = :old_subject;
UPDATE tbl_tally SET student = student + 1 WHERE subject = :new_subject;
I am not familiar with PostgreSQL, but in MySQL, you can even do it with trigger. I think PostgreSQL also has trigger.

Taking average of two rows and adding third row of same column in sql

I'm doing a simple student marks report application and trying to create a View Object through sql statement from two entity object in the database.
StudentDetails -> rollno, name.
marks -> rollno, internal, sub1, sub2, sub3
there are two exams for the students i.e. internal=1 and internal=2 and internal=3 is for assignment submission marks.
snapshot of marks table is below. DRAWING table as i'm not able to attach snapshot here.
select * from marks shows like this:
---------------------------------
ROLLNO | INTERNAL | SUB1 | SUB2 |
1 | 1 | 12 | 15 |
1 | 2 | 15 | 17 |
1 | 3 | 2 | 5 |
2 | 1 | 10 | 14 |
--------------------------------
For calculating aggregate marks we have to avg marks from internal=1 and internal=2 and add marks of internal=3 to it for every rollno(student). Can someone please help me in framing this query.
It is unclear from your question exactly what is being averaged to give the result, but here is an attempt which you can build on.
I will break up your question into parts.
For calculating aggregate marks we have to avg marks - so we will be using the AVG( ) function.
from internal=1 and internal=2 - so that will go in the WHERE clause as WHERE internal=1 or internal=2.
and add marks of internal=3 to it tricky - will need to treat the rows with internal=3 as a separate table and use JOIN to link them.
for every rollno(student) so, we are combining rows by student, which requires a GROUP BY.
Here is the final statement:
select m1.rollno, avg(m1.sub1+m1.sub2)+m2.sub1+m2.sub2 as result
from marks as m1
inner join marks as m2
on m1.rollno = m2.rollno
where m1.internal in (1,2)
and m2.internal = 3
group by rollno
Here is a working example for you to experiment on: http://sqlfiddle.com/#!2/9ed4e0/1
The way it is averaging and adding may be incorrect, but it was difficult to discern from your question what you really needed. It should be easy to change this part.
Note that since rows with internal=3 represent a different type of data they should be in a different table unless you have the experience to be certain what you are doing is workable.

Summing n numerical variables by grouping level specific to each

I am working through a group by problem and could use some direction at this point. I want to summarize a number of variables by a grouping level which is different (but the same domain of values) for each of the variables to be summed. In pseudo-pseudo code, this is my issue: For each empYEAR variable (there are 20 or so employment-by-year variables in wide format), I want to sum it by the county in which the business was located in that particular year.
The data is a bunch of tables representing business establishments over a 20-year period from Dun & Bradstreet/NETS.
More details on the database, which is a number of flat files, all with the same primary key.
The primary key is DUNSNUMBER, which is present in several tables. There are tables detailing, for each year:
employment
county
sales
credit rating (and others)
all organized as follows (this table shows employment, but the other variables are similarly structured, with a year postfix).
dunsnumber|emp1990 |emp1991|emp1992|... |emp2011|
a | 12 |32 |31 |... | 35 |
b | |2 |3 |... | 5 |
c | 1 |1 | |... | |
d | 40 |86 |104 |... | 350 |
...
I would ultimately like to have a table that is structured like this:
county |emp1990|emp1991|emp1992|...|emp2011|sales1990|sales1991|sales1992|sales2011|...
A
B
C
...
My main challenge right now is this: How can I sum employment (or sales) by county by year as in the example table above, given that county as a grouping variable changes sometimes by the year and specified in another table?
It seems like something that would be fairly straightforward to do in, say, R with a long data format, but there are millions of records, so I prefer to keep the initial processing in postgres.
As I understand your question this sounds relatively straight forward. While I normally prefer normalized data to work with, I don't see that normalizing things beforehand will buy you anything specific here.
It seems to me you want something relatively simple like:
SELECT sum(emp1990), sum(emp1991), ....
FROM county c
JOIN emp e ON c.dunsnumber = e.dunsnumber
JOIN sales s ON c.dunsnumber = s.dunsnumber
JOIN ....
GROUP BY c.name, c.state;
I don't see a simpler way of doing this. Very likely you could query the system catalogs or information schema to generate a list of columns to sum up. the rest is a straight group by and join process as far as I can tell.
if the variable changes by name, the best thing to do in my experience is to put together a location view based on that union and join against it. This lets you hide the complexity from your main queries and as long as you don't also join the underlying tables should perform quite well.

Access Join Query

I have two tables tbl_DaysWeeksMonths (Left Table) and tbl_Telephony (Right Table). tbl_DaysWeeksMonths has a record of every day of the year with columns (Row_Date/Week/Month) whilst tbl_Telephony has telephony data for hundreds of agents by day with columns (row_date/agent/calls/talk time)(Note: Each agent only has records for 5-6 days of the week instead of everyday).
I want to join the two tables so that each agent has a record for every day of the week regardless if they took calls on a day or not. I want to display blank records (except for the date field) for days which consultants did not take calls. E.g:
## Date ## ## Agent ## ## Calls ## ## Talk Time ##
1. 26/05/2012 | James | 40 | 560
2. 27/05/2012 | James | |
3. 28/05/2012 | James | 34 | 456
4. 29/05/2012 | James | |
5. 30/05/2012 | James | 40 | 643
6. 31/05/2012 | James | 36 | 345
7. 01/06/2012 | James | 31 | 160
I'm trying to use the below code but I don't think it's correct. Any suggestions on a better code to use. Please help.
SELECT tbl_DaysWeeksMonths.Row_Date,
[tbl_Telephony].Consultant,
[tbl_Telephony].i_acdtime
FROM tbl_DaysWeeksMonths
LEFT JOIN [tbl_Telephony]
ON tbl_DaysWeeksMonths.Row_Date = [tbl_Telephony].row_date;
Step 1 -- Assuming you have a table (say, named Consultants) that lists each distinct consultant, create a query (say, named Consultant Days) that generates all possible combinations of days and consultants. It might look something like this:
SELECT [Consultants].Consultant, tbl_DaysWeeksMonths.Row_Date
FROM [Consultants], tbl_DaysWeeksMonths;
If you don't have a Consultants table, you could substitute it with a query that selects the distinct consultants listed in tbl_Telephony. In other words, you can create a Consultants query that looks something like this:
SELECT DISTINCT Consultant FROM [tbl_Telephony];
Step 2 -- Create a query that outer joins tbl_Telephony to Consultant Days. It might look something like this:
SELECT [Consultant Days].Row_Date, [Consultant Days].Consultant, [tbl_Telephony].i_acdtime
FROM [Consultant Days]
LEFT JOIN [tbl_Telephony]
ON [Consultant Days].Consultant = [tbl_Telephony].Consultant
AND [Consultant Days].Row_Date = [tbl_Telephony].row_date;
This also assumes that the row_date values in tbl_Telephony match the Row_Date values in tbl_DaysWeeksMonths -- in other words, that row_date values in tbl_Telephony are whole days (that is, do not contain a time-of-day component). This also assumes that i_acdtime values in tbl_Telephony are the total talk time for the given consultant and day (as opposed to the talk time for a given call). Presumably there is another column in tbl_Telephony that will give the the total number of cals for the given consultant and day which you could add to the query to get the "Calls" column you said you wanted your question.
You can also use this query.
SELECT tbl_DaysWeeksMonths.Row_Date, [tbl_Telephony].Consultant, [tbl_Telephony].i_acdtime
FROM tbl_DaysWeeksMonths
LEFT OUTER JOIN [tbl_Telephony] ON tbl_DaysWeeksMonths.Row_Date = [tbl_Telephony].row_date;

Rows to Dynamic columns in Access

I need a setup in Access where some rows in a table are converted to columns...for example, lets say I have this table:
Team Employee DaysWorked
Sales John 23
Sales Mark 3
Sales James 5
And then through the use of a query/form/something else, I would like the following display:
Team John Mark James
Sales 23 3 5
This conversion of rows to columns would have to be dynamic as a team could have any number of Employees and the Employees could change etc. Could anyone please guide me on the best way to achieve this?
You want to create a CrossTab query. Here's the SQL that you can use.
TRANSFORM SUM(YourTable.DaysWorked) AS DaysWorked
SELECT YourTable.Team
FROM YourTable
GROUP BY YourTable.Team
PIVOT YourTable.Employee
Of course the output is slightly different in that the columns are in alphabetical order.
Team James John Mark
Sales 5 23 3
For more detail see Make summary data easier to read by using a crosstab query at office.microsoft.com