SQL - How to display the students with the same age? - sql

the code I wrote only tells me how many students have the same age. I want their names too...
SELECT YEAR(CURRENT DATE-DATEOFBIRTH) AS AGE, COUNT(*) AS HOWMANY
FROM STUDENTS
GROUP BY YEAR(CURRENT DATE-DATEOFBIRTH);
this returns something like this:
AGE HOWMANY
--- -------
21 3
30 5
Thank you.
TABLE STUDENTS COLUMNS:
StudentID (primary key), Name(varchar), Firstname(varchar), Dateofbirth(varchar)
I was thinking of maybe using the code above and somewhere add the function concat that will put the stundents' names on the same row as in

your existing SQL looks like it has errors, but you could use GROUP_CONCAT:
add GROUP_CONTACT(colname) as another column to fetch, then split by , in your application

The resulting data set does not appear useful on the surface based on the question unless you are looking for a listing of students, their age, and how many other students are of the same age:
SELECT NAME, AGE, HOWMANY
FROM STUDENTS AS S,
(SELECT YEAR(CURRENT DATE-DATEOFBIRTH) AS AGE,
COUNT(*) AS HOWMANY
FROM STUDENTS
GROUP BY YEAR(CURRENT DATE-DATEOFBIRTH)
) AS A
WHERE YEAR(CURRENT DATE-S.DATEOFBIRTH) = A.AGE
Basically perform a self-join with the age counts you have calculated.

What about...
SELECT name FROM students WHERE age = ENTER_AGE_HERE;
You have the names and the number of students can be found by finding the number of entries you get from the query.
For example, in PHP, you can find the length of the array.
Of course, you have to change to names in my example to the names used in your database.

CREATE TABLE #Student
(
id int identity(1,1),
age int,
name varchar(255)
)
INSERT INTO #Student S
VALUES(21,'bob'),
(21,'tom'),
(21,'dick'),
(21,'william'),
(35,'mark'),
(35,'anthony')
SELECT age,COUNT(*),STUFF(
(
SELECT ',' + name
FROM #Student SS
WHERE SS.age = S.age
FOR XML PATH('')
), 1, 1, '')
FROM #Student s
GROUP BY age

Related

TSQL: How do I search for a grade within a list of grades?

How do I search for a grade within a list of grades? Some grades are of string data type, for example 'PK', and 'KK'.
The production list has over a thousand students each in different grade levels, so I'm not sure how to ensure the query would address that.
The logic I'm trying for is something like WHERE CurrentGrade like ('%SchoolGrades%').
Example query results:
Desired result:
(I didn't design the tables I have to work with as I know they are not optimal, but it's what I have to work with, thanks for the help.)
Sample code:
CREATE TABLE #StudentGrades(
StudentID int
, CurrentGrade varchar(255)
, SchoolEarliestGrade varchar(255)
, SchoolID int
, School varchar(255)
, SchoolGrades varchar(255)
)
INSERT INTO #StudentGrades (StudentID, CurrentGrade, SchoolEarliestGrade, SchoolID, School, SchoolGrades)
VALUES
(7777777, 11, 'PK' , 111 ,'Smith Elementary' ,'PK, KK, 01, 02, 03, 04, 05'),
(7777777, 11, '06' , 222 ,'Jones Middle' ,'06, 07, 08'),
(7777777, 11, '09' , 333 ,'Perez High School' ,'09, 10, 11, 12')
SELECT * FROM #StudentGrades
This will give you the rows where the CurrentGrade is in the SchoolGrades.
SELECT *
FROM StudentGrades
WHERE ', ' + SchoolGrades + ', ' LIKE '%, ' + CurrentGrade + ', %'
Edit: This is the best solution with help from the comments. Thanks, all.
Based on the fatc that the grades are in string field you can use like
select * from StudentGrades
where schoolGrades like '%11%';
or
select * from StudentGrades
where schoolGrades like '%KK%';
select * from StudentGrades
where schoolGrades like '%KK%' OR schoolGrades like '%PK%';
One could use a recursive CTE to unpivot the data in, separated values of SchoolGrades. Then derrive the needed values, and fianally use a simple where clause in the select against the CTE. Not sure of performance as the recursive loop is a record by record approach; however it may be faster than a full table scan with 2 ors.
Working example:
http://rextester.com/NAB12900
strCTE gets the values to normalize the data for us
subcte provides the the needed grades on individual rows with data normalized
and the last query simply limits to only current grade matching stringvalue.
.
WITH StrCTE AS
(
SELECT 1 start, CHARINDEX(',' , schoolGrades) stop, StudentID, CurrentGrade, SchoolEarliestGrade, SchoolID, School, SchoolGrades
FROM#studentGrades A
UNION ALL
SELECT stop + 1, CHARINDEX(',' ,schoolgrades , stop + 1), StudentID, CurrentGrade, SchoolEarliestGrade, SchoolID, School, SchoolGrades
FROM StrCTE A
WHERE stop > 0
),
SUBCTE AS (SELECT StudentID, CurrentGrade, SchoolEarliestGrade, SchoolID, School, SchoolGrades, ltrim(SUBSTRING(schoolgrades , start, CASE WHEN stop > 0 THEN stop-start ELSE 4000 END)) AS stringValue
FROM StrCTE)
SELECT *
FROM SUBCTE
WHERE currentgrade = stringValue
IMO this strCTE query gives you the ability to normalize the data allowing other standard SQL queries to function. Maybe create STRCTE as a materialized view on which your analysis is done. as the materialized view can have indexes which reduce the performance loss from the recursive loop.

