How to retrieved specific data from three different tables - sql

i have 3 tables, every table contains the different information of student(e.g personal detail, course detail, academic details etc), in which students have 4 different category(SC, ST, OBC and Gen). i want to retrieve the student list according to there category and Plus2Percentage.
eg.
1. i want to retrieve 2 students from SC category whose Plus2Percentage is >= 60,
then
i want to retrieve 2 students from ST category whose Plus2Percentage is >= 65,
then
i want to retrieve 1 students from OBC category whose Plus2Percentage is >= 60,
then
i want to retrieve 2 students from All 4(SC, ST, OBC and Gen) category whose Plus2Percentage is >= 70, but in this i dont want to retrieve those students info who has been already retrieved.(e.g those two student from SC Category who has been already retrieved from very step and so on from the ST OBC category)
[Table1]:
| Roll No | Applicant Name| Gender | Category | Father's Name |
|------------|---------------|------------|------------|----------------|
| 001 | A | M | SC | as |
| 002 | B | F | ST | hg |
| 003 | C | F | ST | yj |
| 004 | D | M | OBC | uy |
| 005 | E | F | SC | bn |
| 006 | F | M | OBC | kl |
| 007 | E | F | Gen | bn |
| 008 | F | M | OBC | vg |
| 009 | E | F | Gen | gh |
| 010 | F | M | SC | we |
|------------|---------------|------------|------------|----------------|
[Table2]:
| ID | Semester | Major | Applied Course|
|------------|---------------|------------|---------------|
| 001 | 1 | English | B.A |
| 002 | 1 | English | B.A |
| 003 | 1 | History | B.A |
| 004 | 1 | botany | B.Sc |
| 005 | 1 | Hindi | B.A |
| 006 | 1 | History | B.A |
| 007 | 1 | Maths | B.A |
| 008 | 1 | Hindi | B.A |
| 009 | 1 | History | B.A |
| 010 | 1 | Pol.Science| B.A |
|------------|---------------|------------|---------------|
[Table3]:
| ID |Plus2Percentage|
|------------|---------------|
| 001 | 60 |
| 002 | 65 |
| 003 | 70 |
| 004 | 73 |
| 005 | 87 |
| 006 | 91 |
| 007 | 59 |
| 008 | 78 |
| 009 | 88 |
| 010 | 57 |
|------------|---------------|-
[Output]:
| Roll No |Plus2Percentage| Category |
|------------|---------------|-----------|
| 005 | 87 | SC |
| 001 | 60 | SC |
| 003 | 70 | ST |
| 002 | 65 | ST |
| 006 | 91 | OBC |
| 009 | 88 | Gen |
| 008 | 78 | OBC |
|------------|---------------|-----------|
2 Students from SC Category whose Percentage is above or equal to 60%.
Roll No 005 and 001 from sc.
2 Students from ST Category whose Percentage is above or equal to 65%.
Roll No. 002 and 003 from st.
1 Students from OBC Category whose Percentage is above or equal to 60%.
Roll No. 006 from OBC
and
2 Students from all category whose percentage is above 70%, but excluding previously retrieved students.
Roll No. 009 and 008 from all over
Previously working code,when i was retrieving data from1 table ,instead of 3 Tables:
WITH PRIMARY_CHOICE AS (
SELECT
RollNo,
ApplicantName,
FatherName,
Gender,
Major,
Category,
Plus2Percentage
FROM (
SELECT
RollNo,
ApplicantName,
FatherName,
Gender,
Semester,
Major,
AppliedCourse,
Category,
Plus2Percentage,
row_number() over (partition by Category, Semester, Major, AppliedCourse order by Plus2Percentage desc) as rn
FROM [College Management System].[dbo].[ApplicantPersonalDetail]
) as T
WHERE
rn <= CASE
WHEN Category='SC' AND Semester='1' AND AppliedCourse= 'B.A' AND Plus2Percentage >= '60' THEN '2'
WHEN Category='ST' AND Semester= '1' AND AppliedCourse= 'B.A' AND Plus2Percentage >= '65'THEN '2'
WHEN Category='OBC' AND Semester= '1' AND AppliedCourse= 'B.A' AND Plus2Percentage >= '60' THEN '1'
ELSE 0
END
)
SELECT
RollNo,
ApplicantName,
FatherName,
Gender,
Major,
Category,
Plus2Percentage
FROM PRIMARY_CHOICE
UNION ALL
SELECT
RollNo,
ApplicantName,
FatherName,
Gender,
Major,
Category,
Plus2Percentage
FROM (
SELECT
RollNo,
ApplicantName,
FatherName,
Gender,
Semester,
Major,
AppliedCourse,
Category,
Plus2Percentage,
row_number() over (partition by Semester, Major1, AppliedCourse order by Plus2Percentage desc) as rn
FROM [College Management System].[dbo].[ApplicantPersonalDetail] x
WHERE NOT EXISTS (
select 1 from primary_choice y
where x.RollNo = y.RollNo
)
) AS T2
WHERE
rn <= 2
AND Semester = #semester
AND AppliedCourse = 'B.A'
AND Plus2Percentage >= 70
order by Plus2Percentage desc

