Return all results from a Query on a single row - sql

I have a query that will return all the results for matching row values between two tables.
The tables are Students and StudentRace. The match in the WHERE statement is STUDENTS.ID = STUDENTRACE.STUDENTID.
I am trying to return the column STUDENTRACE.RACECD where all the matching RACECDs are in a single row in the resulting table. The query does something different, however. IF a STUDENTID has more than 1 RACECD it does not repeat each RACECD in a single row for the STUDENTID. It will return a separate row for each RACECD that matches the STUDENTID.Here is my query:
select
STUDENTS.ID as ID,
STUDENTS.STUDENT_NUMBER as STUDENT_NUMBER,
STUDENTRACE.STUDENTID as STUDENTID,
STUDENTS.FIRST_NAME as FIRST_NAME,
STUDENTS.LAST_NAME as LAST_NAME,
STUDENTRACE.RACECD as RACECD,
STUDENTS.ENROLL_STATUS as ENROLL_STATUS
from
STUDENTRACE STUDENTRACE,
STUDENTS STUDENTS
where
STUDENTS.ID = STUDENTRACE.STUDENTID
and ENROLL_STATUS = 0
Here is the result for a STUDENT ID = 23:
StudentID Racecd
23 B
23 W
This is close but not exactly what I would like to see. I would like the result to be:
StudentID Racecd
23 B,W
or something similar to that. I think I may need the CONCAT function and possibly a nested SELECT statement as well, but I am not sure. I am new to SQL so I am not sure how to move forward.

Like jarlh said I am not sure how you select 7 columns but result in 2?
but Listagg is what I think you are looking for. You can separate it by any delimiter (in this case I put comma). Also any outlying columns will need to appear in the group by
select
STUDENTRACE.STUDENTID as STUDENTID,
listagg(STUDENTRACE.RACECD,',') as RACECD
from STUDENTRACE STUDENTRACE,
STUDENTS STUDENTS
where STUDENTS.ID=STUDENTRACE.STUDENTID
AND ENROLL_STATUS = 0
group by STUDENTRACE.STUDENTID

If you want there two records to become one, you want to group by something, aggregating on something else. In this case you want a single record for each student so you can group by the student_id, and you want all races aggregated so you can use GROUP_CONCAT (in MYSQL, but you can use corresponding aggregation function if in other RDBMS) to concatenate the races. It would be like this:
SELECT s.id, s.name, GROUP_CONCAT(sr.race)
FROM students s join studentrace sr on s.id = sr.student_id
GROUP BY s.id
That is the base to get what you want, then you can add the other fields you are interested in on the select and on the where filters.
SQL Fiddle: http://www.sqlfiddle.com/#!9/ad59d6/1/0
Hope that helps.

If you are using Microsoft SQL Server, you might want to try the following code.
create table Person (
Name nvarchar(450)
)
insert into Person values ('Fabio'), ('Laura')
select stuff((select ',' + Name from Person for xml path('')), 1, 1, '')
The above select statement returns the following string.
Fabio,Laura

Related

how do I make a good subquery in SQL in this code

I need a bit of help with an SQL statement, pretty much a beginner so just go easy on me.
The Program want me to give out every Student who studied for less than 7 years at a School
Select schoolid, characterid, firstname, lastname, count(year) as num
from schoolhouse natural join student natural join character
group by schoolid, characterid, firstname, lastname
So far, so good, with this code I can already see a relation with the counted years but I can't make a where statement which includes the "num" count from the select statement.
WHERE clauses are applied to the individual rows after the tables are joined, but before they are grouped/aggregated. HAVING clauses are used to assert conditions on the results of the aggregation. Just add HAVING count(year) < 7
Select schoolid, characterid, firstname, lastname, count(year) as num
from schoolhouse natural join student natural join character
group by schoolid, characterid, firstname, lastname
having count(year) < 7
But also always qualify which table your columns come from. In this query it's not clear if the year column is from the schoolhouse table, the student table or the character table.
It should look more like...
SELECT TABLE.schoolid, TABLE.characterid, TABLE.firstname, TABLE.lastname, COUNT(TABLE.year) AS num
FROM schoolhouse NATURAL JOIN student NATURAL JOIN character
GROUP BY TABLE.schoolid, TABLE.characterid, TABLE.firstname, TABLE.lastname
HAVING COUNT(TABLE.year) < 7
(Replacing each occurance of TABLE with the correct table name for each case.)
Finally, using words such as character and year as column or table names is usually frowned upon. Such words ofter appear as "key-words" in SQL and can cause errors or ambiguity. In general, if something even might appear as an SQL key-word, don't use it as a column or table name.

Count how many employees with the same name exist in the database in PostgreSQL

