How to expand a sql table? - sql

I want to create a view where all birthdays of all persons are listed. Lets say I want to "expand" a table.
I have a table Person with the attributes ID and Birthday.
I have a function FuncBirthdays that takes an ID and a Birthday and then it lists all birthdays until the current date. For example:
SELECT * FROM FuncBirthdays(123, '01.01.2014');
Result:
123; '01.01.2014'
123; '01.01.2015'
123; '01.01.2016'
// Current date is 14.01.2016, so the list stops here.
Now I want to create a view with all birthdays of all persons in table Person.
SELECT *
FROM
Person
INNER JOIN
(SELECT * FROM FuncBirthdays(Person.ID, Person.Birthday)) FB
ON Person.ID = FB.ID
The error message in the MS SQL Server 2012 Studio is that Person.ID and Person.Birthday are unbound.
I'm guessing that this way collides with the way JOINS are done. What SQL-concepts could I use to expand a table? Every data set in Person generates a variable amount of data sets in the output. Currently I have solved this problem with a C#.net function that collects the data manually. Is there a pure SQL way?
Thanks
Martin

"The error message in the MS SQL Server 2012 Studio is that Person.ID and Person.Birthday are unbound."
Is because of you are trying to access person in Join result set which can not be accessible. You need to use cross apply in that case, as per my knowledge. Please try following query
SELECT * FROM Person p
CROSS APPLY
(
SELECT * FROM FuncBirthdays(p.ID, p.Birthday)
) FB
WHERE p.ID = FB.ID

With CTE1 (ID, Birthday) AS
(
SELECT * FROM FuncBirthdays(p.ID, p.Birthday)
)
SELECT *
FROM CTE1, Person p
WHERE p.ID = CTE1.ID

Related

Add aliases to each table in SQL query (programmatically)

I need programmatically edit SQL commands in such way that each table in the SQL command will have an alias. The input is a schema for the database and an SQL command. The output should be an SQL query where every table and has an alias and it is always used when we reference an attribute of that table.
For example, let us have a database person(id, name, salary, did) and department(did, name) and the following SQL command:
select id, t.did, maxs
from person
join (
select did, max(salary) maxs
from person
group by did
) t on t.maxs = salary and person.did = t.did
The expected result for such input would be
select p1.id, t.did, t.maxs
from person p1
join (
select p2.did, max(p2.salary) maxs
from person p2
group by p2.did
) t on t.maxs = p1.salary and p1.did = t.did
I was considering using ANTLR4 for this, however, I was curious whether there is a simpler solution. I recently come across TSqlParser, is it possible to use this class to achieve such rewrite in some simple way?

Create Sql View only a table multiple times

I try create a view with one table multi time here is my code but the result is wrong.Can you help me I cant see here wrong thing.
SELECT DISTINCT
O1.HomeWorkId, O2.FileInfo AS TeacherFileInfo,
O2.Answer AS TeacherAnswer, O1.Answer AS StudentAnswer,
O1.StudentId
FROM
HomeWorkAnswer AS O1
INNER JOIN
HomeWorkAnswer AS O2 ON O1.FileInfo = O2.FileInfo
WHERE
(O1.HomeWorkId > 0)
I save my teacher answer and student answer in this table.In my project I select two times for compare the answer.But I thing for performance it is not good and try create something like this.
You can try using derived tables to separate teachers answer from students answers.
SELECT DISTINCT
sa.HomeWorkId,
ta.FileInfo AS TeacherFileInfo,
ta.Answer AS TeacherAnswer,
sa.Answer AS StudentAnswer,
sa.StudentID
FROM
(SELECT *
FROM HomeWorkAnswer
WHERE studentId = -1) ta
JOIN
(SELECT *
FROM HomeWorkAnswer
WHERE teacherId = -1) sa ON ta.FileInfo = sa.FileInfo

How to Select foreign key names instead of number in SQL Server

