Get Multiple Values for one variable output in stored procedure - c#

These are my classes
Public Class Student
{
Public int Id{get;set;}
public string Name {get;set;}
}
Public Class Department
{
Public int Id{get;set;}
public string Name {get;set;}
public IList <Student> StudentList{get;set;}
}
These are my tables
Student Table
Id |Name |Department
-----------------
1 |aa | 1
2 |bb | 1
3 |cc | 2
Department Table
ID | Name
----------
1 | xxx
2 | yyy
I need the all data of department with the corresponding list of student by using stored procedure
CREATE PROCEDURE test
(
)
AS
BEGIN
select Id as Id,
Name as Name,
----here I need all the corresponding data from student table as StudentList
from Department
Is it possible.. If so Please help me.
Apparently I what want is I want to write another procedure and call it for StudentList,Is it possible

Use join, i choose Left Join here so in case if department does not have corresponding students it will still be retrieved.
SELECT d.ID
,d.Name
,s.ID
,s. Name
FROM Deparment d
LEFT JOIN Student s ON s.Department = d.ID
So from your example the query will return the following result :
ID, Name, Student ID Student Name
1 xxx 1 aa
1 xxx 2 bb
2 yyy 3 cc
Assuming that your stored procedure will accept department id as parameter and retrieve all students under this department, just add a condition in your query
WHERE d.ID = #DeptID
As #Eugen said, just process the result in your application.

What you describe can be done, but it is not the normal process. You can use for xml to return a column where all of the student information is encoded. Note that while this results in all of the data you want being returned in a single row, you will have to create and populate all of your objects by hand. None of the ORM's like Entity Framework or nHibernate will populate both your department and students. In fact, you will probably end up parsing a string by hand in order to create your students.
The most common practice today would be to use Entity Framework and let it do all of the work for you.
Here's some code to show how you can do as you request. Again, note that this is not recommended, and that it's just one way of doing this:
create table #s (id int, name varchar(max), departmentId int);
insert into #s (id, name, departmentId)
select * from (values (1 , 'aa', 1) ,(2 , 'bb', 1) ,(3 , 'cc', 2)
) g(id, name, departmentId);
create table #d (departmentId int, name varchar(max));
insert into #d (departmentId, name) select * from (values
(1 , 'xxx') ,(2 , 'yyy')
)g(departmentId, name)
select *, (select id, name, departmentId
from #s s where s.departmentID = d.departmentId for xml path
)students
from #d d
drop table #d
drop table #s;
Students for deparment 1 =
<row><id>1</id><name>aa</name><departmentId>1</departmentId></row>
<row><id>2</id><name>bb</name><departmentId>1</departmentId></row>
In particular, you may find this a bit heavy, 2016 should be able to do something similar with JSON, but if you'd like that in the meantime, you'll have to customize the output yourself. It can be done, and wouldn't be too difficult.

Related

Select items with the same foreign key

I have a question regarding Linq in C#.
Let's say that I have 3 tables "Company", "Employee" and "Job".
Company
--------
Id - int (PK)
Name - string
Employee
--------
Id - int (PK)
CompanyId - int (FK on Company.Id)
Name - string
Job
--------
Id - int (PK)
CompanyId - int (FK on Company.Id)
EmployeeId - int (FK on Employee.Id)
Name - string
Something like that:
enter image description here
The important thing is that every work must be connected to a company but not necessarily to an employee. However, each employee must be connected to a company. For example we can have racord like that:
Company
--------
Id Name
1 'A'
2 'B'
3 'C'
Employee
--------
Id CompanyId Name
1 1 'A'
2 1 'B'
3 2 'C'
Job
--------
Id CompanyId EmployeeId Name
1 1 1 'clean'
2 1 2 'wash'
3 2 2 'buy'
4 3 NULL 'sell'
And now with linq I would like to get all jobs that are assigned to this employee and other employees from the same company.
So in this case it will should are jobs with id 1 and 2 becouse employee 1 is assigned to company 1 and job with id 2 also has assigned to this company. How could I achieve this using linq but something like this:
_context.Job.Where (x => x)
It is important to make only one query to the database.
Tkanks.
You can simplify the problem by thinking of it in two steps: find the company for a given employee, find all the jobs for that company:
var companyToFind = from e in Employee
where e.Id == empToFind
select e.CompanyId;
var jobsToFind = from j in Job
where companyToFind.Any(cid => cid == j.CompanyId)
select j;
In LINQ to SQL, EF Core 2.2 and EF Core 3.x the Any is translated to an EXISTS query in SQL.
Note since you know there should be only one answer to companyToFind, sending two queries to the database may be more efficient.
For LINQ to SQL or EF Core 3.x, you could also nest the first query and reduce to a single result:
var jobsToFind = from j in Job
where (from e in Employee
where e.Id == empToFind
select e.CompanyId).First() == j.CompanyId
select j;

SQL Query: Display only latest Id from each set

