IF EXISTS (SELECT) in SQL Server not working as expected - sql

I have a code like this:
IF EXISTS (SELECT * FROM table WHERE id = #id)
BEGIN
UPDATE table
SET stock = stock + #stock
WHERE id = #id
END
ELSE
BEGIN
INSERT INTO [table] ([id], [name], [stock])
VALUES (#id, #name, #stock)
END
But, this code isn't working and I am unable to find the root cause for the same. Can someone please help me?

I do not see any error in your code, I tried to replicate the process and it is working fine for me. Can you tell me what is the error you are facing exactly.
The following is the code I tried to replicate your scenario:
CREATE TABLE stocks (
id INT
,NAME VARCHAR(100)
,stock BIGINT
)
CREATE PROCEDURE InsertStocks #id INT
,#name VARCHAR(100)
,#stock BIGINT
AS
BEGIN
IF EXISTS (
SELECT *
FROM stocks
WHERE id = #id
)
BEGIN
UPDATE stocks
SET stock = stock + #stock
WHERE id = #id
END
ELSE
BEGIN
INSERT INTO stocks (
[id]
,[name]
,[stock]
)
VALUES (
#id
,#name
,#stock
)
END
END
INSERT INTO stocks
VALUES (
1
,'abc'
,200
)
INSERT INTO stocks
VALUES (
2
,'abc'
,300
)
INSERT INTO stocks
VALUES (
3
,'abc'
,500
)
EXEC Insertstocks 1
,'abc'
,700
This is updated successfully in my case.

table is a reserved keyword. so I guess you have a trivial syntax error: Incorrect syntax near the keyword 'table'. Wrap it with [], as you already did for INSERT statement
IF EXISTS (
SELECT * FROM [table] WHERE id = #id)
BEGIN
UPDATE [table] SET stock = stock + #stock
WHERE id = #id
END
ELSE
BEGIN
INSERT INTO [table] ([id]
,[name]
,[stock])
VALUES
(
#id,#name,#stock
)
END

Your code and syntax is correct. Let's see a sample example:
if EXISTS(select * from dbo.tbName where Id=1)
BEGIN
print 1
END
ELSE
BEGIN
print 2
END

Related

How can I solve my performance issue in my stored procedure?

How can I get better performance with my sql query in a SP? It has a lot of memory usage.if you look at below my execution pan you will see that :
IF NOT EXISTS(SELECT * FROM Common.[CustomerxxxIds] WHERE xyzType = #xyzType AND CustomerId = #CustomerId)[/code]
has alot of memory usage. How can I reduce that?
ALTER PROCEDURE [Common].[SaveCustomerxxxIds]
(
#xyzType NVARCHAR(128),
#CustomerId INT,
#xxxId INT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS(SELECT * FROM Common.[CustomerxxxIds] WHERE xxxType = #xxxType AND CustomerId = #CustomerId)
BEGIN
INSERT INTO Common.[CustomerxxxIds]
([xxxId]
,[CustomerId]
,[xxxType])
VALUES
(0
,#CustomerId
,#xxxType)
END
UPDATE Common.[CustomerxxxIds]
SET [xxxId] = ([xxxId]) + 1
WHERE [xxxType] = #xxxType
AND CustomerId = #CustomerId
SELECT #xxxId = xxxId
FROM Common.[CustomerxxxIds]
WHERE [xxxType] = #xxxType
AND CustomerId = #CustomerId
END
You can do things to avoid "re-read" of the table to get the output value.
After the INSERT
( INSERT INTO Common.[CustomerxxxIds])
Use SCOPE_IDxxx() to get the newly created surrogate key.
The above will only work for IDxxx columns. From your question, you may not actually have an IDxxx column.
See
https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-idxxx-transact-sql?view=sql-server-2017
.........
with the UPDATE and/or INSERT, you could use OUTPUT functionality to get the value.
https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql?view=sql-server-2017
This AVOIDS the last select statement (the "re-read" as I am calling it) to get the desired output value.
Obviously completely removing a SELECT statement will improve performance.
..
Below is a simple but complete Northwind database example of using OUTPUT for INSERT and UPDATE
SELECT 'Before' as Looksie, [ShipperID]
,[CompanyName]
,[Phone]
FROM [Northwind].[dbo].[Shippers]
--
DECLARE #MyInsertAuditTable table( AuditShipperID INT,
AuditCompanyName nvarchar(40),
AuditPhone nvarchar(24));
INSERT [Northwind].[dbo].[Shippers] (CompanyName , Phone )
OUTPUT INSERTED.ShipperID, INSERTED.CompanyName, INSERTED.Phone
INTO #MyInsertAuditTable (AuditShipperID, AuditCompanyName , AuditPhone )
SELECT TOP 1
--(SELECT MAX(ShipperID) + 1 from dbo.Shippers )
'Shipper' + LEFT(CONVERT(VARCHAR(38), NEWID()), 12)
, '(555) 555-5555'
FROM sys.objects
--Display the result set of the table variable.
SELECT AuditShipperID, AuditCompanyName, AuditPhone FROM #MyInsertAuditTable;
DECLARE #MyUpdateAuditTable table( AuditShipperID INT,
AuditCompanyName nvarchar(40),
AuditOldPhone nvarchar(24),
AuditNewPhone nvarchar(24));
UPDATE [Northwind].[dbo].[Shippers]
SET Phone = '(777) 555-7777'
OUTPUT inserted.ShipperID, inserted.CompanyName ,
deleted.Phone,
inserted.Phone
INTO #MyUpdateAuditTable ( AuditShipperID, AuditCompanyName, AuditOldPhone , AuditNewPhone)
FROM [Northwind].[dbo].[Shippers] shippers
JOIN #MyInsertAuditTable insAudit on shippers.ShipperID = insAudit.AuditShipperID
SELECT * from #MyUpdateAuditTable
SELECT 'After' as Looksie, [ShipperID]
,[CompanyName]
,[Phone]
FROM [Northwind].[dbo].[Shippers]
--
Results
Looksie ShipperID CompanyName Phone
Before 1 Speedy Express (503) 555-9831
Before 2 United Package (503) 555-3199
Before 3 Federal Shipping (503) 555-9931
..
AuditShipperID AuditCompanyName AuditPhone
9 Shipper3C062D46-EEA (555) 555-5555
...
AuditShipperID AuditCompanyName AuditOldPhone AuditNewPhone
9 Shipper3C062D46-EEA (555) 555-5555 (777) 555-7777
..
Looksie ShipperID CompanyName Phone
After 1 Speedy Express (503) 555-9831
After 2 United Package (503) 555-3199
After 3 Federal Shipping (503) 555-9931
After 9 Shipper3C062D46-EEA (777) 555-7777
You can achieve this by changing SELECT * to SELECT 1. it might help
IF NOT EXISTS (SELECT 1 FROM Common.[CustomerxxxIds]
WHERE xyzType = #xyzType AND CustomerId = #CustomerId)
Try this
ALTER PROCEDURE [Common].[SaveCustomerxxxIds]
(
#xyz NVARCHAR(128),
#CustomerId INT,
#xxxId INT OUTPUT
)
AS
BEGIN
set #xxxId=null
--Get xxxId
SELECT #xxxId=[xxxId] FROM Common.[CustomerxxxIds] WHERE xyz = #xyz AND CustomerId = #CustomerId
--If #xxxId means no record we should insert
if (#xxxId is null)
begin
--When insert we always insert xxxId as 0 then update to one then we collect the value (one) from db and return it.
--Better set value directly as one insert it to DB and return it as one. Instead of insert, update, select
--just insert
set #xxxId = 1
INSERT INTO Common.[CustomerxxxIds]
([xxxId]
,[CustomerId]
,[xyz])
VALUES
(#xxxId
,#CustomerId
,#xyz)
end
else
begin
--If we have the value we add one to it update the record and return it.
--better than update table then select.
--We already have the value we add one to it then update table and return the value we have
set #xxxId+=1
UPDATE Common.[CustomerxxxIds] SET [xxxId] = #xxxId
WHERE [xyz] = #xyz AND CustomerId = #CustomerId
END
end

Insert Query to insert multiple rows in a table via select and output clause. SQL Server 2008

I have a created a stored procedure (please ignore syntax errors)
alter proc usp_newServerDetails
(#appid int, #envid int, #serType varchar(20), #servName varchar(20))
as
declare #oTbl_sd table (ID int)
declare #outID1
declare #oTbl_cd table (ID int)
declare #outID2
begin Transaction
insert into server_details(envid, servertype, servername)
output inserted.serverid into #oTbl_sd(ID)
values(#envid, #serType, #servName)
select #outID1 = ID from #oTbl_sd
insert into configdetails(serverid, servertype, configpath, configtype)
output inserted.configid into #oTbl_cd(ID)
(select #outID1, cm.servertype, cm.configpath, cm.configtype
from configpthmaster cm
where cm.appid = #appid )
select #outID2 = ID from #oTbl_cd
insert into configkeydetails(confiid, keyname)
output inserted.Keyid into #oTbl_ckd(ID)
(select #outID2, cm.key
from configpthmaster cm
where cm.appid = #appid)
begin
commit
end
server_details table has an identity column ID with is auto-generated ie. #outID1 and first insert query inserts only 1 row.
configpthmaster table is not related to any other table directly and has 2 unique data rows, which I want to fetch to insert data into other tables, one by one during insertion.
The second insert query fetch data from configpthmaster table
and insert 2 rows in configdetails while generating (auto-generated) ID ie. #outID2.
It also has a FK mapped to server_details.
The problem is "#outID2" giving last inserted ID only (ie. if two id generated 100,101 i am getting 101) which eventually on 3rd insertion, inserting 2 rows with same id 101 only but i want the insertion should be linear. i.e one for 100 and other for 101.
If zero rows affected while insertion how to rollback the transaction?
How can I achieve these requirements? Please help.
Change your procedure like below,and try again.
ALTER PROCEDURE usp_newServerDetails(#appid int, #envid int,#serType varchar(20),#servName varchar(20))
AS
BEGIN
BEGIN TRY
DECLARE #Output TABLE (ID int,TableName VARCHAR(50),cmKey VARCHAR(50)) --table variable for keeping Inserted ID's
BEGIN TRAN
IF EXISTS ( SELECT 1 FROM configpthmaster cm WHERE cm.appid = #appid )
AND ( SELECT 1 FROM configkeydetails ck WHERE ck.appid = #appid ) --add a conditon to satisfy the valid insertions
BEGIN
INSERT INTO server_detials(envid,servertype,servername)
OUTPUT inserted.serverid,'server_detials',NULL INTO #Output(ID,TableName,cmKey )
VALUES(#envid ,#serType ,#servName)
INSERT INTO configdetails(serverid,servertype,configpath,configtype)
OUTPUT inserted.configid,'configdetails',cm.Key INTO #Output(ID,TableName,cmKey )
SELECT t.ID,cm.servertype,cm.configpath,cm.configtype
FROM configpthmaster cm
CROSS APPLY (SELECT ID FROM #Output WHERE TableName='server_detials')t
WHERE cm.appid = #appid
INSERT INTO configkeydetails(configId,keyname)
SELECT ID,cmKey FROM #Output
WHERE TableName='configdetails'
END
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
END
Could you try this solution?
alter proc usp_newServerDetails(#appid int, #envid int,#serType varchar(20),#servName varchar(20))
as
declare #oTbl_sd table (ID int)
declare #outID1
declare #oTbl_cd table (ID int)
declare #outID2
begin Transaction
insert into server_detials(envid,servertype,servername)
output inserted.serverid into #oTbl_sd(ID)
values(#envid ,#serType ,#servName)
select #outID1 = ID from #oTbl_sd
insert into configdetails(serverid,servertype,configpath,configtype)
output inserted.configid into #oTbl_cd(ID)
(select #outID1 ,cm.servertype,cm.configpath,cm.configtype from configpthmaster cm where cm.appid = #appid )
select #outID2 = ID from #oTbl_cd
insert into configkeydetails(confiid,keyname)
output inserted.Keyid into #oTbl_ckd(ID)
(select isnull(replace(stuff((SELECT inserted.configid FOR xml path('')), 1, 1, ''), '&', '&'), '') ,cm.key, from configpthmaster cm where cm.appid = #appid )
begin
commit
end
I just added STUFF in your code.
The STUFF function inserts a string into another string.
Do take note that using STUFF drastically slows the processing time of the code.
for more information about STUFF

Function return table variable

I'm trying to create a function that return a table variable.So firstly i get data from Table1 and put it in another table variable. Here i want check if this variable isempty the function return the parameter result else return the result of the table variable
The function script is bellow :
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
DECLARE #CodeM nvarchar(400)
DECLARE #imax INT SET #imax = ##ROWCOUNT
DECLARE #i INT SET #i = 1
DECLARE #SelectDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
IF(EXISTS (SELECT 1 FROM #SelectDAT))
BEGIN
WHILE (#i <= #imax)
BEGIN
SELECT #CodeM = Code FROM #SelectDAT WHERE RowID = #i
INSERT INTO #VirtualDAT(Code) VALUES (#CodeM)
SET #i = #i + 1
END
END
ELSE
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
RETURN
END
So this script works without put it inside function.
And i test this function like this :SELECT * FROM dbo.CodeFunc( 'toto',Default ) the result is :
IF(EXISTS (SELECT 1 FROM #SelectDAT)) no record returned
esle the result is ok
As VR46 says. The ##ROWCOUNT will be set to 0 because there is no query before it. Any code executing in a function happens as a seperate set of queries. It was probably returning a value outside the function because you had previously used the query window for another unrelated query
You could re-factor this function quite dramatically. Look below, ##ROWCOUNT will work here as it is just after the insert query and will definitely have a value based on the insert.
I have not been able to test this, but I think something like this should do the same job.
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
insert into #VirtualDAT
Select Code from table1 where table1.id = 41
if ##ROWCOUNT = 0
begin
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
end
RETURN
END
Since you are assigning #imax with ##ROWCOUNT right after declaration of variable will be initialized with zero.
From MSDN ##ROWCOUNT
Returns the number of rows affected by the last statement.
If am not wrong you need to assign value to #imax after the insert into..select query.
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
SET #imax= ##ROWCOUNT
You can do the same in SET BASED APPROACH without using while loop.
CREATE FUNCTION Codefunc (#Code NVARCHAR(4),
#Table NVARCHAR(40) = '')
returns #VirtualDAT TABLE (
rowid INT IDENTITY ( 1, 1 ),
code NVARCHAR(400))
AS
BEGIN
IF EXISTS (SELECT code
FROM table1
WHERE table1.id = 41)
BEGIN
INSERT INTO #VirtualDAT
(code)
SELECT code
FROM table1
WHERE table1.id = 41
END
ELSE
INSERT INTO #VirtualDAT
(code)
VALUES (#Code)
RETURN
END

SQL Server : loop through every row, add incremented value to column

I have just added a new column, Person_Id_Helper to MyTable. It is supposed to contain 1,2,3 etc, in the order the table is now sorted.
This is what I want to do:
DECLARE #i INT = 1, #NumberOfRows INT = SELECT COUNT(*) FROM MyTable
WHILE(#i <= #NumberOfRows)
BEGIN
-- Person_Id_Helper = #i
-- #i = #i + 1
END
How do I write this?
I think, that it might be the wrong idea to persist the sort oder within a column. But - for sure! - it is the wrong idea to do this in a while loop.
Read about row-based and set-based approaches. SQL demands for set-based thinking...
Look at this as an example how to do this (just paste it into an empty query window and execute, adapt to your needs):
DECLARE #tbl TABLE(SortDate DATE, Inx INT);
INSERT INTO #tbl VALUES({d'2016-01-20'},0)
,({d'2016-01-19'},0)
,({d'2016-01-14'},0)
,({d'2016-01-16'},0);
WITH cte AS
(
SELECT Inx,ROW_NUMBER() OVER(ORDER BY SortDate) AS RN
FROM #tbl
)
UPDATE cte SET Inx=RN;
SELECT * FROM #tbl;
I think this is what you want to achieve -
DECLARE #i INT = 0
UPDATE MyTable
SET
#i = Person_Id_Helper = #i + 1
Now check your column value.
Well we can not use ORDER BY clause in update statement. But to use it here is the updated query.
UPDATE t
SET Person_Id_Helper = rn.RowNum
FROM MyTable t
INNER JOIN (SELECT
ID
,ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
FROM MyTable) rn
ON t.ID = rn.ID
#shungo: Thanks for point out.
Try like this,
DECLARE #tbl TABLE
(
datecolumn DATE,
Person_Id_Helper INT
);
INSERT INTO #tbl
VALUES ({d'2016-01-20'},
0),
({d'2016-01-19'},
0),
({d'2016-01-14'},
0),
({d'2016-01-16'},
0);
SELECT *
FROM #tbl;
UPDATE T
SET Person_Id_Helper = rn
FROM (SELECT Person_Id_Helper,
Row_number()
OVER(
ORDER BY datecolumn) AS rn
FROM #tbl) T
SELECT *
FROM #tbl;
Looping add increment the value using cursor in sqlserver
CREATE TABLE [dbo].[Table1](
[PID] [int] NULL,
[ProductDesc] [nvarchar](50) NULL,
[ProductCode] [nvarchar](10) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[Table1] ([PID], [ProductDesc], [ProductCode], [Person_Id_Helper]) VALUES (1, N'Packet-Eye', N'P001', NULL)
GO
INSERT [dbo].[Table1] ([PID], [ProductDesc], [ProductCode], [Person_Id_Helper]) VALUES (2, N'Wiggy', N'W099 ', NULL)
GO
INSERT [dbo].[Table1] ([PID], [ProductDesc], [ProductCode], [Person_Id_Helper]) VALUES (3, N'Wimax-Lite', N'W001', NULL)
GO
INSERT [dbo].[Table1] ([PID], [ProductDesc], [ProductCode], [Person_Id_Helper]) VALUES (4, N'Wimax-Home', N'e W002 ', NULL)
GO
Declare #count int
DECLARE #PID int
set #count=0
DECLARE c1 CURSOR READ_ONLY
FOR
SELECT [PID] FROM [Table1]
OPEN c1
FETCH NEXT FROM c1 INTO #PID
WHILE ##FETCH_STATUS = 0
BEGIN
set #count=#count+1
update Table1 set Person_Id_Helper=#count where [PID]=#PID
FETCH NEXT FROM c1 INTO #PID END
CLOSE c1
DEALLOCATE c1
select * from Table1

How to check if there are rows and execute an Insert

I got the following SQL Code, I need
Execute an Insert only if the code returns more than 0 rows.
Put messages on the screen for the person who executes the script, saying
No missing rows were detected or
3 missing rows were detected and added.
select * from DistributionKey_Section where SectionID
not in
(
select siteid from Site where SiteTypeCodeID IN(8)
)
and DistributionKeyID NOT IN
(
select DistributionKeyID from DistributionKey where UnitInclusive=1
)
DECLARE
#MissingRows int,
#InsertedRows int
SELECT *
FROM DistributionKey_Section
WHERE SectionID NOT IN ( select siteid from Site where SiteTypeCodeID IN(8) ) AND
DistributionKeyID NOT IN ( SELECT DistributionKeyID FROM DistributionKey WHERE
UnitInclusive=1 )
SET #MissingRows = ##ROWCOUNT
IF #MissingRows > 0
BEGIN
<Insert Statement/Logic>
SET #InsertedRows = ##ROWCOUNT
PRINT CAST(#InsertedRows as varchar(5)) + ' missing rows were detected and added'
IF #MissingRows <> #InsertedRows
BEGIN
RAISERROR('The number of rows inserted does not equal the number of rows missing', 16, 1)
END
END
ELSE
PRINT 'No Missing Rows Detected'