Merge into table with parameter - sql-server

I'm stuck and i know that this is easier than i think it is.
I have a table that looks like this:
SELECT ID , NAME , CITY FROM TEMP
I have created a Merge statement with Parameters that should insert a new row if ID and City does not already exist
ALTER PROCEDURE [dbo].[sp_TMP]
#ID INT,
#City NVARCHAR(50)
AS
MERGE TEMP AS TARGET
USING
(
SELECT
ID,
NAME,
CITY
FROM TEMP
) AS SOURCE
ON [TARGET].[ID]= #ID AND [TARGET].[City] = #City
WHEN NOT MATCHED THEN
INSERT
(
[ID],
[NAME],
[CITY]
)
VALUES
(
#ID,
[SOURCE].[NAME],
#City
);
I know im doing something wrong here because ALLA the records gets effected.
I would like a outcome of this (if #ID = '1' and #City = 'New York')
I would like to use a MERGE() and not "Insert into" if possible.

First I'd like to say the consistency of a solution like this isn't good at all.
However, in order for it to work as you described in the comments, change the code like so:
ALTER PROCEDURE [dbo].[sp_TMP]
#ID INT,
#City NVARCHAR(50)
AS
MERGE TEMP AS TARGET
USING
(
SELECT
ID,
NAME,
CITY
FROM TEMP
) AS SOURCE
ON [TARGET].[ID]= #ID AND [TARGET].[City] = #City
WHEN NOT MATCHED THEN
INSERT
(
[ID],
[NAME],
[CITY]
)
VALUES
(
#ID,
(SELECT TOP(1) t.Name FROM TEMP t WHERE t.ID = #ID),
#City
);
Should do the trick.
Also, I'm not sure why you want to use MERGE only, but this would be a good candidate for IF NOT EXISTS ... INSERT INTO like so:
ALTER PROCEDURE [dbo].[sp_TMP]
#ID INT,
#City NVARCHAR(50)
AS
IF NOT EXISTS (SELECT TOP(1) 1 FROM TEMP WHERE ID = #ID AND City = #City)
INSERT INTO TEMP(ID, City, Name)
VALUES (#ID, #City, (SELECT TOP(1) Name FROM TEMP WHERE ID = #ID));

If you are using the code first approach you simply use [Index(IsUnique = true)] try this.Ex:
[Required]
[Column(TypeName = "varchar")]
[StringLength(100)]
[Index(IsUnique = true)]
public string Name { get; set; }

Related

Update but only what is different (what've changed)

I've tried something like this:
UPDATE Person
SET Name = #Name, Phone = #Phone, Email = #Email, Status = #Status
WHERE Id = #Id AND Name != #Name AND Phone != #Phone AND Email != #Email AND Status != #Status;
But isn't working.
If Id is your primary key then you probably want:
UPDATE Person
SET Name = #Name, Phone = #Phone, Email = #Email, Status = #Status
WHERE Id = #Id AND
(Name != #Name OR Phone != #Phone OR Email != #Email OR Status != #Status);
But there generally isn't any harm in updating columns to the same value unless you have some sort of trigger that you don't want to run. Even with the code above, if only one of the column values changes, you're going to "update" the other three to their same value.
Here is another way to prevent unnecessary updates.
It is using set based operator INTERSECT, and it won't trip on NULL values in comparison:
AND (Name != #Name OR Phone != #Phone OR Email != #Email)
The solution is using UpdatedOn DATETIMEOFFSET(3) column to track the updated DateTime.
SQL
-- DDL and sample data population, start
DECLARE #person TABLE (
ID INT PRIMARY KEY,
Name VARCHAR(20),
Phone VARCHAR(15),
Email VARCHAR(128),
UpdatedOn DATETIMEOFFSET(3)
);
INSERT #person (ID, Name, Phone, Email, UpdatedOn) VALUES
(1, 'Peter', '1-305-803-1234', 'peter#gmail.com', NULL),
(2, 'Paul', NULL, 'paul#gmail.com', NULL);
-- DDL and sample data population, end
-- before
SELECT * FROM #person;
DECLARE #ID INT = 1
, #Name VARCHAR(20) = 'Peter' -- try 'PeterZ'
, #Phone VARCHAR(15) = '1-305-803-1234'
, #Email VARCHAR(128) = 'peter#gmail.com';
;WITH rs AS
(
SELECT #ID AS ID
, #Name AS NAme
, #Phone AS Phone
, #Email AS Email
)
UPDATE T
SET Name = S.Name, Phone = S.Phone, Email = S.Email
, T.UpdatedOn = SYSDATETIMEOFFSET()
FROM #person AS T -- target
INNER JOIN rs AS S -- source
ON T.ID = S.ID
WHERE NOT EXISTS (SELECT S.*
INTERSECT SELECT T.ID, T.Name, T.Phone, T.Email);
-- test
SELECT * FROM #person;

How to insert data into a temporary table using an existing table and new columns

I am trying to insert data into a temporary table within my stored procedure. The data is selected from an existing table and creating new columns with concatenated data. I'm getting an error that the column name or number of supplied values does not match table definition. I'm pretty certain that the code in my application is correct so I believe the issue is with the way I'm storing the data in a temporary table.
Here is my proc:
AS
BEGIN
CREATE TABLE #TempTable
(
[ID] [varchar](10),
[FIRST_NAME] varchar(50),
[LAST_NAME] varchar(50),
[WEBSITE_LINK] varchar(200)
)
INSERT INTO #TempTable
SELECT USER.ID,USER.FIRSTNAME AS [FIRST_NAME], USER.LASTNAME AS
[LAST_NAME]
FROM USER
WHERE USER.Registered = 'Yes'
DECLARE #Link1 NVARCHAR(100)
DECLARE #Link2 VARCHAR(10)
DECLARE #Link3 NVARCHAR(4)
SET #Link1 = 'http://www.mywebsite.com/user/'
SET #Link2 = (SELECT USER.ID FROM USER WHERE USER.Registered =
'Yes')
SET #Link3 ='/document.doc'
SET #WEBSITE_LINK = (SELECT concat(#Link1,#Link2,#Link3 )AS
[WEBSITE_LINK])
DROP TABLE #TempTable
END
I think this is your problem:
SET #Link2 = (SELECT USER.ID FROM USER WHERE USER.Registered = 'Yes')
What if there are six of them? A single variable can't hold all of them. You can do:
SELECT TOP(1) #Link2 = USER.ID FROM USER WHERE USER.Registered = 'Yes' ORDER BY [SOMETHING];
If the goal is to create a temp table with a full [WEBSITE_LINK], you can do that without all those variables:
BEGIN
CREATE TABLE #TempTable
(
[ID] [varchar](10),
[FIRST_NAME] varchar(50),
[LAST_NAME] varchar(50),
[WEBSITE_LINK] varchar(200)
)
INSERT INTO #TempTable
SELECT DISTINCT u.ID
, [FIRST_NAME] = u.FIRSTNAME
, [LAST_NAME] = u.LASTNAME
, [WEBSITE_LINK] = 'http://www.mywebsite.com/user/' +
CAST(u.ID AS VARCHAR(10)) +
'/document.doc'
FROM [USER] u
WHERE u.Registered = 'Yes'
-- Do something with these values...
DROP TABLE #TempTable
END

How to use output parameter with other columns names in SQL Server

I have a simple table with columns - id, name, and salary.
I want to get the name, salary and annual salary by id using a stored procedure.
I thought of creating a simple stored procedure like this:
CREATE PROCEDURE spGetDetails
#id int,
#annualSal int out
AS
BEGIN
SELECT
name, salary,
#annualSal = (salary * 12)
FROM
tblPrac
WHERE
id = #id
END
But I'm getting an error:
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations
If this qs is already asked please give me the link and I'll delete this qs. I searched but I think I'm missing the key word. Thanks
You don't need an OUTPUT parameter. You can simply query like this -
CREATE PROCEDURE spGetDetails
#id int
AS
BEGIN
SET NOCOUNT ON
SELECT Name, Salary, (Salary*12) AS AnnualSalary FROM tblPrac WHERE id = #id
END
You have stuff mixed so you need to choose between 2 possibilities
CREATE PROCEDURE spGetDetails
#id int,
#name varchar(100) out,
#salary decimal(16,2) out,
#annualSal decimal(16,2) out
AS
BEGIN
set nocount on
SELECT #name = name,
#salary = salary,
#annualSal = (salary * 12)
FROM tblPrac
WHERE id = #id
END
or this
CREATE PROCEDURE spGetDetails
#id int
AS
BEGIN
set nocount on
SELECT name,
salary,
(salary * 12) as anualSal
FROM tblPrac
WHERE id = #id
END
You need to store the data and separate the operations (as the error message is explaining):
CREATE PROCEDURE spGetDetails
(
#id int,
#annualSal int out
)
AS
BEGIN;
SET NOCOUNT ON;
DECLARE #DataSource TABLE
(
[name] VARCHAR(12)
,[salary] DECIMAL(9,2)
);
INSERT INTO #DataSource ([name], [salary])
SELECT name, salary
FROM tblPrac
WHERE id = #id;
SELECT [name]
,[salary]
FROM #DataSource;
SELECT #annualSal = 12 * [salary]
FROM #DataSource;
SET NOCOUNT OFF;
RETURN;
END;

Stored procedure inserting values into a table using Table-Valued Parameters

Given the following simple structure:
TABLE: Product (ProductId, ProductName)
TABLE: Category (CategoryId, CategoryName)
LINK TABLE: ProductId,CategoryId
I have a table type which I want to pass to a stored procedure to insert the values into another table if they don't exist.
CREATE TYPE StringList_TBLType AS TABLE (s NVARCHAR(255) NOT NULL PRIMARY KEY)
I want to do the following in a stored procedure where I pass in a ProductName, and the StringList_TBLType of Category Names
select all the strings from my StringList_TBLType
Insert the string into the Category TABLE if it does not exist
Get the ID of the inserted or already existing Category
Insert the ProductId, and CategoryId into the LINK TABLE.
I could probably struggle along and get something working, but I have little experience with MS SQL, and stored procedures in general, and am scared that I would end up writing a very inefficient way of doing this.
You can use the MERGE statement to capture the category IDs.
DECLARE #changes TABLE (ChangeID VARCHAR(10), Id INTEGER);
DECLARE #JustSomeRandomVariable INT;
MERGE Category AS TARGET
USING #data AS SOURCE
ON TARGET.Category = SOURCE.s
WHEN NOT MATCHED THEN
INSERT ([Category])
VALUES (SOURCE.s)
WHEN MATCHED THEN
UPDATE SET #JustSomeRandomVariable = 1
OUTPUT $action, inserted.Id INTO #changes;
The random variable in the merge statement makes sure that updates get logged into the #changes table variable.
Now you can use the #changes to update your link table.
INSERT INTO Link SELECT ProductID, ChangeID FROM #changes
Just retrieve the required ProductID with a simple select query.
EDIT:
This could potentially result in double records in the Link table. You might need to tweak it a bit, perhaps use the MERGE statement for inserting into the Link table aswell.
#data is the StringList_TBLType paramter of your procedure.
This is what I would suggest (not tested)
CREATE PROCEDURE AddProductToCategories
#productname nvarchar(255),
#categories StringList_TBLType READONLY
AS
BEGIN
DECLARE #productId bigint --change to datatype of Product.ProductId
SET #productId = (SELECT TOP 1 ProductId FROM Product WHERE ProductName = #productname) --What to do if your product names are not unique?
IF #productId is not NULL
BEGIN
DECLARE cur CURSOR FOR (
SELECT cat.CategoryName, cat.CategoryId, c.s
FROM #categories c
RIGHT OUTER JOIN Category cat on c.s = cat.CategoryName
)
DECLARE #id as bigint --change to datatype of Category.CategoryId
DECLARE #name as nvarchar(255) --change to datatype of Category.CategoryName
DECLARE #categoryNameToAdd as nvarchar(255)
OPEN cur
FETCH NEXT FROM cur INTO #name, #id, #categoryNameToAdd
WHILE ##FETCH_STATUS = 0
BEGIN
IF #id is NULL
BEGIN
--category does not exist yet in table Category
INSERT INTO Category (CategoryName) VALUES (#categoryNameToAdd)
SET #id = SCOPE_IDENTITY()
END
INSERT INTO ProductsCategories --your link table
(ProductId, CategoryId)
VALUES
(#productId, #id)
FETCH NEXT FROM cur INTO #name, #id, #categoryNameToAdd
END --cursor
CLOSE cur
DEALLOCATE cur
END --IF #productId
END --sproc

Get highest value of a column in a stored procedure in SQLServer

I have the following stored procedure in SQL Server
IF OBJECT_ID ('kii.p_CreateSection') IS NOT NULL
DROP PROCEDURE kii.p_CreateSection
GO
CREATE PROCEDURE kii.p_CreateSection
#Name AS NVARCHAR(200),
#DocumentId AS INT,
#TypeId AS INT = NULL,
#ReportId AS INT = NULL,
#OrdinalPosition AS SMALLINT
AS
INSERT INTO kii.Section (Name, DocumentId, TypeId, ReportId, OrdinalPosition)
VALUES (#Name, #DocumentId, #TypeId, #ReportId, #OrdinalPosition)
SELECT SCOPE_IDENTITY();
GO
GRANT EXECUTE on kii.p_CreateSection TO p_role_kii
GO
The table Section is related to Document. Each document has several sections and they're ordered by the OrdinalPosistion value.
I'd like to test that if the given value for #OrdinalPosition is 0, then set it at the maximum value of all the sections of this Document +1.
Insert kii.Section( Name, DocumentId, TypeId, ReportId, OrdinalPosition )
Select #Name, #DocumentId, #TypeId, #ReportId
, Case
When #OrdinalPosition <> 0 Then #OrdinalPosition
Else (
Select Max( OrdinalPosition ) + 1
From kii.Section
Where DocumentId = #DocumentId
)
End

Resources