SQL Server - Stored Procedure Return in Case Result Expression - sql

I'd like to use a return from a procedure in a CASE statement. It can't be a function because that procedure returns an inserted key.
UPDATE TIM
SET CD_LINHA_EVENTO =
CASE WHEN
TIM.CD_SUBESTRUTURA_PARAMETRO = (SELECT TOP 1 SPZ.CD_SUBESTRUTURA_PARAMETRO FROM SUBESTRUTURA_PARAMETRO SPZ WITH (NOLOCK) WHERE SPZ.CD_SUBESTRUTURA = TIM.CD_SUBESTRUTURA
AND SPZ.FL_SELECAO = 1 ORDER BY SPZ.NR_ORDEM)
THEN
**EXEC [dbo].[SPRTO_NumeracaoEventos]**
ELSE
(SELECT MAX(TIM2.CD_LINHA_EVENTO) FROM #TB_INSERTED_MODIFIED TIM2 WITH(NOLOCK))
END
FROM #TB_INSERTED_MODIFIED TIM WITH (NOLOCK)
stored procedure [SPRTO_NumeracaoEventos]:
INSERT INTO TB_NUMERACAO_EVENTOS (VALOR) VALUES ('')
RETURN SCOPE_IDENTITY()
Thank you!

I wasn't able to figure out your table schema well enough to provide a direct answer, but I put together a way to use inserted identity values within an update trigger. Hopefully you can adapt this to fit your specific use case.
Have a look at this SQL Fiddle
As noted in the comments in the fiddle, this strategy is based on using the OUTPUT clause to capture identity values into a table variable. See http://msdn.microsoft.com/en-us/library/ms177564.aspx for more on the OUTPUT clause.
For rows where the case is true, we insert one row per true case into the secondary table and capture the identities to a table variable, then we update the underlying table with a row number join between the inserted virtual table and the identity table variable. Then we run another update for the rows where the case is false. Here's the relevant trigger code from my fiddle example.
-- Create a table variable to hold identity ID values from OUTPUT clause
DECLARE #table_2_ids TABLE (id INT NOT NULL)
-- Insert to our secondary table where the condition is true and capture the identity values to table variable
INSERT table_2 (textval)
OUTPUT inserted.id INTO #table_2_ids
SELECT 'from trigger'
FROM inserted
WHERE flag = 1
-- Use row number to match up rows from inserted where the condition is true to the identity value table variable
-- Update matched identity id to underlying table
UPDATE t
SET table_2_id = r.id, textval = textval + ' and trigger inserted to table_2'
FROM table_1 t
JOIN (SELECT id, ROW_NUMBER() OVER (ORDER BY id) rn FROM inserted WHERE flag = 1) i ON i.id = t.id
JOIN (SELECT id, ROW_NUMBER() OVER (ORDER BY id) rn FROM #table_2_ids) r ON r.rn = i.rn
-- and now update where condition is false
UPDATE t
SET textval = textval + ' and trigger did not insert to table_2'
FROM table_1 t
WHERE flag = 0

Related

Automation Anywhere SQL results

I am trying to capture if my SQL Query have 0 rows or multiple rows. If it has 0 rows then I will insert, if 1 will perform an update, if > 1 will perform additional analysis.
Is there a way I can see if my query resulted in x results or no results in automation anywhere?
Any assistance will be appreciated.
You can make use of if exists and if not exists and check if rows exists or not, or even if there are multiple before doing the insert.
Here is a simple example using if not exists where if the row doesn't exist on dbo.Table it will insert a row. If it already exists then the ID will be logged to an Error table.
declare #InsertID int = 5, #Name nvarchar(max) = 'some name'
if ((select count(1) from dbo.Table where ID = #InsertID) > 1) -- detect error; more than one record for an id
begin
insert into dbo.Error (ErrorID, ErrorDate)
select #InsertID, getdate()
end
else if not exists (select 1 from dbo.Table where ID = #InsertID) -- no record exists for ID, insert it
begin
insert into dbo.Table (ID, Name)
select #InsertID, #Name
else if exists (select 1 from dbo.Table where ID = #InsertID) -- update the single record
begin
update dbo.Table set Name = #Name where ID = #InsertID
end
A2019 returns the results of a SQL Query as a table...
You could have an if statement right after your query which checks to see if the row count of the returned table is > 0 then take action accordingly.

I need to optimize my first T-SQL update trigger

How do I rewrite this update trigger without using a lot of variables?
I wrote my first SQL Server trigger and it works fine, but I think, that there must be an easier solution.
If minimum one of 5 columns is changed I write two new rows in another table.
row 1 = old Fahrer (=Driver) and old dispodate and update-time
row 2 = new Fahrer and new dispodate and updatedatetime
My solution is just a copy of the foxpro-trigger, but there must be a easier solutions in T-SQL to check whether one colum is changed.
ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[tbldisposaetze]
AFTER UPDATE
AS
SET NOCOUNT ON;
/*SET XACT_ABORT ON
SET ARITHABORT ON
*/
DECLARE #oldfahrer varchar(10)
DECLARE #oldbus varchar(10)
DECLARE #olddispodat date
DECLARE #oldvzeit decimal(4,0)
DECLARE #oldbzeit decimal(4,0)
DECLARE #oldbeschreibk varchar(255)
DECLARE #newfahrer varchar(10)
DECLARE #newbus varchar(10)
DECLARE #newdispodat date
DECLARE #newvzeit decimal(4,0)
DECLARE #newbzeit decimal(4,0)
DECLARE #newbeschreibk varchar(255)
SELECT #oldfahrer = fahrer,#oldbeschreibk=beschreibk,#oldbus=bus,#oldbzeit=bzeit,#olddispodat=dispodat,#oldvzeit=vzeit
FROM DELETED D
SELECT #newfahrer = fahrer,#newbeschreibk=beschreibk,#newbus=bus,#newbzeit=bzeit,#newdispodat=dispodat,#newvzeit=vzeit
FROM inserted I
if #oldbeschreibk <> #newbeschreibk or #oldbus <> #newbus or #oldbzeit <> #newbzeit or #oldfahrer <> #newfahrer or #oldvzeit <> #newvzeit
begin
IF (SELECT COUNT(*) FROM tbldispofahrer where fahrer=#oldfahrer and dispodat=#olddispodat) > 0
update tbldispofahrer set laenderung = GETDATE() where fahrer=#oldfahrer and dispodat=#olddispodat
else
INSERT into tbldispofahrer (fahrer,dispodat,laenderung) VALUES (#oldfahrer,#olddispodat,getdate())
IF (SELECT COUNT(*) FROM tbldispofahrer where fahrer=#newfahrer and dispodat=#newdispodat) > 0
update tbldispofahrer set laenderung = GETDATE() where fahrer=#newfahrer and dispodat=#newdispodat
else
INSERT into tbldispofahrer (fahrer,dispodat,laenderung) VALUES (#newfahrer,#newdispodat,getdate())
end
I'll assume you have SQL Server 2008 or greater. You can do this all in one statement without any variables.
Instead of doing all the work to first get the variables and see if they don't match, you can easily do that in as part of where clause. As folks have said in the comments, you can have multiple rows as part of inserted and deleted. In order to make sure you're working with the same updated row, you need to match by the primary key.
In order to insert or update the row, I'm using a MERGE statement. The source of the merge is a union with the where clause above, the top table in the union has the older fahrer, and the bottom has the new farher. Just like your inner IFs, existing rows are matched on farher and dispodat, and inserted or updated appropriately.
One thing I noticed, is that in your example newfahrer and oldfahrer could be exactly the same, so that only one insert or update should occur (i.e. if only bzeit was different). The union should prevent duplicate data from trying to get inserted. I do believe merge will error if there was.
MERGE tbldispofahrer AS tgt
USING (
SELECT d.farher, d.dispodat, GETDATE() [laenderung]
INNER JOIN inserted i ON i.PrimaryKey = d.PrimaryKey
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreik ... )
UNION
SELECT i.farher, i.dispodat, GETDATE() [laenderung]
INNER JOIN inserted i ON i.PrimaryKey = d.PrimaryKey
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreik ... )
) AS src (farher, dispodat, laenderung)
ON tgt.farher = src.farher AND tgt.dispodat = src.dispodat
WHEN MATCHED THEN UPDATE SET
laenderung = GETDATE()
WHEN NOT MATCHED THEN
INSERT (fahrer,dispodat,laenderung)
VALUES (src.fahrer, src.dispodat, src.laenderung)
There were a few little syntax errors in the answer from Daniel.
The following code is running fine:
MERGE tbldispofahrer AS tgt
USING (
SELECT d.fahrer, d.dispodat, GETDATE() [laenderung] from deleted d
INNER JOIN inserted i ON i.satznr = d.satznr
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreibk or i.bus <> d.bus or i.bzeit <> d.bzeit or i.vzeit <> d.vzeit)
UNION
SELECT i.fahrer, i.dispodat, GETDATE() [laenderung] from inserted i
INNER JOIN deleted d ON i.satznr = d.satznr
AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreibk or i.bus <> d.bus or i.bzeit <> d.bzeit or i.vzeit <> d.vzeit)
) AS src (fahrer, dispodat, laenderung)
ON tgt.fahrer = src.fahrer AND tgt.dispodat = src.dispodat
WHEN MATCHED THEN UPDATE SET
laenderung = GETDATE()
WHEN NOT MATCHED THEN
INSERT (fahrer,dispodat,laenderung)
VALUES (src.fahrer, src.dispodat, src.laenderung);

Update or insert data depending on whether row exists

I have a collection of rows that I get from a web service. Some of these rows are to be inserted, some are updates to existing rows. There is no way of telling unless I do a query for the ID in the table. If I find it, then update. If I don't, then insert.
Select #ID from tbl1 where ID = #ID
IF ##ROWCOUNT = 0
BEGIN
Insert into tbl1
values(1, 'AAAA', 'BBBB', 'CCCC', 'DDD')
END
ELSE
BEGIN
UPDATE tbl1
SET
A = #AAA,
B = #BBB,
C = #CCC,
D = #DDD
WHERE ID = #ID
END
I am trying to figure out the most effient way to update/insert these rows into the table without passing them into a stored procedure one at a time.
UPDATE 1
I should have mentioned I am using SQL Server 2005. Also if I have 300 records I don't want to make 300 stored procedure calls.
the most efficient way will be first try to update the table if it returns 0 row updated then only do insertion. for ex.
UPDATE tbl1
SET
A = #AAA,
B = #BBB,
C = #CCC,
D = #DDD
WHERE ID = #ID
IF ##ROWCOUNT = 0
BEGIN
Insert into tbl1
values(1, 'AAAA', 'BBBB', 'CCCC', 'DDD')
END
ELSE
BEGIN
END
Instead of paying for a seek first and then updating using another seek, just go ahead and try to update. If the update doesn't find any rows, you've still only paid for one seek, and didn't have to raise an exception, but you know that you can insert.
UPDATE dbo.tbl1 SET
A = #AAA,
B = #BBB,
C = #CCC,
D = #DDD
WHERE ID = #ID;
IF ##ROWCOUNT = 0
BEGIN
INSERT dbo.tbl1(ID,A,B,C,D)
VALUES(#ID,#AAA,#BBB,#CCC,#DDD);
END
You can also look at MERGE but I shy away from this because (a) the syntax is daunting and (b) there have been many bugs and several of them are still unresolved.
And of course instead of doing this one #ID at a time, you should use a table-valued parameter.
CREATE TYPE dbo.tbl1_type AS TABLE
(
ID INT UNIQUE,
A <datatype>,
B <datatype>,
C <datatype>,
D <datatype>
);
Now your stored procedure can look like this:
CREATE PROCEDURE dbo.tbl1_Update
#List AS dbo.tbl1_type READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE t
SET A = i.A, B = i.B, C = i.C, D = i.D
FROM dbo.tbl1 AS t
INNER JOIN #List AS i
ON t.ID = i.ID;
INSERT dbo.tbl1
SELECT ID, A, B, C, D
FROM #List AS i
WHERE NOT EXISTS
(
SELECT 1
FROM dbo.tbl1 WHERE ID = i.ID
);
END
GO
Now you can just pass your DataTable or other collection from C# directly into the procedure as a single parameter.
From the collection of rows you get from the server find out which ones are already there:
select #id from tbl1 where id in (....)
Then you have a list of ids that are in the table and one that there are not in the table.
You will have then 2 batch operations: one for update, the other for insert.
what i understand is this :
at the front end u issue a single sql statement
ArrayofIDsforInsert = select ID from tbl1 where ID not in ( array of ids at the front end)
ArrayofIDsforUpdate = (IntialArrayofids at frontend) - (ArrayofIdsforInsert)
one insert into table and one update table...
now call the insert into table with ArrayofIds for insert
call the update table with ArrayofIds for update..

What's the best way to lock a record while it is being updated?

If I need to SELECT a value from a table column (happens to be the primary key column) based on a relatively complex WHERE clause in the stored procedure, and I then want to update that record without any other concurrent stored procedures SELECTing the same record, is it as simple as just using a transaction? Or do I also need to up the isolation to Repeatable Read?
It looks like this:
Alter Procedure Blah
As
Declare #targetval int
update table1 set field9 = 1, #targetval = field1 where field1 = (
SELECT TOP 1 field1
FROM table1 t
WHERE
(t.field2 = 'this') AND (t.field3 = 'that') AND (t.field4 = 'yep') AND (t.field9 <> 1))
return
I then get my targetval in my program so that I can do work on it, and meanwhile I don't have to worry about other worker threads grabbing the same targetval.
I'm talking SQL 2000, SQL 2005, and SQL 2008 here.
Adding ROWLOCK,UPDLOCK to the sub query should do it.
ALTER PROCEDURE Blah
AS
DECLARE #targetval INT
UPDATE table1
SET field9 = 1,
#targetval = field1
WHERE field1 = (SELECT TOP 1 field1
FROM table1 t WITH (rowlock, updlock)
WHERE ( t.field2 = 'this' )
AND ( t.field3 = 'that' )
AND ( t.field4 = 'yep' )
AND ( t.field9 <> 1 ))
RETURN
Updated
The currently accepted answer to this question does not use updlock. I'm not at all convinced that this will work. As far as I can see from testing in this type of query with a sub query SQL Server will only take S locks for the sub query. Sometimes however the sub query will get optimised out so this approach might appear to work as in Query 2.
Test Script - Setup
CREATE TABLE test_table
(
id int identity(1,1) primary key,
col char(40)
)
INSERT INTO test_table
SELECT NEWID() FROM sys.objects
Query 1
update test_table
set col=NEWID()
where id=(SELECT top (1) id from test_table )
Query 2
update test_table
set col=NEWID()
where id=(SELECT max(id) from test_table)

Update and Insert Stored Procedure

I want to create a stored procedure that performs insert or update operation on a column if
that column does not contains a value that already exists in database it should allow insert when COUNT(field) = 0 or update when COUNT(field)=0 or 1 And I should know that either of these operation is performed or not.
Please solve my problem using COUNT not Exists because that won't work for UPDATE.
I am working in ASP.net - I have two columns of a table that are needed to be kept unique without using the unique constraint. So I want a procedure like this:
create proc usp_checkall #field1 varchar(20),
#field2 varchar(20),
#ID int,
#count int output
Now your query on updating/inserting #field1 & #field2 on basis of #id
If you happen to have SQL Server 2008, you could also try:
MERGE dbo.SomeTable AS target
USING (SELECT #ID, #Field_1, #Field_2) AS source (ID, Field_1, Field_2)
ON (target.ID = source.ID)
WHEN MATCHED THEN
UPDATE SET Field_1 = source.Field_1, Field_2 = source.Field_2
WHEN NOT MATCHED THEN
INSERT (ID, Field_1, Field_2)
VALUES (source.ID, source.Field_1, source.Field_2)
Use:
INSERT INTO your_table
(column)
VALUES
([ your_value ])
WHERE NOT EXISTS (SELECT NULL
FROM your_table
WHERE t.column = [ your_value ])
That will work on SQL Server, MySQL, Oracle, Postgres. All that's needed is to use the db appropriate variable reference. IE: For MySQL & SQL Server:
INSERT INTO your_table
(column)
VALUES
( #your_value )
WHERE NOT EXISTS (SELECT NULL
FROM your_table
WHERE t.column = #your_value)
To see if anything was inserted, get the value based on ##ROWCOUNT if using SQL Server. Use SQL%ROWCOUNT if you are using Oracle.
if Exists select * from Yourtable WHere Your Criteria
begin
update ...
end
else
begin
insert ...
end
This kind of approach will do the trick. #AlreadyExisted could be an OUTPUT parameter on the sproc for your calling code to check once it's returned.
DECLARE #AlreadyExisted BIT
SET #AlreadyExisted = 0
IF EXISTS(SELECT * FROM YourTable WHERE YourField = #FieldValue)
BEGIN
-- Record already exists
SET #AlreadyExisted = 1
UPDATE YourTable
SET....
WHERE YourField = #FieldValue
END
ELSE
BEGIN
-- Record does not already exist
INSERT YourTable (YourField,....) VALUES (#FieldValue,.....)
END