Combining more than one piece of data in a cell. sql - sql

This is for a Homework assignment, but I want to go a step farther.
Let me show my Tables then I'll ask my question.
Table -- Students
StudentID PK, LastName, FirstName,
Table -- Courses
CourseID PK, CourseName
Table -- Registrations
StudentID FK, CourseID FK
The question is How can I add more than one CourseName in that specific cell? For Example I have one student who is taking 3 classes, can I show all 3 CourseNames for that particular student in the same cell in the same row?
Example.......
123456, Smith, John, English, Math, Science
Sorry if this seems simplistic but I just can't find what I'm looking for after searching.

You don't put them into 1 cell.
It will be 3 rows. An example will make this a bit more clear:
John Smith: ID 1025
Math ID 2500
English ID 2585
Violin ID 3250
In your database you will get the following rows:
StudentID CourseID
1025 2500
1025 2585
1025 3250

Well in the spirit of Christmas you can always imagine it's a turkey and STUFF it..
DECLARE #Students TABLE(StudentID INT, LastName VARCHAR(50), FirstName VARCHAR(50))
DECLARE #Courses TABLE(CourseID INT, CourseName VARCHAR(50))
DECLARE #Registrations TABLE(StudentID INT, CourseID INT)
INSERT INTO #Students VALUES
(123456, 'John', 'Smith'),(123457, 'Adrian', 'Sullivan'),(123458, 'Dude', 'Guy')
INSERT INTO #Courses VALUES
(1,'English'),(2,'Math'),(3,'Science')
INSERT INTO #Registrations VALUES
(123456,1),(123456,2),(123456,3),(123457,1),(123457,2),(123458,3)
DECLARE #STID INT
SELECT *, STUFF((SELECT ','+C2.CourseName
FROM #Registrations R2
INNER JOIN #Courses C2 ON C2.CourseID = R2.CourseID
WHERE R2.StudentID = S.StudentID
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 1, '') [AllText]
FROM #Students S
For more reading on STUFF

Related

T-SQL - Insert Data into Parent and Child Tables

Code:
CREATE TYPE dbo.tEmployeeData AS TABLE
(
FirstName NVARCHAR(50),
LastName NVARCHAR(50),
DepartmentType NVARCHAR(10),
DepartmentBuilding NVARCHAR(50),
DepartmentEmployeeLevel NVARCHAR(10),
DepartmentTypeAMetadata NVARCHAR(100),
DepartmentTypeBMetadata NVARCHAR(100)
)
GO
CREATE PROC dbo.EmployeeImport
(#tEmployeeData tEmployeeData READONLY)
AS
BEGIN
DECLARE #MainEmployee TABLE
(EmployeeID INT IDENTITY(1,1),
FirstName NVARCHAR(50),
LastName NVARCHAR(50))
DECLARE #ParentEmployeeDepartment TABLE
(EmployeeID INT,
ParentEmployeeDepartmentID INT IDENTITY(1,1),
DepartmentType NVARCHAR(10))
DECLARE #ChildEmployeeDepartmentTypeA TABLE
(ParentEmployeeDepartmentID INT,
DepartmentBuilding NVARCHAR(50),
DepartmentEmployeeLevel NVARCHAR(10),
DepartmentTypeAMetadata NVARCHAR(100))
DECLARE #ChildEmployeeDepartmentTypeB TABLE
(ParentEmployeeDepartmentID INT,
DepartmentBuilding NVARCHAR(50),
DepartmentEmployeeLevel NVARCHAR(10),
DepartmentTypeBMetadata NVARCHAR(100))
-- INSERT CODE GOES HERE
SELECT * FROM #MainEmployee
SELECT * FROM #ParentEmployeeDepartment
SELECT * FROM #ChildEmployeeDepartmentTypeA
SELECT * FROM #ChildEmployeeDepartmentTypeB
END
GO
DECLARE #tEmployeeData tEmployeeData
INSERT INTO #tEmployeeData (FirstName, LastName, DepartmentType,
DepartmentBuilding, DepartmentEmployeeLevel,
DepartmentTypeAMetadata, DepartmentTypeBMetadata)
SELECT
N'Tom_FN', N'Tom_LN', N'A',
N'101', N'IV', N'Tech/IT', NULL
UNION
SELECT
N'Mike_FN', N'Mike_LN', N'B',
N'OpenH', N'XII', NULL, N'Med'
UNION
SELECT
N'Joe_FN', N'Joe_LN', N'A',
N'101', N'IV', N'Tech/IT', NULL
UNION
SELECT
N'Dave_FN', N'Dave_LN', N'B',
N'OpenC', N'XII', NULL, N'Lab'
EXEC EmployeeImport #tEmployeeData
GO
DROP PROC dbo.EmployeeImport
DROP TYPE dbo.tEmployeeData
Notes:
The table variables are replaced by real tables in live environment.
EmployeeID and ParentEmployeeDepartmentID columns' values don't always match each other. Live environment has more records in the udt (tEmployeeData) than just 4
Goal:
The udt (tEmployeeData) will be passed into the procedure
The procedure should first insert the data into the #MainEmployee table (and get the EmployeeIDs)
Next, the procedure should insert the data into the #ParentEmployeeDepartment table (and get the ParentEmployeeDepartmentID) - note EmployeeID is coming from the previous output.
Then, the procedure should split the child level data based on the DepartmentType ("A" = insert into #ChildEmployeeDepartmentTypeA and "B" = insert into #ChildEmployeeDepartmentTypeB).
ParentEmployeeDepartmentID from #ParentEmployeeDepartment should be used when inserting data into either #ChildEmployeeDepartmentTypeA or #ChildEmployeeDepartmentTypeB
The procedure should should run fast (need to avoid row by row operation)
Output:
#MainEmployee:
EmployeeID FirstName LastName
---------------------------------
1 Tom_FN Tom_LN
2 Mike_FN Mike_LN
3 Joe_FN Joe_LN
4 Dave_FN Dave_LN
#ParentEmployeeDepartment:
EmployeeID ParentEmployeeDepartmentID DepartmentType
-------------------------------------------------------
1 1 A
2 2 B
3 3 A
4 4 B
#ChildEmployeeDepartmentTypeA:
ParentEmployeeDepartmentID DepartmentBuilding DepartmentEmployeeLevel DepartmentTypeAMetadata
---------------------------------------------------------------------------------------------------------
1 101 IV Tech/IT
3 101 IV Tech/IT
#ChildEmployeeDepartmentTypeB:
ParentEmployeeDepartmentID DepartmentBuilding DepartmentEmployeeLevel DepartmentTypeAMetadata
----------------------------------------------------------------------------------------------------------
2 OpenH XII Med
4 OpenC XII Lab
I know I can use the OUTPUT clause after the insert and get EmployeeID and ParentEmployeeDepartmentID, but I'm not sure how to insert the right child records into right tables with right mapping to the parent table. Any help would be appreciated.
Here is my solution (based on the same answer I've linked to in the comments):
First, you must add another column to your UDT, to hold a temporary ID for the employee:
CREATE TYPE dbo.tEmployeeData AS TABLE
(
FirstName NVARCHAR(50),
LastName NVARCHAR(50),
DepartmentType NVARCHAR(10),
DepartmentBuilding NVARCHAR(50),
DepartmentEmployeeLevel NVARCHAR(10),
DepartmentTypeAMetadata NVARCHAR(100),
DepartmentTypeBMetadata NVARCHAR(100),
EmployeeId int
)
GO
Populating it with that new employeeId column:
DECLARE #tEmployeeData tEmployeeData
INSERT INTO #tEmployeeData (FirstName, LastName, DepartmentType,
DepartmentBuilding, DepartmentEmployeeLevel,
DepartmentTypeAMetadata, DepartmentTypeBMetadata, EmployeeId)
SELECT
N'Tom_FN', N'Tom_LN', N'A',
N'101', N'IV', N'Tech/IT', NULL, 5
UNION
SELECT
N'Mike_FN', N'Mike_LN', N'B',
N'OpenH', N'XII', NULL, N'Med', 6
UNION
SELECT
N'Joe_FN', N'Joe_LN', N'A',
N'101', N'IV', N'Tech/IT', NULL, 7
UNION
SELECT
N'Dave_FN', N'Dave_LN', N'B',
N'OpenC', N'XII', NULL, N'Lab', 8
Insert part goes here
Then, you use a table variable to map the inserted value from the employee table to the temp employee id in the data you sent to the procedure:
DECLARE #EmployeeidMap TABLE
(
temp_id int,
id int
)
Now, the trick is to populate the employee table with the MERGE statement instead of an INSERT...SELECT because you have to use values from both inserted and source data in the output clause:
MERGE INTO #MainEmployee USING #tEmployeeData AS sourceData ON 1 = 0 -- Always not matched
WHEN NOT MATCHED THEN
INSERT (FirstName, LastName)
VALUES (sourceData.FirstName, sourceData.LastName)
OUTPUT sourceData.EmployeeId, inserted.EmployeeID
INTO #EmployeeidMap (temp_id, id); -- populate the map table
From that point on it's simple, you need to join the data you sent to the #EmployeeidMap to get the actual employeeId:
INSERT INTO #ParentEmployeeDepartment (EmployeeID, DepartmentType)
SELECT Id, DepartmentType
FROM #tEmployeeData
INNER JOIN #EmployeeidMap ON EmployeeID = temp_id
Now you can use the data in #ParentEmployeeDepartment to map the actual values in ParentEmployeeDepartmentID to the data you sent:
Testing the inserts so far
SELECT FirstName,
LastName,
SentData.DepartmentType As [Dept. Type],
DepartmentBuilding As Building,
DepartmentEmployeeLevel As [Emp. Level],
DepartmentTypeAMetadata As [A Meta],
DepartmentTypeBMetadata As [B Meta],
SentData.EmployeeId As TempId, EmpMap.id As [Emp. Id], DeptMap.ParentEmployeeDepartmentID As [Dept. Id]
FROM #tEmployeeData SentData
INNER JOIN #EmployeeidMap EmpMap ON SentData.EmployeeId = temp_id
INNER JOIN #ParentEmployeeDepartment DeptMap ON EmpMap.id = DeptMap.EmployeeID
results:
FirstName LastName Dept. Type Building Emp. Level A Meta B Meta TempId Emp. Id Dept. Id
--------- -------- ---------- -------- ---------- ------ ------ ------ ----------- -----------
Dave_FN Dave_LN B OpenC XII NULL Lab 8 1 1
Joe_FN Joe_LN A 101 IV Tech/IT NULL 7 2 2
Mike_FN Mike_LN B OpenH XII NULL Med 6 3 3
Tom_FN Tom_LN A 101 IV Tech/IT NULL 5 4 4
I'm sure that from this point you can easily figure out the last 2 inserts yourself.

How to insert a list of values

I’m trying to create a stored procedure that is automatically able to populate a list of players in a team based on the team’s ID.
What I’m trying to do is say that each team has 20 players, their first name matches the team name and their surname should begin with ‘Player’ and end with a number based on a count for the relevant team e.g.
First Name Surname TeamID
AFC Wimbledon Player1 1
AFC Wimbledon Player2 1
AFC Wimbledon Player3 1
--All the way to 20 players
Angers Player1 2
I am not sure how to do this and need little guidance or a code sample so I can look to see how it’s done. I thought I may need an OUTPUT to help solve this but I think my logic is terribly flawed.
Here are the tables:
Team:
TeamID TeamName
1 AFC Wimbledon
2 Angers
3 Ards
Player (currently empty as need this to be inserted to:
PlayerID FirstName Surame TeamID
Below is my PROC currently:
CREATE PROCEDURE [dbo].[Player_Insert]
#FirstName VARCHAR(25) OUTPUT,
#Surname VARCHAR(25) OUTPUT,
#TeamName VARCHAR(50)
AS
SET NOCOUNT ON
BEGIN
DECLARE #TeamID INT, #CountPlayers INT
SELECT #TeamID = TeamID FROM Team WHERE TeamName = #TeamName
SELECT #CountPlayers = count(*) FROM Player WHERE TeamID = #TeamID
IF #CountPlayers >= 20
BEGIN
RAISERROR('Reached the maximum limit of players for %s', 16, 1, #TeamName);
RETURN;
END
SET #FirstName = #TeamName
SET #Surname = 'Player' + #CountPlayers
INSERT INTO Player (FirstName, Surname, TeamID)
VALUES (#FirstName, #Surname, #TeamID)
END
Below is the EXEC PROC:
DECLARE #FirstNameOutput VARCHAR(25)
DECLARE #SurnameOutput VARCHAR(25)
EXEC Player_Insert
#FirstName = #FirstNameOutput OUTPUT,
#Surname = #SurnameOutput OUTPUT,
#TeamId = 1
You will need NUMBERS table .Here are ways to create this
DEMO here :
---Table creattion script
create table #t
(
teamid int,
teamname varchar(100)
)
insert into #t
select '1','AFC Wimbledon'
union all
select '2','Angers'
union all
select '3 ','Ards'
--final query
SELECT teamname as firstname,
'player'+ cast(n as varchar(5)) AS [surname],
teamid
FROM
#t
CROSS APPLY
(select n from numbers nmbr where nmbr.n<=20) b
order by teamid
This should get you what you need.
;WITH TallyTable
AS (
SELECT TOP 20 ROW_NUMBER() OVER (
ORDER BY a.id
) AS N
FROM Master.dbo.SysColumns a
,Master.dbo.SysColumns b
)
INSERT INTO Player (FirstName, Surname, TeamID)
SELECT TeamName AS FirstName
,'Player' + CAST(N AS VARCHAR(2)) AS Surname
,TeamID
FROM Team
CROSS JOIN TallyTable
ORDER BY TeamID, N

Matching First and Last Name on two different tables

I am trying to match the first name varchar (50) and last name varchar(50) from table A to the first name varchar(50) and last name varchar(50) on table B. The issue is that both table contain a lot of shortened first names like the name Andrew in table A and there might be a matching record with the last name but the first name is Andy so it comes up as not a match. Is there anyway to get around this in SQL. The shortened names is a vice verse problem meaning that both Table A and Table B have some shortened names.
Here are some more examples:
This is my current code.
Select *
FROM TableA p
JOIN TableB e ON e.CompanyNumber = 1 and e.LastName like '%' + rtrim(ltrim(p.lastname)) + '%'
and e.FirstName like '%' + ltrim(rtrim(p.firstname)) + '%'
NOTE: This is the only way to match the tables together.
Create a third table that associates Long-form and short-form names.
For examle:
Long Form Short Form
Andrew Andy
Andrew Drew
David Dave
William Will
William Bill
William Billy
William Willy
Provided you use a 3rd Table to hold you Long/Short Names as so.
CREATE TABLE TableNames
([Id] int, [OfficialName] varchar(7), [Alias] varchar(7))
;
INSERT INTO TableNames
([Id], [OfficialName], [Alias])
VALUES
(1, 'Andrew', 'Andy'),
(2, 'Andrew', 'Andrew'),
(3, 'William', 'Bill'),
(4, 'William', 'William'),
(5, 'David', 'Dave'),
(6, 'David', 'David')
The following query should give you what you are looking for.
SELECT *
FROM (
SELECT TableA.Id AS T1_Id
,CompanyId AS T1_CompanyId
,FirstName AS T1_FirstName
,LastName AS T1_LastName
,TableNames.OfficialName AS OfficialName
FROM tableA
INNER JOIN tableNames ON TableA.FirstName = TableNames.Alias
) T1
,(
SELECT tableB.Id AS T2_Id
,CompanyId AS T2_CompanyId
,FirstName AS T2_FirstName
,LastName AS T2_LastName
,TableNames.OfficialName AS OfficialName
FROM tableB
INNER JOIN tableNames ON TableB.FirstName = TableNames.Alias
) T2
WHERE T1.T1_CompanyId = T2.T2_CompanyId
AND T1.OfficialName = T2.OfficialName
AND T1.T1_LastName = T2.T2_LastName
I set up my solution sqlfiddle at http://sqlfiddle.com/#!3/64514/2
I hope this helps.
Select *
<br>FROM TableA pJOIN TableB e
<br>ON e.CompanyNumber = 1
<br>and e.LastName like '%' + rtrim(ltrim(p.lastname)) + '%'
<br>OR
<br>e.FirstName like '%' + ltrim(rtrim(p.firstname)) + '%'
Now this depends how you determine it is match,
example:
TableA:
--------
Rownum FristName LastName
1 Andy Smith
2 Andy Mathew
TableB:
--------
Rownum FristName LastName
1 Logan Andy
2 Mathew Andy
Now will you consider first record from both the tables as a match
What about the second record in both the tables?
Basing on this we can even change the query

MS SQL Stored procedure to get the value

I have 3 tables
Staff table:
EmpId CandidateId
------------------------
1 2
Candidate table:
CandidateId Firstname Last name CountryId PassportCountry
--------------------------------------------------------------------
1 Mark Antony 2 3
2 Joy terry 1 3
Country:
CountryId Name
---------------------------
1 USA
2 UK
3 Australia
User will pass the EmpId in the querystring I need to show the candidate details according to the empId. I have only one country table and using that table for country, passportport country. So I need to get the country name when I get the candidate value.
How to write the stored procedure to get the candidate details. Im not good in sql. Can you guys help me on this. Thanks in advance.
Hi I tried the below script to get the country name and passport country name. I can get the country name, but not the passport country.
SELECT
FirstName,
LastName,
PassportCountry,
Country.CountryName as Country
from Candidate
inner join Country
on country.CountryId=candidate.country
where CandidateId=#CandidateId;
This should get you going in the right direction.
Basically, since you're referencing the Country table twice, you need to join it twice.
declare #staff table (EmpId int, CandidateId int)
insert into #staff values (1, 2 )
declare #Candidate table (CandidateId int, Firstname nvarchar(50), Lastname nvarchar(50), CountryId int, PassportCountry int)
insert into #Candidate
values (1, 'Mark', 'Antony', 2, 3),
(2, 'Joy', 'Terry', 1, 3)
declare #Country table (CountryId int, Name nvarchar(50))
insert into #Country
values (1, 'USA'),
(2, 'UK'),
(3, 'Australia')
declare #empID int
set #empID = 1
SELECT
FirstName,
LastName,
t2.Name as PersonCountry,
t3.Name as PassportCountry
from #staff s
inner join #Candidate t1 on s.CandidateId = t1.CandidateId
inner join #Country t2 on t1.CountryId=t2.CountryId
inner join #Country t3 on t1.PassportCountry=t3.CountryId
where s.EmpId=#empID;

Distinct over one column with value comparing on another column ICriteria NHibernate

I have table and object called Person. I have problem to create a distinct (over column "lastname") criteria. I want to get only the oldest Person with distinct lastnames.
For example i have (properties: firstname, lastname, age):
John Smith, 52
Jessica Smith, 45
Ann Pit, 21
Brad Pit, 30
Can anybody help me to create criteria which result i get Person object with John Smith and Brad Pit?
Probably the best approach here is to use EXISTS to filter the result set, first a SQL example to get the logic correct:
DECLARE #Person TABLE (
Id INT,
Firstname VARCHAR(20),
Lastname VARCHAR(20),
Age INT
)
INSERT INTO #Person VALUES (1, 'Brad', 'Pitt', 42)
INSERT INTO #Person VALUES (2, 'Angelina', 'Pitt', 45)
INSERT INTO #Person VALUES (3, 'John', 'Smith', 50)
INSERT INTO #Person VALUES (4, 'Jane', 'Smith', 55)
SELECT P.* FROM #Person P
WHERE EXISTS(
SELECT SUB.LastName, MAX(SUB.Age) as Age FROM #Person SUB
GROUP BY SUB.LastName
HAVING SUB.LastName = P.LastName AND MAX(SUB.Age) = P.Age)
This yields the following results which is as expected:
Id Firstname Lastname Age
-------------------------------
2 Angelina Pitt 45
4 Jane Smith 55
Now to convert to nHibernate, this effectively builds the same query as above:
var subQuery = DetachedCriteria.For<Person>("SUB")
.SetProjection(Projections.ProjectionList()
.Add(Projections.GroupProperty("LastName"), "Lastname")
.Add(Projections.Max("Age"), "Age"))
.Add(Restrictions.EqProperty(Projections.Max("Age"), "P.Age")
.Add(Restrictions.EqProperty("LastName", "P.LastName"));
return session.CreateCriteria<Person>("P")
.Add(Subqueries.Exists(subQuery))
.List<Person>();