I have the following SQL Table:
Name Description Id UserId CreatedDate
UserSet1 Desc1 1 Abc 06/01/2018
UserSet1 Desc2 2 Def 06/02/2018
UserSet2 Desc for 2 5 NewUser 06/04/2018
UserSet2 Desc for 2 7 NewUser 06/19/2018
What I want to extract from the above table is just the latest Id for each Name so that I could get the following output
Name Description Id UserId CreatedDate
UserSet1 Desc2 2 Def 06/01/2018
UserSet2 Desc for 2 7 NewUser 06/19/2018
Since Id 2 & 7 are the latest entries in the table for UserSet1 & UserSet2, I would like to display that instead of all the entries in the table.
Any inputs how can I get the desired result.
I am open for solutions directly returning the output or any linq (C#) solutions as well. Ie returning the entire dataset and then using linq to filter the above.
EDIT: Since you are looking for the highest number ID, the GROUP BY method would probably be easier to work with.
Using a window function:
SELECT *
FROM (
SELECT Name, Description, Id, UserId, CreatedDate
, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY CreatedDate DESC) AS rn
FROM myTable
) s1
WHERE rn = 1
I don't have an instance of dynamoDB to test on, but I believe it can use the ROW_NUMBER() window function.
Thanks everyone for pointing to right direction. I have got this working with the below code of Linq and C#:
var results = response.GroupBy(row => row.Name)
.SelectMany(g => g.OrderByDescending(row => row.Id).Take(1));
For the initial tests this seems to be working. Let me know if you think this has come issues.
This should be a general SQL answer:
SELECT * FROM yourtable Y1
WHERE Id = (SELECT MAX(Id)
FROM yourtable Y2
WHERE Y2.Name = Y1.Name)
If it was MS SQL you could use Partition By command, otherwise most performant way would be:
select * from Table
where Id in (
select Max(Id) from Table
Group By Name
)
not sure if you can leave Name out of the Select statement, you might need to do:
select * from Table
where Id in (
Select Id from
(select Name, Max(Id) as Id from Table
Group By Name)
)

Nested SELECT in an INSERT Query

