How can I make this pl/sql cursor more efficient? - sql

Hi guys I have a pl/sql cursor that takes too long to execute. I want to know how can I make the same process but with better performance and probably better code. I am new to PL/SQL.
Declare
Cursor Cursor1 is
select * from table1 where
field1 IS NULL
or
field2 IS NULL or field3 IS NULL or field4 is null or field5 IS NULL or field6 IS NULL;
Begin
For i in Cursor1 loop
if i.field1 IS NULL then
update table1 set field1=0 where recordId=i.recordId;
end if;
if i.field2 IS NULL then
update table1 set field2=0 where recordId=i.recordId;
end if;
if i.field3 IS NULL then
update table1 set field3=0 where recordId=i.recordId;
end if;
if i.field4 IS NULL then
update table1 set field4=0 where recordId=i.recordId;
end if;
if i.field5 IS NULL then
update table1 set field5=0 where recordId=i.recordId;
end if;
if i.field6 IS NULL then
update table1 set field6=0 where recordId=i.recordId;
end if;
End loop;
End;
The question basically is how can I update a field of one specific record, taking into account the conditions of the field. The thing is that the update can occur in the same record many times if the condition apply for many fields in the record.
Thanks...

It's possible to do the same with one UPDATE
UPDATE table1 SET
field1 = COALESCE(field1, 0)
, field2 = COALESCE(field2, 0)
, field3 = COALESCE(field3, 0)
, field4 = COALESCE(field4, 0)
, field5 = COALESCE(field5, 0)
, field6 = COALESCE(field6, 0)
WHERE field1 IS NULL OR field2 IS NULL OR field3 IS NULL
OR field4 IS NULL OR field5 IS NULL OR field6 IS NULL

Here's another take on this:
UPDATE TABLE1
SET FIELD1 = NVL(FIELD1, 0),
FIELD2 = NVL(FIELD2, 0),
FIELD3 = NVL(FIELD3, 0),
FIELD4 = NVL(FIELD4, 0),
FIELD5 = NVL(FIELD5, 0),
FIELD6 = NVL(FIELD6, 0);
Rationale: any query which performs this update is going to do a full table scan anyways because it's looking for NULLs, which won't be indexed in the usual case, and even if they ARE indexed there's a fair chance the optimizer will choose a full table scan anyways. Why waste time checking six different fields for NULLs?
Share and enjoy.

Try executing couple of updates like this, avoiding the usage of a cursor:
update table1 set field1=0 where field1 IS NULL;
update table1 set field2=0 where field2 IS NULL;
update table1 set field3=0 where field3 IS NULL;
update table1 set field4=0 where field4 IS NULL;
update table1 set field5=0 where field5 IS NULL;
update table1 set field6=0 where field6 IS NULL;
I don't think that there is a more efficient way to do this.

Related

Selecting a specific value within a case statement

I need help with selecting a specific value within a CASE Statement.
For an example
SELECT CASE WHEN Field1 >= (Field2 = 1) THEN Answer
WHEN Field1 < (Field2 = 1) THEN No Answer
ELSE Question END AS Field3
I want to be able to select a specific value in Field2 within the CASE Statement
could be you are looking for
SELECT CASE WHEN Field1 >= Field2 AND Field2 = 1 THEN Answer
WHEN Field1 < Field2 AND Field2 = 1 THEN No Answer
ELSE Question END AS Field3
or more simply
SELECT CASE WHEN Field1 >= 1 THEN Answer
WHEN Field1 < 1 THEN No Answer
ELSE Question END AS Field3
-- 1. You don't specify the fields types.
-- 2. You do a comparison of Field1 with a logical expression (Field2=1). i guess it fails.
-- 3. I will assume Answer, No answer and Question will be the desired string result, and have to be quoted.
-- 4. A Table name is missing. I will assume Field1 and Field2 are fields from MyTable
-- 5. I' am assuming Field1 and Field2 of the same type, and be varchar(10) for example.
DECLARE #Field2 varchar(10) = 1
SELECT
CASE WHEN Field1 >= Field2 THEN 'Answer'
WHEN Field1 < Field2 THEN 'No Answer'
ELSE 'Question'
END AS Field3
FROM MyTable
WHERE Field2 = #Field2

SQL Server : skip update if variable is NOT NULL