I have the following situation:
One table called cad (id, name, gender, age, fok_professional, fok_agegroup, fok_ativity) and other table called professional (id, name), agegroup (id, desc),
ativity (id, name);
How to select the name value instead number as generally is presented in simple query: For exemple:
SELECT * FROM cad
output is:
id -> 1;
name -> Teste;
gender -> Male;
age -> 22;
fok_professional -> 1;
fok_agegroup -> 4;
fok_ativity -> 2;
instead I would like:
id -> 1;
name -> Teste;
gender -> Male;
age -> 22;
fok_professional -> Administrator;
fok_agegroup -> Age 19 55;
fok_ativity -> Testestetstats;
How to get the values name ?
You want something like this, using SQL JOINs to connect the tables together :
SELECT
cad.id, cad.name, cad.gender, cad.age,
professional.name,
agegroup.desc,
ativity.name
FROM cad INNER JOIN professional ON cad.fok_professional = professional.id
INNER JOIN agegroup ON cad.fok_agegroup = agegroup.id
INNER JOIN ativity ON cad.fok_ativity = ativity.id
You need to use the JOIN statement on tables cad, professionnal, agegroup and activity ON fok_XXX=XXX.id
Assuming you have appropriate permissions on the database that those tables belong to, if you want to be able to be able to just call something as simple as SELECT * FROM sometable to produce that sort of output, you may want to consider creating a view, then joining the two other tables that contain the human-readable rows in your view query.
Luckily for you, SQL Server Management Studio has a simple view designer which can help you in this regard. To access it, expand the database you want to create the view for in the Object Explorer, right-click Views, then click New View. Once you add the three tables you need into the View Designer, it will automatically create the required joins for you, assuming the relationships on each table are set up properly.
If you don't have SSMS, or you're not sure what views are, or why they're useful for your particular situation, you should review the SQL Views topic on TechNet. If you're not sure what joins are, or why tabular relationships are important, then you should consider brushing up on relational database theory a bit.
You need to use inner join between those three tables.
Try this:
SELECT C.ID,
C.NAME,
C.GENDER,
C.AGE,
P.NAME AS [FOK_PROFESSIONAL],
A.DESC AS [FOK_AGEGROUP],
AT.NAME AS [FOK_ATIVITY]
FROM CAD C
INNER JOIN PROFESSIONAL P
ON C.FOK_PROFESSIONAL = P.ID
INNER JOIN AGEGROUP A
ON C.FOK_AGEGROUP = A.ID
INNER JOIN ATIVITY AT
ON C.FOK_ATIVITY = AT.ID
You could use above solutions or you can use the following known as Common Table Expressions (CTEs) for more readability.
WITH C AS
(
SELECT
CAD.id, CAD.name, CAD.gender, CAD.age,
PRO.name,
AG.desc,
AT.name
FROM CAD INNER JOIN professional As PRO ON CAD.fok_professional = PRO.id
INNER JOIN agegroup As AG ON CAD.fok_agegroup = AG.id
INNER JOIN ativity As AT ON CAD.fok_ativity = AT.id
)
SELECT * FROM C;

Average grade of student temporary table in SQL Server

I have a database with the following tables:
Students_T (SNo, SDedc, SAddress)
Courses_T (CNo, CDesc)
CoursesRegister_T (CRNo, CR_CNo, CR_SNo, CRGrade)
I need to represent the following data:
For each student show:
Student desc, Course desc, Course grade, Grades average
And I need to do this for every course that the student register in.
For example:
if student A is registered to course B and his grade is 90
and he's also registered to course C and his grade is 70, I suppose to get the following table:
A B 90 80
A C 70 80
The important thing is that I need to use temporary tables with SELECT INTO syntax and I can't figure it out.
I'm using SQL Server.
Someone know how to do that?
EDIT:
I already did this:
select CR_ST_No, ST_Desc, CR_Desc, CR_Grade
into #Grades
from Students_T
left join CoursesRegister_T on CR_ST_NO = ST_No
where CRS_Desc is not null
select *
from #Grades
drop table #Grades
and it's giving me table with all data I need except the average.
When I try to change the row select * to select *, avg(CR_Grade)
it can't execute.
This is a typical use case for a Window Function. You haven't provided any sample data and I can't test it right now, but something like that should work:
SELECT s.SDedc, c.CDesc, r.CRGrade, AVG(r.CRGrade) OVER (PARTITION BY s.SNo) AS average_grade
FROM Students_T AS s
LEFT JOIN CoursesRegister_T AS r ON r.CR_SNo = s.SNo
LEFT JOIN Courses_T AS c ON r.CR_CNo = c.CNo;
From your question and the subsequent comments, you need to change your select-statement to include a group by clause.
Please read through the MSDN SELECT (Transact-SQL) article as to how a select statement is structure, with explanations and examples of each section.
The reason you are getting your error message is that the AVG is an aggregate function and so requires that all columns that are not part of the aggregate, to be included into the group by clause of your select statement.

SQL COUNT entries related to 3 tables

I had this sql statement running for these two tables: tblStations and tblThreads, for counting number of Stations in each thread:
SELECT tblThreads.*, (SELECT COUNT(*) FROM tblStations WHERE tblStations .fldThreadID=tblThreads.fldID) AS TotalStationsInThread FROM tblThreads;
and then display tblThread.Name and TotalStationsInThread, for each thread in the system.
Now I added another table (tblUsers) in this hierarchy:
each thread can have many users, and each user can have many stations.
The three tables are related to each other by this:
tblStations.fldUserID=tblUsers.fldID > tblUsers.fldThreadID=tblThreads.fldID.
So I changed my SQL query to this:
SELECT tblThreads.*, (SELECT COUNT(*) FROM tblStations WHERE tblStations.fldUserID=tblUsers.fldID AND tblUsers.fldThreadID=tblThreads.fldID) AS TotalStationsInThread FROM tblThreads;
But now I'm getting this message: "No value given for one or more required parameters." It's like the database can't connect the tables tblStations with tblThreads via tblUsers...
Any help please on how to count the number of stations that are connected to all the users that are connected to each thread??
This is the correct answer for MS Access Jet Database Engine:
SELECT tblThreads.*, (SELECT COUNT(tblStations.fldUserID) FROM tblStations INNER JOIN tblUsers ON tblStations.fldUserID = tblUsers.fldID WHERE tblUsers.fldThreadID = tblThreads.fldID) AS TotalStationsInThread FROM tblThreads;
Many thanks for Gordon Linoff for his answer.
You need to include tblUsers in the from clause:
SELECT tblThreads.*,
(SELECT COUNT(*)
FROM tblUsers inner join
tblStations
on tblStations.fldUserID = tblUsers.fldID
WHERE tblUsers.fldThreadID = tblThreads.fldID
) AS TotalStationsInThread
FROM tblThreads;