These are my tables:
Equipment
-------------
ID INT (PKey)
Name CHAR(10)
Type CHAR(10)
LocationID INT (FKey)
Description TEXT
Location
-------------
LocationID INT (PKey)
Name CHAR(10)
Code INT
The user is given a list of Location.Code to select when inputing new Equipment. (This is how the user identifies the Equipment.LocationID.)
However if I input the data for Location.Code as Equipment.LocationID it will break the system. Is there better way of doing this? For example can I come up with a way to select the Location.LocationID and use the in place of Equipment.LocationID?
NOTE I can't change what the user selects, that has to remain the same.
For example (pseudo code):
string userInput = "110"; // Location.Code
SELECT LocationID FROM Location WHERE Code = #userInput;
userInput = LocationID; // LocationID = 1;
INSERT INTO Equipment(LocationID) VALUES (#userInput);
Is this the best way or is there a more efficient way?
merge two queries into one as below
INSERT INTO Equipment(LocationID) SELECT LocationID FROM Location WHERE Code = '110';

Insert multiple records with values based on each other

Sorry for the bad title, I havent come up with a better one yet.
I am currently optimising a tool which basically does thousands of selects and inserts.
Assume the following relation
class A
{
public long ID; // This is an automatic key by the sqlserver
...Some other values
}
class B
{
public long RefID // Reference to A.ID;
... some other values...
}
class C
{
public long RefID // Reference to A.ID;
... some other values
}
What currently is happening is a SELECT to get ObjectA,
if it doesnt exist, create a new one. The Query returns the ID (OUTPUT INSERTED.ID)
Then it selects (inserts if not existant) the objects B and C.
Is there a way to compress this into a single SQL statement?
Im struggling at the part where the automatic generation of object A happens.
So it must do something like this:
IF NOT EXISTS(SELECT * FROM TableA WHERE someConditions)
INSERT... and get the ID
ELSE
REMEMBER THE ID?
IF NOT EXISTS(SELECT * FROM TableB WERE RefID = ourRememberedID)
INSERT...
IF NOT EXISTS(SELECT * FROM TableC WERE RefID = ourRememberedID)
INSERT...
Please note, stored procedures cannot be used.
A little help
You could issue these three in one statement
Use a DataReader NextResult
select ID from FROM TableA WHERE someConditions
select count(*) from TableB where refID = (select ID from FROM TableA WHERE someConditions)
select count(*) from TableC where refID = (select ID from FROM TableA WHERE someConditions)
But even then you take a risk the TableB or TableC had an insert before you got to it
If I could not use a stored procedure I think I would load the data into a #temp using a TVP
I think you could craft 4 statements in a transaction

How to get rid of multiple columns in a database?

I'm creating an Access DB for use in an C# application at school. I've not had much experience working with DB's so if this sounds stupid just ignore it. I want the user to be able to select all the classes that a certain student has had in our IT department. We have about 30 in all and the maximum that a person can take in 4 years of high school is 15. Right now my DB has 15 different columns for each class that a user could have. How can I compress this down to one column (if there is a way)?
Excellent question Lucas, and this delves into the act of database normalization.
The fact that you recognized why having multiple columns to represent classes is bad already shows that you have great potential.
What if we wanted to add a new class? Now we have to add a whole new column. There is little flexibility for this.
So what can be done?
We create THREE tables.
One table is for students:
Student
|-------------------------|
| StudentID | Student_Name|
|-------------------------|
| 1 | John |
| 2 | Sally |
| 3 | Stan |
---------------------------
One table is for Classes:
Class
------------------------
| ClassID | Class_Name|
------------------------
| 1 | Math |
| 2 | Physics |
------------------------
And finally, one table holds the relationship between Students and Classes:
Student_Class
-----------------------
| StudentID | ClassID |
-----------------------
If we wanted to enroll John into Physics, we would insert a row into the Student_Class table.
INSERT INTO Student_Class (StudentID, ClassID) VALUES (1, 2);
Now, we have a record saying that Student #1 (John) is attending Class #2 (Physics). Lets make Sally attend Math, and Stan attend Physics and Math.
INSERT INTO Student_Class (StudentID, ClassID) VALUES (2, 1);
INSERT INTO Student_Class (StudentID, ClassID) VALUES (3, 1);
INSERT INTO Student_Class (StudentID, ClassID) VALUES (3, 2);
To pull that data back in a readable fashion, we join the three tables together:
SELECT Student.Student_Name,
Class.Class_Name
FROM Student,
Class,
Student_Class
WHERE Student.StudentID = Student_Class.StudentID
AND Class.ClassID = Student_Class.ClassID;
This would give us a result set like this:
------------------------------
| Student_Name | Class_Name |
------------------------------
| John | Physics |
| Sally | Math |
| Stan | Physics |
| Stan | Math |
------------------------------
And that is how database normalization works in a nutshell.
So you have 15 columns (e.g. class1, class2, class3 ... class15)?
Looks like you have a classic many-to-many relationship. You should create a new table to relate students and classes.
student { StudentID, StudentName ... }
classes { ClassID, ClassName ... }
student_classes { StudentID, ClassID }
If you are tracking classes on a year-by-year basis, you could add a year column to the relationship as well:
student_classes { StudentID, Year, ClassID }
It sounds like you need to think about normalizing your database schema.
There is a many-to-many relationship between students and classes such that many students can take many classes and many classes can be taken by many students. The most common approach to handling this scenario is to use a junction table.
Something like this
Student Table
-------------
id
first_name
last_name
dob
Class Table
-----------
id
class_name
academic_year
Student_Class Table
-------------------
student_id
class_id
year_taken
Then your queries would join on the tables, for example,
SELECT
s.last_name + ', ' + s.first_name AS student_name,
c.class_name,
sc.year_taken
FROM
student s
INNER JOIN
student_class sc
ON
s.id = sc.student_id
INNER JOIN
class c
ON
sc.class_id = class.id
ORDER BY
s.last_name, sc.year_taken
One word of advice that I would mention is that Access requires you to use parentheses when joining more than table in a query, I believe this is because it requires you to specify an order in which to join them. Personally, I find this awkward, particularly when I am used to writing a lot of SQL without designers. Within Access, I would recommend using the designer to join tables, then modify the generated SQL for your purposes.
This is a normalisiation issue. In effect you are asking the wrong question. In stead ask yourself the question how can you store 0 or more classes_taken? What other details do you need to store about each class taken? E.g. just the class taken, or data taken, result, etc?
For example consider somethin like the following
table Student
id int
name varchar(25)
...
table classes
id int
name varchar(25)
...
table clases_taken
student_id int (foreign key to student.id)
class_id int (foreign key to class.id)
data_started datatime
result varchar(5)
tutor_id int (foreign key to tutor.id)
...
You should never have columns like class1, class2, class3, class4 etc in a database table. What you should have is a related table. Your stucture would be something like:
Student Table with the following columns
StudentID
StudentLastName
StudentFirstName
(and so forth for all the data to describe a student)
Then
Course table with the following columns
CourseId
CourseName
Then
StudentCourse Table with the following columns
StudentId
CourseID
CourseDate
Now to find out what courses the person took you join these tables together.
Something like:
Select StudentID,StudentLastName,StudentFirstName, CourseName, CourseDate
from Student
join StudentCourse on student. studentid = StudentCourse.StudentID
join Course on Course.courseID = StudentCourse.CourseID
Please read this link to start learning database fundamentals:
http://www.deeptraining.com/litwin/dbdesign/FundamentalsOfRelationalDatabaseDesign.aspx
How about no class columns in the student table. Setup a new table with student id and class id columns. Each row represents a class the student took. Maybe add more columns such as: the year/semester, grade, etc.

Categories

Resources