I have a problem with a SQL Server database. I want to update the values of an ID only if the value is null, if there is a value it should be skipped -> not updated.
I don't know how to realise this "if else" in SQL Server - could somebody give me a hint?
Thanks in advance.
This is my pseudo code:
UPDATE dbo.mytable
IF FIELD1 IS NOT NULL SKIP --Don't update
ELSE IF FIELD1 IS NULL
SET FIELD1 (SELECT DISTINCT FIELD1
FROM mytable
WHERE {Some ID} = '123'
AND FIELD1 IS NOT NULL)
WHERE {Some ID} = '123'
AND FIELD1 IS NULL
UPDATE t
SET t.FIELD1 = ( SELECT DISTINCT TOP 1 FIELD1
FROM mytable t2
WHERE SomeID = '123'
AND FIELD1 IS NOT NULL )
FROM dbo.mytable t
WHERE FIELD1 IS NULL
AND SomeId = '123'
I think you want something like this:
UPDATE dbo.mytable
SET FIELD1 = ?
WHERE FIELD1 IS NULL;
The ? is a placeholder for the value you want to set it to.
If you want to set the value to the "123" value:
UPDATE dbo.mytable
SET FIELD1 = (SELECT t2.FIELD1 FROM dbo.mytable t2 WHERE t2.ID = 123)
WHERE FIELD1 IS NULL;
You can simply write the below query
UPDATE dbo.mytable
SET ID = 123
WHERE ID IS NULL
Hope this will help you. It will only update the ID when the value in ID field is NULL.
UPDATE dbo.mytable
SET FIELD1 = (SELECT DISTINCT FIELD1
FROM mytable
WHERE {Some ID} = '123'
AND FIELD1 IS NOT NULL)
WHERE {Some ID} = '123'
AND FIELD1 IS NULL --here your IF condition will get filter [[IF FIELD1 IS NOT NULL SKIP --Don't update]]

SQL partial update of columns based on case statement?

Recently I have had to do a few variable updates to a table, and although I am aware of the MERGE statement (although need to catch up on all of that!), I also performed the following statement to optional update a table and wish to check if this is "a good idea" or has some hidden consequences that I not aware of.
So in my case, I pass a primary key to a table, however depending on if parameters passed are null or not, I update the column.. obviously if you had to ensure a forceful update (of a status etc.) then you would just update the column.. this is to save having multiple "IF / THEN" type structures..
create procedure sp_myprocedure
as
#id bigint,
#field1 int = null,
#field2 varchar(255) = null,
#field3 char(1) = null
begin
update my_table
set
field1 = case when #field1 is not null then #field1 else field1 end,
field2 = case when #field2 is not null then #field2 else field2 end,
field3 = case when #field3 is not null then #field3 else field3 end,
where
id = #id
end
Just after some thoughts of the above or is it best to pursue the MERGE statement for scenarios like the above?
Many thanks in advance,
This is fine although it can be written in a cleaner way.
update my_table
set
field1 = coalesce (#field1,field1)
,field2 = coalesce (#field2,field2)
,field3 = coalesce (#field3,field3)
where
id = #id and coalesce(#field1,#field2,#field3) is not null
You can also move the coalesce(#field1,#field2,#field3) is not null to a wrapper block
if coalesce(#field1,#field2,#field3) is not null
begin
update my_table
set
field1 = coalesce (#field1,field1)
,field2 = coalesce (#field2,field2)
,field3 = coalesce (#field3,field3)
where
id = #id
end
MERGE statement is not relevant here.
With MERGE the decision is if to INSERT, UPDATE or DELETE a record base on the non-existent/existent of a record with the same merge keys in the source/target table.
In your case it is always UPDATE.
create procedure sp_myprocedure
#id bigint,
#field1 int = null,
#field2 varchar(255) = null,
#field3 char(1) = null
as
begin
IF coalesce(#field1,#field2,#field3) is not null
update dbo.my_table
set
field1 = coalesce (#field1,field1),
field2 = coalesce (#field2,field2),
field3 = coalesce (#field3,field3)
where id = #id
END
Responding to the answer by #Dudu Markovitz:
MERGE statement is not relevant here.
I disagree , I think MERGE is entirely relevant here.
The idea is to create a source table expression using the parameter values with which to update the target table:
MERGE my_table T
USING ( VALUES ( #id, #field1, #field2, #field3 ) )
AS S ( id, field1, field2, field3 )
ON T.id = S.id
WHEN MATCHED THEN
UPDATE
SET field1 = COALESCE( S.field1, T.field1 ),
field2 = COALESCE( S.field2, T.field2 ),
field3 = COALESCE( S.field3, T.field3 );
Of course, if there was a single table-valued parameter (as arguable there should be) then the relevance of MERGE is even more obvious.

sql atomic exists/insert - multiple unique fields

Ok, so I've learned from other posts/sites, that the following style of query will execute atomically:
INSERT INTO Foo(field1, field2)
SELECT #field1, #field2
WHERE NOT EXISTS (SELECT * FROM Foo
WHERE Field1 = #field1
OR Field2 = #field2)
This would be used to satisfy a design requirement that both field1 and field2 be unique. So far, all is fine and dandy.
BUT, in the case of such a record already existing (where the query inserts 0 rows) what if I want to know WHICH field already had a record with the same value. (i.e., which of the following statements are true?
there was already an existing record where Field1 = #field1
there was already an existing record where Field2 = #field2
both of the above
You would need to know which field is at fault in order to show an error message that tells the user which field they need to change to a unique value. I could always check with a second query, but that would not be atomic. The data may have changed between the attempted insert and the 2nd query to determine which field was at fault, and the error message may not reflect the actual state of the DB.
Any ideas on how to handle this case?
Assumptions:
You expect most INSERTs to succeed.
You have a unique contraint such that a bad INSERT will fail.
You aren't concerned about matching NULLs.
set transaction isolation level serializable;
begin transaction;
begin try
insert into Foo ( Field1, Field2 ) values ( #Field1, #Field2 );
end try
begin catch
-- Really ought to confirm that the error was the expected one here.
select
#Field1Match = case when Field1 = #Field1 then Cast( 1 as Bit ) else Cast( 0 as Bit ) end,
#Field2Match = case when Field2 = #Field2 then Cast( 1 as Bit ) else Cast( 0 as Bit ) end
from Foo
where Field1 = #Field1 or Field2 = #Field2;
end catch;
commit transaction;
Based on my research (here & here), unless you have a very high confidence that over 95% of the inserts will succeed, I would use the opposite approach as HABO. Borrowing from the same assumption that you aren't concerned about matching NULLs:
DECLARE #Col1Match VARCHAR(20), #Col2Match VARCHAR(20), #msg NVARCHAR(4000);
SET ANSI_WARNINGS OFF;
BEGIN TRANSACTION;
SELECT #Col1Match = MAX(CASE Field1 WHEN #Field1 THEN 'Field1 exists:' END),
#Col2Match = MAX(CASE Field2 WHEN #Field2 THEN 'Field2 exists:' END)
FROM dbo.Foo WITH (HOLDLOCK)
WHERE Field1 = #Field1 OR Field2 = #Field2;
IF #Col1Match IS NULL AND #Col2Match IS NULL
BEGIN
INSERT dbo.Foo(Field1, Field2) SELECT #Field1, #Field2;
END
ELSE
BEGIN
SET #msg = LTRIM(COALESCE(' ' + #Col1Match + ' (' + #Field1 + ')', '')
+ COALESCE(' ' + #Col2Match + ' (' + #Field2 + ')', ''));
END
COMMIT TRANSACTION;
IF #msg IS NOT NULL
BEGIN
RAISERROR(#msg, 11, 1);
END

Sql If statement based on COUNT return

Basically, I am looking to modify a stored procedure that I have in my database already that updates a table's records (table x) with data coming from a web application. After updating table x, I want to develop that stored procedure to check table x to see if there exist anymore records for that instance of the primary key where column_z is null. Basically, something like
ALTER PROCEDURE [cred].[UpdateTablex]
(#field0 int,
#field1 int,
#field2 int,
#field3, int,
#field4 VARCHAR(100),
#field5 datetime,
#field6 datetime)
AS
UPDATE tablex
SET field4 = #field4,
field6 = #field6
WHERE field1 = #field1 AND
field2 = #field2 AND
field3 = #field3 AND
field0 = #field0 AND
field5 = #field5;
The rest of this will be psuedocode for my idea and the way I thought it might be developed
IF ((SELECT COUNT(field0) FROM tablex WHERE field6 is null AND field2 = #field2
AND field3 = #field3 AND field5 = #field5) equals 0)
exec cred.Demobilize(#field0, #field1);
Or simply, if that Select statement returns any results indicating that field6 is null anywhere then we do nothing. Obviously that would mean then that if the Select statement returns nothing, then I want to execute another stored procedure.
This is probably something simple so please forgive me as I'm kinda new to certain types of SQL syntax and usage, this being one of them.
Also, could anybody point me in a proper direction to further educate myself on topics like this?
Thank you.
You can do the if without using a variable:
IF (0 = (SELECT COUNT(field0)
FROM tablex
WHERE field6 is null AND field2 = #field2 AND field3 = #field3 AND field5 = #field5
)
)
begin
exec cred.Demobilize(#field0, #field1);
end;
In practice, I think this would more commonly be written using not exists:
IF (not exists (SELECT 1
FROM tablex
WHERE field6 is null AND field2 = #field2 AND field3 = #field3 AND field5 = #field5
)
)
begin
exec cred.Demobilize(#field0, #field1);
end;
The not exists version can be faster, because the first row encountered will satisfy the condition clause. The count(*) version has to find all matching rows and count them.