SELECT RollNo, Plus2Percentage, Category
FROM TABLE1 a
INNER JOIN Table3 b on a.rollno=b.id
WHERE a.category='SC' and b.Plus2Percentage>=60
That should be enough for the first three bullets. The fourth one is a matter of either setting up the predicate or encapsulating a union of the first three and then doing a NOT IN.
Although I do feel it difficult to believe that someone who knows enough SQL to use ROW_NUMBER cannot troubleshoot a JOIN.

I have achieved the output but the query cost is high, if you have lots of data it populating every mins into your database than this query might not support the execution time.
I have used CTE table and UNION clauses for generating your output -
I have not used your table2 because there is no data required from that table in the output or is not dependent on table2
SQL SELECT CODE - you can replace your table and column names accordingly -
EDIT AFTER THE COMMENTS :- SOLUTION IS CHANGED INTO A STROED PROCEDURE READING DATA FROM TABLE VARIABLE
CREATE PROCEDURE usp_SelectCategorywiseData
AS
BEGIN
SET NOCOUNT ON;
DECLARE #tbl_LIST TABLE (RollNo int, [Plus2Percentage] int, Category varchar(10));
WITH CTE AS
(
SELECT A.RollNo, P.Percentage AS [Plus2Percentage], A.Category
, row_number() OVER (PARTITION BY A.Category ORDER BY P.Percentage DESC) AS Rank
FROM APPLICANT A INNER JOIN Plus2Percentage P ON A.RollNo = P.ID
)
INSERT INTO #tbl_LIST
SELECT RollNo, Plus2Percentage as [Plus2Percentage], Category
FROM CTE
WHERE rank <=
CASE
WHEN Category='SC' AND [Plus2Percentage] >= '60' THEN '2'
WHEN Category='ST' AND [Plus2Percentage] >= '60' THEN '2'
WHEN Category='OBC' AND [Plus2Percentage] >= '55' THEN '1'
ELSE 0
End
INSERT INTO #tbl_LIST
SELECT TOP 2 A.RollNo, P.Percentage as [Plus2Percentage], A.Category
FROM APPLICANT A INNER JOIN Plus2Percentage P ON A.RollNo = P.ID
WHERE Percentage > 70 and RollNo NOT IN (SELECT RollNo FROM #tbl_LIST) ORDER BY P.Percentage DESC
SELECT * FROM #tbl_LIST
END
What you will need to do is - create a temp table which will hold the data of first 5 records for SC, ST, OBC Category
Then Insert the records for all the category TOP 2 records order by percentage desc where Rollno is not in temp table. so that duplicates will be excluded and you will get the data.
ORDER BY SHOULD NOT BE USED AS ORDERING THE DATA CAN BE DONE LATER - IT COST THE EXECUTION TIME.
I will explain the USE of all the clauses here:
CTE - used to identify the row number (Ranking the records based on Percentage and grouping on Category)
Using CTE to select the data with individual conditional checks for Category and min. percentage.
Using UNION to get only distinct records as the final output.

Related

Join & Group By - Column is invalid in the select list

I'm running a query to join 2 tables and then group the rows by 2 fields and select the rows with the min ID from these groups and I get an error. The joined table looks like:
+--------+---------+-----------------+-----------+-----------+
| ID | CODE | NAME | VRACHAR01 | VRACHAR02 |
+--------+---------+-----------------+-----------+-----------+
| 290861 | 1110896 | PRODUCT NAME XX | 001 | 706 |
| 290864 | 1110899 | PRODUCT NAME XX | 001 | 706 |
| 290865 | 1110900 | PRODUCT NAME XX | 003 | 721 |
| 290870 | 1110905 | PRODUCT NAME XX | 004 | 743 |
| 290871 | 1110906 | PRODUCT NAME XX | 004 | 743 |
| 290878 | 1110913 | PRODUCT NAME XX | 006 | 806 |
| 290879 | 1110914 | PRODUCT NAME XX | 007 | 807 |
| 290908 | 1110943 | PRODUCT NAME XX | 008 | 815 |
+--------+---------+-----------------+-----------+-----------+
If I run the script below to group the results by the last 2 fields I get an error:
Column 'A.CODE' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
SELECT
min(A.ID),
A.CODE,
A.NAME,
B.VARCHAR01,
B.VARCHAR02
FROM
PRODUCTS A
INNER JOIN
EXTRAS B
ON
A.ID = B.ID
WHERE
A.COMPANY = 1002
AND
A.TYPE = 50
GROUP BY
B.VARCHAR01,
B.VARCHAR02
Any help is appreciated.
If you want the min id, then you don't need other columns:
SELECT MIN(P.ID), E.VARCHAR01, E.VARCHAR02
FROM PRODUCTS P INNER JOIN
EXTRAS E
ON P.ID = E.ID
WHERE P.COMPANY = 1002 AND P.TYPE = 50
GROUP BY E.VARCHAR01, E.VARCHAR02;
Note that this replaces the meaningless table aliases with abbreviations for the table names.
If you want the entire row, then you can use window functions:
SELECT PE.*
FROM (SELECT P.*, E.VARCHAR01, E.VARCHAR02,
ROW_NUMBER() OVER (PARTITION BY E.VARCHAR01, E.VARCHAR02 ORDER BY P.ID ASC) as seqnum
FROM PRODUCTS P INNER JOIN
EXTRAS E
ON P.ID = E.ID
WHERE P.COMPANY = 1002 AND P.TYPE = 50
) PE
WHERE seqnum = 1;

Compare dates and data column

I have tables like this:
TABLE 1 - PERSON:
m_id | name |
-------------
22 | jo |
-------------
77 | john |
--------------
TABLE 2 - AMT_DATA
m_id | amt | activity |
-------------------------
22 | 100 | - |
-------------------------
77 | 300 | n |
-------------------------
TABLE 3 - STATUS_DATA:
m_id | status | s_date |
22 | - | 01.01.2000 |
22 | n | 01.01.2001 |
22 | - | 01.01.2002 |
77 | - | 01.01.2001 |
77 | n | 01.01.2002 |
How can i write a query or procedure that will return me all m_ids which biggest status_data.s_date for that m_id also have status_data.status = '-'?
I need to get result like this:
person.m_id | person.name | amt_data.amt | status | s_date
------------------------------------------------------------------
22 | jo | 100 | - | 01.01.2002
I don't see what amt really has to do with the question. You can just join that in.
One method is:
select p.*, status_date, status
from person p join
(select m_id, max(s_date) as status_date,
max(status) keep (dense_rank first order by s_date desc) as status
from status_data
group by m_id
) s
using (m_id)
where status = '-';
The keep syntax is Oracle's (rather verbose) way of implementing a "first" aggregation function.
You can use the analytical function as follows:
Select * from
(Select p.m_id,
P.name,
A.amt,
S.status,
S.s_date,
Row_number() over (partition by p.m_id order by s.s_date desc) as rn
From person p
join amt_data a on p.m_id = a.m_id
Join status_data s on p.m_id = s.m_id
Where s.status = '-')
Where rn = 1;

Is it possible for foxpro in sql statement to fill the winners_name column base on condition maximum score and id with different names

Is it possible for foxpro in sql statement to add and fill a winners_name column base on condition maximum score and id with different names.
I have created a sql statement but it was not supported by foxpro, is there other alternative to do this rather than using a loop (I like sql statement for faster result even in 50k row lines)
SELECT * , ;
(SELECT TOP 1 doc_name FROM Table1 as b1 WHERE ALLTRIM(b1.id) = a.id ORDER BY b1.score DESC, b1.id) as WINNERS_NAME ;
FROM Table1 as a
I have only 1 table, with columns [ name, id, score ]
A sample table would be like this
NAME | ID | SCORE |
BEN | 101 | 5 |
KEN | 101 | 2 |
ZEN | 101 | 3 |
JEN | 103 | 4 |
REN | 103 | 3 |
LEN | 102 | 5 |
PEN | 102 | 4 |
ZEN | 102 | 3 |
The result would be like this (winners_name is tag on ID)
NAME | ID | SCORE | WINNERS_NAME
BEN | 101 | 5 | BEN
KEN | 101 | 2 | BEN
ZEN | 101 | 3 | BEN
JEN | 103 | 4 | PEN
REN | 103 | 3 | PEN
LEN | 102 | 5 | LEN
PEN | 103 | 5 | PEN
ZEN | 102 | 3 | LEN
Try this approach:
SELECT
a.NAME,
a.ID,
a.SCORE,
b.WINNERS_NAME
FROM Table1 a
INNER JOIN
(
SELECT t1.ID, t1.NAME AS WINNERS_NAME
FROM
(
SELECT ID, SCORE, MIN(NAME) AS NAME
FROM Table1
GROUP BY ID, SCORE
) t1
INNER JOIN
(
SELECT ID, MAX(SCORE) AS MAX_SCORE
FROM Table1
GROUP BY ID
) t2
ON t1.ID = t2.ID AND
t1.SCORE = t2.MAX_SCORE
) b
ON a.ID = b.ID
ORDER BY
a.ID;
Follow the link below for a demo running in MySQL (though the syntax should still work on FoxPro):
Demo

Teradata: Query the next lowest value of two columns for a given input

I have the following two tables:
TableA:
+---------+--------+
| name | nm_key |
+---------+--------+
| Bob | 1124 |
| Sally | 3278 |
| Frank | 6484 |
| Mary | 1125 |
| Annette | 2798 |
+---------+--------+
TableB:
+--------+----------+--------+
| nm_key | sequence | status |
+--------+----------+--------+
| 1124 | 33333 | 3 |
| 2798 | 11111 | 1 |
| 3278 | 12226 | 2 |
| 1125 | 24356 | 3 |
| 6484 | 12272 | 2 |
+--------+----------+--------+
Using the two tables, how do I write a Teradata SQL query that will return the name that has the next lowest status and the next lowest sequence for a name input? For example, an input of Bob will return Frank because the next lowest status is 2 with the next lowest sequence of 12272.
Try this:
Select * from table1
Left join table2 on table2.nm_key=table1.nm_key
Where status=ceil(status +.99)
Let's just focus on num_key here and table b. The name comes from joining the results. One method is a correlated subquery. The following gets the key
select b.*,
(select top 1 b2.nm_key
from b b2
where b2.status < b.status
order by b2.status desc, b2.sequence desc
) as prev_nm_key
from b;
Limiting to "Bob" and getting the name are minor adjustments to the logic.
EDIT:
That is a painful limitation. Here is one workaround:
select bb.*, b.nm_key
from (select b.*,
(select max(b2.status || ':' || b2.sequence) as statseq
from b b2
where b2.status < b.status
order by b2.status desc, b2.sequence desc
) as prev_nm_key
from b
) bb join
b
on (b.status || ':' || b.sequence) = bb.statseq;

SQL Server - COUNT with GROUP BY in subquery

I have been really struggling with this one! Essentially, I have been trying to use COUNT and GROUP BY within a subquery, errors returning more than one value and whole host of errors.
So, I have the following table:
start_date | ID_val | DIR | tsk | status|
-------------+------------+--------+-----+--------+
25-03-2015 | 001 | U | 28 | S |
27-03-2016 | 003 | D | 56 | S |
25-03-2015 | 004 | D | 56 | S |
25-03-2015 | 001 | U | 28 | S |
16-02-2016 | 002 | D | 56 | S |
25-03-2015 | 001 | U | 28 | S |
16-02-2016 | 002 | D | 56 | S |
16-02-2016 | 005 | NULL | 03 | S |
25-03-2015 | 001 | U | 17 | S |
16-02-2016 | 002 | D | 81 | S |
Ideally, I need to count the number of times the unique value of ID_val had for example U and 28 or D and 56. and only those combinations.
For example I was hoping to return the below results if its possible:
start_date | ID_val | no of times | status |
-------------+------------+---------------+--------+
25-03-2015 | 001 | 3 | S |
27-03-2016 | 003 | 1 | S |
25-03-2015 | 004 | 1 | S |
25-03-2015 | 002 | 3 | S |
I've managed to get the no of times on their own, but not be apart of a table with other values (subquery?)
Any advice is much appreciated!
This is a basic conditional aggregation:
select id_val,
sum(case when (dir = 'U' and tsk = 28) or (dir = 'D' and tsk = 56)
then 1 else 0
end) as NumTimes
from t
group by id_val;
I left out the other columns because your question focuses on id_val, dir, and tsk. The other columns seem unnecessary.
You want one result per ID_val, so you'd group by ID_val.
You want the minimum start date: min(start_date).
You want any status (as it is always the same): e.g. min(status) or max(status).
You want to count matches: count(case when <match> then 1 end).
select
min(start_date) as start_date,
id_val,
count(case when (dir = 'U' and tsk = 28) or (dir = 'D' and tsk = 56) then 1 end)
as no_of_times,
min(status) as status
from mytable
group by id_val;
Use COUNT with GROUP BY.
Query
select start_date, ID_val, count(ID_Val) as [no. of times], [status]
from your_table_name
where (tsk = 28 and DIR = 'U') or (tsk = 56 and DIR = 'D')
group by start_date, ID_val, [status]
So far, all the answers assume you are going to know the value pairs in advance and will require modification if these change or are added to. This solution makes no assumptions.
Table Creation
CREATE TABLE IDCounts
(
start_date date
, ID_val char(3)
, DIR nchar(1)
, tsk int
, status nchar(1)
)
INSERT IDCounts
VALUES
('2015-03-25','001','U' , 28,'S')
,('2016-03-27','003','D' , 56,'S')
,('2015-03-25','004','D' , 56,'S')
,('2015-03-25','001','U' , 28,'S')
,('2016-03-16','002','D' , 56,'S')
,('2015-03-25','001','U' , 28,'S')
,('2016-02-16','002','D' , 56,'S')
,('2016-02-16','005', NULL, 03,'S')
,('2015-03-25','001','U' , 17,'S')
,('2016-02-16','002','D' , 81,'S');
Code
SELECT Distinct i1.start_date, i1.ID_Val, i2.NumOfTimes, i1.status
from IDCounts i1
JOIN
(
select start_date, ID_val, isnull(DIR,N'')+cast(tsk as nvarchar) ValuePair, count(DIR+cast(tsk as nvarchar)) as NumOfTimes
from IDCounts
GROUP BY start_date, ID_val, isnull(DIR,N'')+cast(tsk as nvarchar)
) i2 on i2.start_date=i1.start_date
and i2.ID_val =i1.ID_val
and i2.ValuePair =isnull(i1.DIR,N'')+cast(i1.tsk as nvarchar)
order by i1.ID_val, i1.start_date;