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

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

Related

How To Filter SQL Results Efficiently

I have a requirement to filter the table records based on passed input criteria to the SQL stored procedure. To make it simple, I'm elaborating the question using simple Employee table (I'm using MSSQL server for my project):
Table: Employee(Id, FirstName,LastName, Designation)
The stored procedure takes 3 input arguments (Id, LastName, Designation). This will be invoked with at-least one input parameter (other parameter will be set to empty string (in case of LastName, Designation) or 0 (In case of Id)).
Here is the stored procedure:
Create Procedure GetEmployees
(
#id int,
#lastName varchar(30),
#designation varchar(30)
)
AS
BEGIN
Create Table #employees (Id int, FirstName varchar(30),LastName varchar(30), Designation varchar(30);
IF(#id != 0)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where Id = #id
END
IF(#lastName != ‘’)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where LastName = #lastName
END
IF(designation != ‘’)
BEGIN
Insert Into #employees (Id,FirstName,LastName,Designation)
Select Id, FirstName,LastName,Designation From Employee Where Designation = #designation
END
-- Returning filtered record set to the application layer
Select Id,FirstName,LastName,Designation From #employees;
END
GO
I think there a lot of code repetition in the stored procedure. Is there an efficient way of solving this scenario ?
You may use a single insert query with a WHERE clause which covers all the logic:
BEGIN
INSERT INTO #employees (Id, FirstName, LastName, Designation)
SELECT Id, FirstName, LastName, Designation
FROM Employee
WHERE
(Id = #id AND #id <> 0) OR
(LastName = #lastName AND #lastName <> '') OR
(Designation = #designation AND #designation <> '');
You can use Case condition in Where clause, Find Below Query.
INSERT INTO #employees (Id, FirstName, LastName, Designation)
SELECT Id, FirstName, LastName, Designation
FROM Employee
WHERE
(Id = CASE WHEN #id = 0 THEN ID ELSE #ID END) OR
(LastName = CASE WHEN #lastName = '' THEN LastName ELSE #lastName END) OR
(Designation = CASE WHEN #designation = '' THEN Designation ELSe #designation END);
We can also Use AND Instead of OR in Where clause.
As #trincot said, there is no need for a temporary table. My suggestion is:
Create Procedure GetEmployees
(
#id int,
#lastName varchar(30),
#designation varchar(30)
)
AS
BEGIN
Select Id, FirstName,LastName,Designation From Employee
Where (#id>'' AND Id = #id)
OR (#lastName>'' AND LastName = #lastName)
OR (#designation>'' AND Designation = #designation);
END
GO

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

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;

Insert selected values

Have table users with vallues :
FirstName LastName Age
A A 20
B B 21
C C 22
D D 21
E E 20
Have procedure where I select values from this table , then want sorting and insert to another tables, I do it like :
SELECT #firstName = FirstName ,lastName = LastName ,#age = Age FROM dbo.users
if #age = 20
insert into tbl1(FirstName,LastName) values (#firstName,#LastName )
if #age = 21
insert into tbl2(FirstName,LastName) values (#firstName,#LastName )
if #age = 22
insert into tbl3(FirstName,LastName) values (#firstName,#LastName )
When I do like this ,all rows dont insert to tables, i think need loop or something for do it, can anyone help me ? I want do it without cursor
Wrap these codes inside your procedure.
insert into tbl1(FirstName,LastName) SELECT FirstName ,LastName FROM dbo.users WHERE Age = 20;
insert into tbl2(FirstName,LastName) SELECT FirstName ,LastName FROM dbo.users WHERE Age = 21;
insert into tbl3(FirstName,LastName) SELECT FirstName ,LastName FROM dbo.users WHERE Age = 22;
begin tran
declare #firstName varchar(20)
declare #lastName varchar(20)
declare #age int
SELECT #firstName = firstName,
#lastName = lastName,
#age = Age
FROM dbo.users
if #age = 20
begin
insert into tbl1
select #firstName,#LastName from dbo.users where #age = 20
end
if #age = 21
begin
insert into tbl2
select #firstName,#LastName from dbo.users where #age = 21
end
if #age = 22
begin
insert into tbl3
select #firstName,#LastName from dbo.users where #age = 22
end
--check
select * from tbl1
select * from tbl2
select * from tbl3
rollback tran
--commit tran

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'
?