How do I Use Count Function for different values of the same column in MS-SQL?

For DB StudentInfo and Table Student as follows:
CREATE TABLE Student
(
ID INT PRIMARY KEY IDENTITY(1,1),
Name nvarchar(255)
)
and inserting values:
Insert Into Student Values ('Ashok')`
executing it 3 times, and
Insert Into Student Values ('Achyut')
executing it 2 times and total 5 rows of data are inserted into the table.
I want to display a result counting the result with the name having 'Ashok' & 'Achyut'.
Generally for single values count in a column I use:
SELECT Count(Name) AS NoOfStudentHavingNameAshok
FROM Student
WHERE Name = 'Ashok'
but how to display the NoOfStudentHavingNameAshok & NoOfStudentHavingNameAchyut what query should I run?
You should include name in the select and group by name.
SELECT name, Count(*)
From Student
group by name
You can put conditions inside your COUNT() function:
select count(case when Name = 'Ashok' then 'X' end) as NoOfStudentHavingNameAshok,
count(case when Name = 'Achyut' then 'X' end) as NoOfStudentHavingNameAchyut
from Student

Custom Ordering of SELECT Results

I'm working with a Pro*C query, but this question should be general SQL. My research has been a dead end, but I think I'm missing something.
Suppose my server has an array of students' names, {"Alice","Charlie","Bob"}. I query the Student_Ids table for the students' ID numbers:
SELECT id_no FROM student_ids
WHERE student_name IN ('Alice','Charlie','Bob');
To simplify server-side processing, I want to sort the result set in the same order as the students' names. In other words, the result set would be {alice_id_no, charlie_id_no, bob_id_no} regardless of the actual ordering of the table or the behavior of any particular vendor's implementation.
The only solution I can think of is:
. . .
ORDER BY
CASE WHEN student_name='Alice' THEN 0
WHEN student_name='Charlie' THEN 1
WHEN student_name='Bob' THEN 2 END;
but that seems extremely messy and cumbersome when trying to dynamically generate/run this query.
Is there a better way?
UPDATE I gave a terrible example by pre-sorting the students' names. I changed the names to be deliberately unsorted. In other words, I want to sort the names in a non-ASC or DESC-friendly way.
UPDATE II Oracle, but for knowledge's sake, I am looking for more general solutions as well.
The ORDER BY expression you've given for your sample data is equivalent to ORDER BY student_name. Is that what you intended?
If you want a custom ordering that is not alphabetical, I think you might have meant something like this:
ORDER BY
CASE
WHEN student_name = 'Alice' THEN 0
WHEN student_name = 'Charlie' THEN 1
WHEN student_name = 'Bob' THEN 2
END;
You can use a derived table as well, that holds the names as well as the ordering you want. This way you only have to put the names in a single time:
SELECT S.id_no
FROM
student_ids AS S
INNER JOIN (
SELECT Name = 'Alice', Seq = 0 FROM DUAL
UNION ALL SELECT 'Bob', 2 FROM DUAL
UNION ALL SELECT 'Charlie', 1 FROM DUAL
) AS N
ON S.student_name = N.Name
ORDER BY
N.Seq;
You could also put them into a temp table, but in Oracle that could be somewhat of a pain.
Can you do this?
order by student_name
To do a custom sort, you only need one case:
ORDER BY (CASE WHEN student_name = 'Alice' THEN 1
WHEN student_name = 'Bob' THEN 2
WHEN student_name = 'Charlie' THEN 3
ELSE 4
END)
why not this :
SELECT id_no FROM student_ids
WHERE student_name IN ('Alice','Bob','Charlie')
ORDER BY student_name
You can ORDER BY any columns, not necessary those in SELECT list or WHERE clause
SELECT id_no
FROM student_ids
WHERE student_name IN ('Alice','Bob','Charlie)
ORDER BY id_no;
Add a table to hold the sort priorities then you can use the sort_priorities in whatever query you want (and easily update the priorities):
CREATE TABLE student_name_sort_priorities (
student_name VARCHAR2(30) CONSTRAINT student_name_sort_priority__pk PRIMARY KEY,
sort_priority NUMBER(10) CONSTRAINT student_name_sort_priority__nn NOT NULL
CONSTRAINT student_name_sort_priority__u UNIQUE
);
(If you want two values to be equivalently sorted then don't include the UNIQUE constraint.)
INSERT INTO student_name_sort_priorities VALUES ( 'Alice', 0 );
INSERT INTO student_name_sort_priorities VALUES ( 'Charlie', 2 );
INSERT INTO student_name_sort_priorities VALUES ( 'Bob', 1 );
Then you can join the sort priority table with the student_ids table and use the extra column to perform ordering:
SELECT id_no
FROM student_ids s
LEFT OUTER JOIN
student_name_sort_priorities p
ON (s.student_name = p.student_name)
ORDER BY
sort_priority NULLS LAST;
I've used a LEFT OUTER JOIN so that if a name is not contained on the student_name_sort_priorities table then it does not restrict the rows returned from the query; NULLS LAST is used in the ordering for a similar reason - any student names that aren't in the sort priorities table will return a NULL sort priority and be placed at the end of the ordering. If you don't want this then just use INNER JOIN and remove the NULLS LAST.
How about using a 'table of varchar' type like the build-in below:
TYPE dbms_debug_vc2coll is table of varchar2(1000);
test:
SQL> select customer_id, cust_first_name, cust_last_name from customers where cust_first_name in
2 (select column_value from table(sys.dbms_debug_vc2coll('Frederic','Markus','Dieter')));
CUSTOMER_ID CUST_FIRST_NAME CUST_LAST_NAME
----------- -------------------- --------------------
154 Frederic Grodin
149 Markus Rampling
152 Dieter Matthau
That seems to force the order, but that might just be bad luck. I'm not really a sql expert.
The execution plan for this uses 'collection iterator' instead of a big 'or' in the typical:
select customer_id, cust_first_name, cust_last_name from customers where cust_first_name in ('Frederic','Markus','Dieter');
hth, Hein.

SQL groupby multiple columns

Tablename: EntryTable
ID CharityName Title VoteCount
1 save the childrens save them 1
2 save the childrens saving childrens 3
3 cancer research support them 10
Tablename: ContestantTable
ID FirstName LastName EntryId
1 Neville Vyland 1
2 Abhishek Shukla 1
3 Raghu Nandan 2
Desired output
CharityName FullName
save the childrens Neville Vyland
Abhishek Shukla
cancer research Raghu Nandan
I tried
select LOWER(ET.CharityName) AS CharityName,COUNT(CT.FirstName) AS Total_No_Of_Contestant
from EntryTable ET
join ContestantTable CT
on ET.ID = CT.ID
group by LOWER(ET.CharityName)
Please advice.
Please have a look at this sqlfiddle.
Have a try with this query:
SELECT
e.CharityName,
c.FirstName,
c.LastName,
sq.my_count
FROM
EntryTable e
INNER JOIN ContestantTable c ON e.ID = c.EntryId
INNER JOIN (
SELECT EntryId, COUNT(*) AS my_count FROM ContestantTable GROUP BY EntryId
) sq ON e.ID = sq.EntryId
I assumed you actually wanted to join with ContestantTable's EntryId column. It made more sense to me. Either way (joining my way or yours) your sample data is faulty.
Apart from that, you didn't want repeating CharityNames. That's not the job of SQL. The database is just there to store and retrieve the data. Not to format it nicely. You want to work with the data on application layer anyways. Removing repeating data doesn't make this job easier, it makes it worse.
Most people do not realize that T-SQL has some cool ranking functions that can be used with grouping. Many things like reports can be done in T-SQL.
The first part of the code below creates two local temporary tables and loads them with data for testing.
The second part of the code creates the report. I use two common table expressions (CTE). I could have used two more local temporary tables or table variables. It really does not matter with this toy example.
The cte_RankData has two columns RowNum and RankNum. If RowNum = RankNum, we are on the first instance of charity. Print out the charity name and the total number of votes. Otherwise, print out blanks.
The name of the contestant and votes for that contestant are show on the detail lines. This is a typical report with sub totals show at the top.
I think this matches the report output that you wanted. I ordered the contestants by most votes descending.
Sincerely
John Miner
www.craftydba.com
--
-- Create the tables
--
-- Remove the tables
drop table #tbl_Entry;
drop table #tbl_Contestants;
-- The entries table
Create table #tbl_Entry
(
ID int,
CharityName varchar(25),
Title varchar(25),
VoteCount int
);
-- Add data
Insert Into #tbl_Entry values
(1, 'save the childrens', 'save them', 1),
(2, 'save the childrens', 'saving childrens', 3),
(3, 'cancer research', 'support them', 10)
-- The contestants table
Create table #tbl_Contestants
(
ID int,
FirstName varchar(25),
LastName varchar(25),
EntryId int
);
-- Add data
Insert Into #tbl_Contestants values
(1, 'Neville', 'Vyland', 1),
(2, 'Abhishek', 'Shukla', 1),
(3, 'Raghu', 'Nandan', 2);
--
-- Create the report
--
;
with cte_RankData
as
(
select
ROW_NUMBER() OVER (ORDER BY E.CharityName ASC, VoteCount Desc) as RowNum,
RANK() OVER (ORDER BY E.CharityName ASC) AS RankNum,
E.CharityName as CharityName,
C.FirstName + ' ' + C.LastName as FullName,
E.VoteCount
from #tbl_Entry E inner join #tbl_Contestants C on E.ID = C.ID
),
cte_SumData
as
(
select
E.CharityName,
sum(E.VoteCount) as TotalCount
from #tbl_Entry E
group by E.CharityName
)
select
case when RowNum = RankNum then
R.CharityName
else
''
end as rpt_CharityName,
case when RowNum = RankNum then
str(S.TotalCount, 5, 0)
else
''
end as rpt_TotalVotes,
FullName as rpt_ContestantName,
VoteCount as rpt_Votes4Contestant
from cte_RankData R join cte_SumData S
on R.CharityName = S.CharityName

SQL Where clause

My application initially had a query similar to this one:-
SELECT column_name from PERSON
WHERE name in (list);
where list is comma separated list.
But, now the requirement has changed and i have to query the Persons table with name and age given.
I have the nameAgeList.
Initially, i thought a query similar to this would work (Create nameList and ageList from nameAgeList)
SELECT column_name from Person
WHERE name in (nameList)
AND age in (ageList)
But after carefully thinking, this seems to be a wrong query.
Please let me know how should I proceed ahead with this query.
Under Oracle, you can do this:
SELECT * FROM Person
WHERE
(name, age) IN (
('name1', age1),
('name2', age2)
-- Etc...
)
You can have up to 1000 tuples in this list.
One option is to create a temporary table (or if SQL Server, a table variable), place your names and ages in this table, and then simply join to it:
SELECT column_name from Person p
INNER JOIN myTempTable t ON t.Name = p.Name AND t.age = p.age
It's not pretty, and this one only works when you can generate your statement in code:
SELECT column from Person
WHERE 1=1
AND ( ( name = name1 and age = age1 )
OR ( name = name2 and age = age2 )
OR ( name = name3 and age = age3 )
OR ( name = name4 and age = age4 )
OR ( name = name5 and age = age5 )
... et cetera
)
Now, if you could put those lists into tables you could do alot better than this. Is there any way you can get those lists into the database? I assume that you really need some Person table that holds name and age for each individual.