I have a data table of employees, I want to get how many same name employees in the database. Name information is saved as first_name and last_name. I have tried.
Select count(concat(first_name,'',last_name) as empname, (concat(first_name,'',last_name) as empname from xyz.
getting error.
Your SQL is missing some parentheses (brackets), and to use an aggregation function as well as a non-aggregated column, you must include the non-aggregated column in a GROUP BY
So your SQL should look like this:
Select count(concat(first_name,' ',last_name)) as countempname,
(concat(first_name,' ',last_name)) as empname
from xyz
GROUP BY (concat(first_name,' ',last_name));
Notice also that I added a space. It is a bit odd to include an empty string in the concat. Also as a short form, if you do not want to repeat the concat in the GROUP BY, you can replace it with the column ordinal number (in this case 2) so it becomes:
Select count(concat(first_name,' ',last_name)) as countempname,
(concat(first_name,' ',last_name)) as empname
from xyz
GROUP BY 2;
If you want to count how many times each first/last name tuple appears in the table, you can use aggregation:
select first_name, last_name, count(*) cnt
from xyz
group by first_name, last_name
This gives you one row per first/last name tuple, with the total count. Note that there is not point concatenating the variables; group by can operate of column tuples.
On the other hand, maybe you want the entire employee row, with an additional column that holds the total count of other rows having the same names. If so, you can use a window count instead of aggregation:
select x.*, count(*) over(partiton by first_name, last_name) cnt
from xyz

Why sometimes a subquery can work like using 'group by'

I'm new to sql and can't understand why sometimes a subquery can work like using 'group by'.
Say, there are two tables in a data base.
'food' is a table crated by:
CREATE TABLE foods (
id integer PRIMARY KEY,
type_id integer,
name text
);
'foods_episodes' is a table created by:
CREATE TABLE foods_episodes (
food_id integer,
episode_id integer
);
Now I'm using the following two sqls and generating the same result.
SELECT name, (SELECT count(*) FROM foods_episodes WHERE food_id=f.id) AS frequency
FROM foods AS f
ORDER BY name;
SELECT name, count(*) AS frequency
FROM foods_episodes,
foods AS f
WHERE food_id=f.id
GROUP BY name;
So why the subquery in the first sql works like it group the result by name?
When I run the subquery alone:
SELECT count(*)
FROM foods_episodes,
foods f
WHERE food_id=f.id
the result is just one row. Why using this sql as a subquery can generate multi-rows result?
The first query isn't actually grouping by name. If you have more than 1 record with the same name (different ID), you will see it being displayed twice (hence, not grouped by).
The first query uses what is called a correlated subquery, it calculates the subquery (the inner SELECT) once for each row of the outmost select. Because the FROM in this outmost SELECT is just from the table foods, you will get one record for each food + the results of the subquery, thus no need to group.

SQL help required

I have a question which asks:
Make a list of all data stored in da_students, da_enrolments and da_courses. Write a query that makes all columns appear only once in the output, the rows are sorted by students' first name ascending AND course_id descending so that students are grouped by course (without using a group by clause) and generated first/ last names( fname.., Lname...) do not appear in the output.
The current code I have is:
SELECT *
FROM DA_STUDENTS
FULL JOIN DA_ENROLMENTS ON student_id = student_student_id
FULL JOIN DA_COURSES ON course_course_id = course_id
ORDER BY first_name ASC, course_id DESC;
Any help please?
To start with: The sorting thing is not well explained. It even looks like it is indented to obfuscate. To sort by student name and course but have the students grouped by course in the end, simply means order by course and then by student.
Then: "generated first/last names ... do not appear in the output". What? How are they generated? Aren't they simple columns in the students table? As to showing something in the output or not: state what you want to show in your SELECT clause. Other columns will not be shown of course.
As to "Make a list of all data stored in da_students, da_enrolments and da_courses": This means all students, all courses plus the associtated enrolements:
from da_students s
cross join da_courses c
left join da_enrolments e on e.student_id = s.student_id and e.course_id = c.course_id
As to "Write a query that makes all columns appear only once in the output": This means, don't
select *
nor
select s.student_id, e.student_id, ...
but only
select s.student_id, ...
so as to show the student ID only once.

SQL Database SELECT question

Need some help with an homework assignment on SQL
Problem
Find out who (first name and last name) has played the most games in the chess tournament with an ID = 41
Background information
I got a table called Games, which contains information...
game ID
tournament ID
start_time
end_time
white_pieces_player_id
black_pieces_player_id
white_result
black_result
...about all the separate chess games that have taken place in three different tournaments ....
(tournaments having ID's of 41,42 and 47)
...and the first and last names of the players are stored in a table called People....
person ID (same ID which comes up in the table 'Games' as white_pieces_player_id and
black_pieces_player_id)
first_name
last_name
...how to make a SELECT statement in SQL that would give me the answer?
sounds like you need to limit by tournamentID in your where clause, join with the people table on white_pieces_player_id and black_pieces_player_id, and use the max function on the count of white_result = win union black_result = win.
interesting problem.
what do you have so far?
hmm... responding to your comment
SELECT isik.eesnimi
FROM partii JOIN isik ON partii.valge=isik.id
WHERE turniir='41'
group by isik.eesnimi
having count(*)>4
consider using the max() function instead of the having count(*)> number
you can add the last name to the select clause if you also add it to the group by clause
sry, I only speak American. What language is this code in?
I would aggregate a join to that table to a derived table like this:
SELECT a.last_name, a.first_name, CNT(b.gamecount) totalcount
FROM players a
JOIN (select cnt(*) gamecount, a.playerid
FROM games
WHERE a.tournamentid = 47
AND (white_player_id = a.playerid OR black_player_id = a.playerid)
GROUP BY playerid
) b
ON b.playerid = a.playerid
GROUP BY last_name, first_name
ORDER BY totalcount
something like this so that you are getting both counts for their black/white play and then joining and aggregating on that.
Then, if you only want the top one, just select the TOP 1