Join on select SQL Server stored procedure - sql

I am trying to create a stored procedure to insert data into 2 tables in SQL Server.
I have tried putting the join in all different positions of the code and still get an error.
CREATE PROCEDURE sp_Insert_Person
#s_FirstName nvarchar(50),
#s_Surname nvarchar(50),
#s_AddressLine1 nvarchar(50),
#s_AddressLine2 nvarchar(50),
#s_Postcode nvarchar(10),
#s_Phone nvarchar(50),
#s_Department nvarchar(50)
AS
BEGIN
INSERT INTO
tbl_person(FirstName, Surname, AddressLine1, AddressLine2,
Postcode, Phone, tbl_Department.Department)
INNER JOIN tbl_person
ON tbl_person.DepartmentID = tbl_Department.DepartmentID
VALUES (#s_FirstName,
#s_Surname,
#s_AddressLine1,
#s_AddressLine2,
#s_Postcode,
#s_Phone,
#s_Department)
END
I have tried the join at the end and at the beginning I have looked all over for insert joins, wondered if i was just getting it all wrong.
I have a department table and a person table and thought I would be able to access the department table through the FK DepartmentID which I have in the Person table, as is the PK in the Department table

I think something like this
INSERT INTO tbl_person
(FirstName,
Surname,
AddressLine1,
AddressLine2,
Postcode,
Phone,
DepartmentID)
Select #s_FirstName,
#s_Surname,
#s_AddressLine1,
#s_AddressLine2,
#s_Postcode,
#s_Phone,
tbl_Department.DepartmentID
from tbl_person
join DepartmentID
ON tbl_person.DepartmentID = tbl_Department.DepartmentID
where tbl_Department.Department = #s_Department

CREATE PROCEDURE sp_Insert_Person
#s_FirstName nvarchar(50),
#s_Surname nvarchar(50),
#s_AddressLine1 nvarchar(50),
#s_AddressLine2 nvarchar(50),
#s_Postcode nvarchar(10),
#s_Phone nvarchar(50),
#s_Department nvarchar(50)
AS
BEGIN
if not Exists(select * from tbl_Department where Department=#s_Department)
insert into tbl_Department (Department) Values (#s_Department)
INSERT INTO tbl_person
(FirstName,
Surname,
AddressLine1,
AddressLine2,
Postcode,
Phone,
DepartmentID)
select #s_FirstName,
#s_Surname,
#s_AddressLine1,
#s_AddressLine2,
#s_Postcode,
#s_Phone,
#s_Department,
DepartmentID
from tbl_Department
where Department=#s_Department
END

Related

Stored procedure with multiple insert

I want a value of #temp table and People table value together. I can use json_modify but it will work only for single value insert. But in case of multiple values, we can make both in JSON object which will be array of object and combine both. But how?
CREATE PROCEDURE [dbo].[SpPersonIns]
(#json nvarchar(max) OUTPUT)
AS
BEGIN
CREATE TABLE #temp (PersonId int)
INSERT INTO People (FirstName, MiddleName, LastName, SSN, UserPersonId)
OUTPUT inserted.PersonId
INTO #temp
SELECT p.firstName, p.middleName, p.lastName, p.ssN, p.userPersonId
FROM OPENJSON(#json) WITH(
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
) as p
declare #p nvarchar(max);
select #p = t.PersonId from #temp as t
SELECT #json = JSON_MODIFY(#json,'$.PersonId',#p)
END
You have two options.
The simplest is to just output the whole inserted and then rebuild the JSON from it
CREATE OR ALTER PROCEDURE [dbo].[SpPersonIns]
#json nvarchar(max) OUTPUT
AS
DECLARE #temp TABLE (
PersonId int,
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
);
INSERT INTO People (FirstName, MiddleName, LastName, SSN, UserPersonId)
OUTPUT inserted.PersonId, inserted.firstName, inserted.middleName, inserted.lastName, inserted.ssN, inserted.userPersonId
INTO #temp (PersonId, firstName, middleName, lastName, ssN, userPersonId)
SELECT p.firstName, p.middleName, p.lastName, p.ssN, p.userPersonId
FROM OPENJSON(#json)
WITH (
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
) as p;
SET #json = (
SELECT *
FROM #temp
FOR JSON PATH
);
Or, if you have a unique natural key within the JSON (for example userPersonId), you can use some kind of join.
CREATE OR ALTER PROCEDURE [dbo].[SpPersonIns]
#json nvarchar(max) OUTPUT
AS
DECLARE #temp TABLE (PersonId int not null, userPersonId int PRIMARY KEY);
INSERT INTO People (FirstName, MiddleName, LastName, SSN, UserPersonId)
OUTPUT inserted.PersonId, inserted.userPersonId
INTO #temp (PersonId, userPersonId)
SELECT p.firstName, p.middleName, p.lastName, p.ssN, p.userPersonId
FROM OPENJSON(#json)
WITH (
firstName varchar(100),
middleName varchar(100),
lastName varchar(100),
ssN varchar(9),
userPersonId int
) as p;
SET #json = (
SELECT
'[' +
STRING_AGG(
JSON_MODIFY(
arr.value,
'$.PersonId',
t.PersonId
),
','
) +
']'
FROM OPENJSON(#json) AS arr -- if you don't specify a schema then you get the whole array object
JOIN #temp t ON t.userPersonId = JSON_VALUE(arr.value, '$.userPersonId')
);

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

ANSI NULLS and the ON clause

Please see the DDL below:
create table #address (ID int IDENTITY, housenumber varchar(30), street varchar(30), town varchar(30), county varchar(30), postcode varchar(30), primary key (id))
insert into #address (housenumber,street,town,county,postcode) values ('1', 'The Street', 'Lincoln', null, 'LN21AA')
insert into #address (housenumber,street,town,county,postcode) values ('1', 'The Street', 'Lincoln', null, 'LN21AA')
insert into #address (housenumber,street,town,county,postcode) values ('1', 'The Street', 'Lincoln', 'Lincolnshire', 'LN21AA')
and the SQL below:
select #address .id as masterid, address2.id as childid from #address inner join #address as address2 on
#address.housenumber=address2.housenumber and #address.street=address2.street
and #address.town=address2.town
and #address.county=address2.county
and #address.postcode=address2.postcode
where #address.id<address2.id
I am trying to identify duplicates.
The 'County' is null sometimes and is not null others. The query above returns no rows.
I have tried this command:
set ansi_nulls off
However, it makes no difference. I realise I can do this:
select #address .id as masterid, address2.id as childid from #address inner join #address as address2 on
#address.housenumber=address2.housenumber and #address.street=address2.street
and #address.town=address2.town
and ((#address.county=address2.county) or (#address.county is null and address2.county is null))
and #address.postcode=address2.postcode
However, I am interested to know why setting ansi nulls to off allows you to do this:
select * from #address where county=null
which returns two rows. However, my first query returns no rows when ANSI NULLs is off. Why does ANSI NULLS have no affect on the ON clause.
I have spend 20 minutes Googling this, however I have not found my answer.
where #address.id
You can identify duplicates by using group by. The following returns the ids when there are two values:
select housenumber, street, town, country postcode, count(*) as cnt,
min(a.id) as masterid, max(a.id) as childid
from #address a
group by housenumber, street, town, country postcode
having count(*) >= 2;
Getting all ids for a given address would require additional joins or funky string aggregations.

Insert a row if it doesn't exist via query

I am trying to write a query that will insert a group of people into a table if that person does not exist. For example, I have table full of people and I need to add more people into the database and I don't know if they are already there. I do know that the social security number (ssn) will never be the same for two people. Could a query be used to check if the ssn is in the table and if not insert the person into the table? If the ssn is in the table then go to the next person and check?
I was thinking about using a stored procedure, but I do not have any rights to create a store procedure.
You can insert your data into a table variable or temp table and then INSERT INTO table from temp table where it does not exists in your table.
DECLARE #Inserted AS TABLE
(
NAME VARCHAR(50)
,SSN DECIMAL(10, 0)
)
INSERT INTO #Inserted
( NAME, SSN )
VALUES ( 'Bob', 123456789 )
, ( 'John', 123546789 )
, ( 'James', 123456798 )
INSERT INTO MyTable
SELECT *
FROM #Inserted AS i
LEFT OUTER JOIN MyTable AS m
ON i.SSN = m.SSN
WHERE m.SSN IS NULL
Here are a couple ideas to get you started. I use MERGE a lot because it offers so much control. You could also look into the IN clause as part of a WHERE predicate in the INSERT SELECT statement.
MERGE
DECLARE #PERSONTABLE TABLE (ID INT PRIMARY KEY IDENTITY(1,1), FirstName VARCHAR(max))
INSERT INTO #PERSONTABLE (FirstName) VALUES ('Bill'),('Sally'),('Bob')
DECLARE #NEWPEOPLE TABLE (FirstName VARCHAR(max))
INSERT INTO #NEWPEOPLE (FirstName) VALUES ('Jim'), ('Sally')
--MERGE
MERGE INTO #PERSONTABLE AS T
USING #NEWPEOPLE AS S
ON (T.FirstName = S.FirstName)
WHEN NOT MATCHED BY TARGET THEN
INSERT (FirstName) VALUES (S.FirstName);
SELECT * FROM #PERSONTABLE
EXCEPT
DECLARE #PERSONTABLE TABLE (ID INT PRIMARY KEY IDENTITY(1,1), FirstName VARCHAR(max))
INSERT INTO #PERSONTABLE (FirstName) VALUES ('Bill'),('Sally'),('Bob')
DECLARE #NEWPEOPLE TABLE (FirstName VARCHAR(max))
INSERT INTO #NEWPEOPLE (FirstName) VALUES ('Jim'), ('Sally')
--EXCEPT
INSERT INTO #PERSONTABLE (FirstName)
SELECT FirstName FROM #NEWPEOPLE
EXCEPT
SELECT FirstName FROM #PERSONTABLE
SELECT * FROM #PERSONTABLE
You could do it like this if the new people are in another table. If not, then use Vladimir's solution.
INSERT INTO People(ssn, firstname, lastname)
SELECT ssn, firstname, lastname
FROM newpeople
WHERE ssn not in (select ssn from people )
INSERT INTO People(ssn, firstname, lastname)
SELECT np.ssn, np.firstname, np.lastname
FROM newpeople np
LEFT JOIN People p on np.ssn = p.ssn
WHERE p.ssn IS NULL
Here's another option I use a lot. Normally joins are better than sub-selects... if the joined table value is null you know you don't have a hit in the joined table.

How to have multiple inserts in one sql query that uses the identities from the previous table

I am trying to insert data into many tables in one SQL Server stored procedure. I am also using the identities from the tables that I have inserted data into to then resolve the many to many relationship by writing those identities to another table.
In theory the logic seems to be there for the stored procedure, but on execution only the first insert statement has been executed. Please could anyone assist with this.
The stored procedure is as follows:
Create Procedure [dbo].[InsertAllCustomerDetails]
(
--#CustomerID Bigint output,
#Firstname varchar(100),
#LastName varchar(100),
#Initials varchar(10),
#Title varchar(20),
#DateCreated datetime,
#isDeleted Bit,
--#ContactNumberID BIGINT Output,
#ContactNumber Varchar(100),
#ContactTypeID bigint,
#Street Varchar(550),
#AreaID BIGINT,
#isPreferred Bit
--#AddressID Bigint OutPut
)
AS
Insert Into Customer
(
FisrtName,
LastName,
Initials,
[Title],
DateCreated,
isDeleted
)
Values
(
#Firstname,
#LastName,
#Initials,
#Title,
#DateCreated,
#isDeleted
)
Declare #CustomerID BIGINT
SELECT #CustomerID = ##IDENTITY
RETURN #CustomerID
--This will now insert the contact details for the customer
Insert Into ContactNumber
(
ContactNumber,
ContactTypeID
)
Values
(
#ContactNumber,
#ContactTypeID
)
Declare #ContactNumberID BIGINT
SELECT #ContactNumberID = ##IDENTITY
--This will insert into the CustomerContactNumber
Insert Into CustomerContactNumber
(
ContactNumberID,
CustomerID
)
Values
(
#ContactNumberID,
#CustomerID
)
--This will insert the address
Insert Into [Address]
(
Street,
AreaID,
isPreferred
)
Values
(
#Street,
#AreaID,
#isPreferred
)
Declare #AddressID BIGINT
SELECT #AddressID = ##IDENTITY
--This will insert the relationship for the customer Address table
Insert into CustomerAddress
(
CustomerID,
AddressID
)
Values
(
#CustomerID,
#AddressID
)
I see two things:
You seem to have a typo in the Customer insert:
Insert Into Customer
(
FisrtName, <-- should be FirstName?
LastName,
You are RETURNing after the Customer insert - that's why only the first one runs
Declare #CustomerID BIGINT
SELECT #CustomerID = ##IDENTITY
RETURN #CustomerID <---- This exits the sproc
--This will now insert the contact details for the customer
Insert Into ContactNumber
I'm guessing the RETURN was there for debugging and not removed since it's obscured by the indentation.