How to insert a list of values - sql

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

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.

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;

Multiple joins, show one value if it occurs more than once

Say I have 3 tables below:
Table1 t1
id c1 c2
1 Jon Doe
2 James SMith
3 jolly rancher
Table2 t2
id c1
1 addr_1
1 addr_2
2 addr_1
2 addr_2
3 addr_1
Table3 t3
id c1
1 phone_1
2 phone_1
3 phone_1
3 phone_2
What i am trying to get is this:
1 Jon Doe addr_1 phone_1
1 addr_2
2 James SMith addr_1 phone_1
2 addr_2
3 jolly rancher addr_1 phone_1
3 phone_2
So that id is there for each output but other fields only once per that grouping—is this possible?
Is it possible? Yes. Is it ideal? Not really. I have to agree with D Stanley that it is definitely better to handle something like this in the app/reporting layer if you can.
However, if you really wanted to do this in SQL, you can see my solution below or go to the SQL fiddle here. This solution is not ideal because it's only working off the sample data you provided. If you have data where there are at least 2 phone numbers and at least 2 addresses for a given person, then the SQL below would need to be modified to handle that scenario. I've renamed the tables and columns to something a little more meaningful so that it's easier to read.
create table person(
id int,
firstName varchar(20),
lastName varchar(20)
)
create table address(
id int,
addr varchar(20)
)
create table phonenumber(
id int,
phoneNum varchar(20)
)
insert person (id, firstName, lastName)select 1, 'jon', 'doe'
insert person (id, firstName, lastName)select 2, 'james', 'smith'
insert person (id, firstName, lastName)select 3, 'jolly', 'rancher'
insert address(id, addr)select 1, 'addr_1'
insert address(id, addr)select 1, 'addr_2'
insert address(id, addr)select 2, 'addr_1'
insert address(id, addr)select 2, 'addr_2'
insert address(id, addr)select 3, 'addr_1'
insert phonenumber(id, phoneNum)select 1, 'phone_1'
insert phonenumber(id, phoneNum)select 2, 'phone_1'
insert phonenumber(id, phoneNum)select 3, 'phone_1'
insert phonenumber(id, phoneNum)select 3, 'phone_2'
CREATE TABLE #results(
id int,
firstName varchar(20),
lastName varchar(20),
address varchar(20),
phoneNumber varchar(20)
)
CREATE TABLE #finalresults(
id int,
firstName varchar(20),
lastName varchar(20),
address varchar(20),
phoneNumber varchar(20)
)
INSERT #results
(
id, firstName, lastName, address, phoneNumber
)
SELECT Person.id, Person.firstName, Person.lastName, address.addr, phonenumber.phoneNum
FROM Person
INNER JOIN address
ON person.id = address.id
INNER JOIN phonenumber
ON person.id = phonenumber.id
DECLARE #id int, #firstname varchar(20), #lastname varchar(20),
#address varchar(20), #phonenumber varchar(20)
DECLARE my_cursor CURSOR
FOR SELECT * FROM #results
OPEN my_cursor
FETCH NEXT FROM my_cursor INTO #id, #firstname, #lastname, #address, #phonenumber
while ##fetch_status = 0
BEGIN
IF NOT EXISTS (SELECT 1 FROM #finalresults WHERE id = #id)
BEGIN
INSERT #finalresults(id, firstname, lastname, address, phonenumber)
SELECT #id, #firstname, #lastname, #address, #phonenumber
END
ELSE
BEGIN
INSERT #finalresults(id, firstname, lastname, address, phonenumber)
SELECT distinct #id, '', '',
CASE WHEN finalAddress.address IS NOT NULL
THEN ''
ELSE #address
END,
CASE WHEN finalPhoneNumber.phonenumber IS NOT NULL
THEN ''
ELSE #phonenumber
END
FROM #finalresults
LEFT OUTER JOIN #finalresults finalAddress
ON finalAddress.id = #id
AND finalAddress.address = #address
LEFT OUTER JOIN #finalresults finalPhoneNumber
ON finalPhoneNumber.id = #id
AND finalPhoneNumber.phonenumber = #phonenumber
END
FETCH NEXT FROM my_cursor INTO #id, #firstname, #lastname, #address, #phonenumber
END
CLOSE my_cursor
DEALLOCATE my_cursor
SELECT * FROM #finalresults

I need to split string in select statement and insert to table

I have a data in one table. I need to copy it to another table. One of the column is text delimited string. So what I'm thinking to select all columns insert get indentity value and with subquery to split based on delimiter and insert it to another table.
Here is the data example
ID Name City Items
1 Michael Miami item|item2|item3|item4|item5
2 Jorge Hallandale item|item2|item3|item4|item5
copy Name, City to one table get identity
and split and copy Items to another table with Identity Column Value
So output should be
Users table
UserID Name City
1 Michael Miami
2 Jorge Hallandale
...
Items table
ItemID UserID Name
1 1 Item
2 1 Item2
3 1 Item3
4 1 Item4
5 2 Item
6 2 Item2
7 2 Item3
8 2 Item4
Not really sure how to do it with T-SQL. Answers with examples would be appreciated
You may create you custom function to split the string in T-Sql. You could then use the Split function as part of a JOIN with your base table to generate the final results for your INSERT statement. Have a look at this post. Hope this help.
You can do this using xml and cross apply.
See the following:
DECLARE #t table (ID int, Name varchar(20), City varchar(20), Items varchar(max));
INSERT #t
SELECT 1,'Michael','Miami' ,'item|item2|item3|item4|item5' UNION
SELECT 2,'Jorge' ,'Hallandale','item|item2|item3|item4|item5'
DECLARE #u table (UserID int identity(1,1), Name varchar(20), City varchar(20));
INSERT #u (Name, City)
SELECT DISTINCT Name, City FROM #t
DECLARE #i table (ItemID int identity(1,1), UserID int, Name varchar(20));
WITH cte_Items (Name, Items) as (
SELECT
Name
,CAST(REPLACE('<r><i>' + Items + '</i></r>','|','</i><i>') as xml) as Items
FROM
#t
)
INSERT #i (UserID, Name)
SELECT
u.UserID
,s.Name as Name
FROM
cte_Items t
CROSS APPLY (SELECT i.value('.','varchar(20)') as Name FROM t.Items.nodes('//r/i') as x(i) ) s
INNER JOIN #u u ON t.Name = u.Name
SELECT * FROM #i
See more here:
http://www.kodyaz.com/articles/t-sql-convert-split-delimeted-string-as-rows-using-xml.aspx
Can you accomplish this with recursion? My T-SQL is rusty but this may help send you in the right direction:
WITH CteList AS (
SELECT 0 AS ItemId
, 0 AS DelimPos
, 0 AS Item_Num
, CAST('' AS VARCHAR(100)) AS Item
, Items AS Remainder
FROM Table1
UNION ALL
SELECT Row_Number() OVER(ORDER BY UserID) AS ItemId
, UserID
, CASE WHEN CHARINDEX('|', Remainder) > 0
THEN CHARINDXEX('|', Remainder)
ELSE LEN(Remainder)
END AS dpos
, Item_num + 1 as Item_Num
, REPLACE(Remainder, '|', '') AS Element
, right(Remainder, dpos+1) AS Remainder
FROM CteList
WHERE dpos > 0
AND ItemNum < 20 /* Force a MAX depth for recursion */
)
SELECT ItemId
, Item
FROM CteList
WHERE item_num > 0
ORDER BY ItemID, Item_Num

SQL Recursive Coalesce

I'm trying to create a column that contains all cities of the referenced addresses.
DECLARE #AddressList nvarchar(max)
SELECT #AddressList = COALESCE(#AddressList + ' ', '') + City FROM [Address]
SELECT
Employee.*,
(SELECT #AddressList) AS AddressCities
FROM Employee
But I dont know where to put the WHERE clause.
...
(SELECT #AddressList WHERE EmployeeId = Employee.EmployeeId) AS AddressCities
...
The above test doesnt work..
Table schemas are:
Employee
EmployeeId
Name
Address
Street
City
EmployeeId
If i understand you correctly, you wish to show all Cities in a single column for the employee. So you wish to GROUP BY and CONCAT.
Using Sql Server 2005, try this (working example)
DECLARE #Employee TABLE(
EmployeeId INT,
NAME VARCHAR(100)
)
INSERT INTO #Employee (EmployeeId,[NAME]) SELECT 1, 'A'
INSERT INTO #Employee (EmployeeId,[NAME]) SELECT 2, 'B'
DECLARE #Address TABLE(
Street VARCHAR(50),
City VARCHAR(50),
EmployeeId INT
)
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'A','A', 1
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'B','B', 1
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'C','C', 1
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'D','D', 2
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'E','E', 2
INSERT INTO #Address (Street,City, EmployeeId) SELECT 'F','F', 2
SELECT e.EmployeeId,
e.[NAME],
(
SELECT al.City + ','
FROM #Address al
WHERE al.EmployeeId = e.EmployeeId
FOR XML PATH('')
)
FROM #Employee e
GROUP BY e.EmployeeId,
e.[NAME]
Do need more information about what you mean by 'column that contains all cities'. How is what you want different to the following might help you phrase the question
SELECT e.EmployeeId,e.Name,a.City
FROM Employee e
INNER JOIN Address a ON a.EmployeeId = e.EmployeeId
GROUP BY e.EmployeeId,e.Name
-- update
I think I see what you mean, do you want like:
EmployeeID | Name | Address
1 | John | 'London','Paris','Rome'
2 | Jane | 'New York','Miami'
?