TSql (sql server 2005) query running slow - performance

The following query is running slow. for one value in memberid, there are multiple entries in memberid0, memberid1........ memberid9. Therefore each statement is effecting multiple row updates. Ofcourse the table size is in MBs
Declare #memberName nvarchar(250)
Declare #memberID bigint
Declare #dimId int
Declare #levelId int
Declare #newName nvarchar(250)
Declare #updateSQL1 nvarchar(500)
Declare #updateSQL2 nvarchar(500)
Declare #cursorStmt nvarchar(300)
Declare #custCounter bigint
Declare #prodCounter bigint
Declare #regCounter bigint
Declare #memberCounter int
SET #custCounter = 1
SET #prodCounter = 1
SET #regCounter = 1
SET #memberCounter = 0
BEGIN TRANSACTION
While #memberCounter < 3
Begin
Set #cursorStmt = 'Declare memberCursor CURSOR
FOR Select name, memberid, dimensionId, levelNumber from member' + CAST(#memberCounter as NVARCHAR(1)) + ' where memberID <> 0 order by memberid'
print #cursorStmt
exec sp_executesql #cursorStmt
OPEN memberCursor
FETCH NEXT FROM memberCursor INTO #memberName, #memberId, #dimId, #levelId
WHILE ##FETCH_STATUS = 0
BEGIN
IF #dimId = 0
BEGIN
SET #newName = 'Customer_' + CAST(#custCounter AS NVARCHAR(10)) + '_LEVEL_' + CAST(#levelId AS NVARCHAR(10))
SET #custCounter = #custCounter + 1
END
else if #dimId = 1
BEGIN
SET #newName = 'Product_' + CAST(#prodCounter AS NVARCHAR(10)) + '_LEVEL_' + CAST(#levelId AS NVARCHAR(10))
SET #prodCounter = #prodCounter + 1
END
else if #dimId = 2
BEGIN
SET #newName = 'Region_' + CAST(#regCounter AS NVARCHAR(10)) + '_LEVEL_' + CAST(#levelId AS NVARCHAR(10))
SET #regCounter = #regCounter + 1
END
SET #updateSQL1 = 'Update Member' + CAST(#dimId AS NVARCHAR(5)) + ' set name = ''' + #newName + ''' where memberId = ' + CAST(#memberId AS NVARCHAR(10))
SET #updateSQL2 = 'Update Member' + CAST(#dimId AS NVARCHAR(5)) + ' set memberName' + CAST(#levelId-1 AS NVARCHAR(5)) + ' = ''' + #newName + ''' where memberId' + CAST(#levelId-1 AS NVARCHAR(5)) + ' = ' + CAST(#memberId AS NVARCHAR(10))
--print #updateSQL1
--print #updateSQL2
exec sp_executesql #updateSQL1
exec sp_executesql #updateSQL2
FETCH NEXT FROM memberCursor INTO #memberName, #memberId, #dimId, #levelId
END
CLOSE memberCursor
DEALLOCATE memberCursor
Set #memberCounter = #memberCounter + 1
END

I double marc_s says, but if you don't want to do that,
try creating a table which hold your Update statements and then execute them in a batch of minimum 10 updates per query.
Try to create two table to store two different kind of updates so in a batch, you can have only kind of update in minimum of 10 rows. I would like to update as many as I could in a single query.
Multiple updates in single SQL query is faster, see:
Why are batch inserts/updates faster? How do batch updates work?

Related

How to modify a value of a json array from a value but not from a key (Oracle, PL SQL)?

I want to change my value '4444' to '7727'
knowing that I only have the old value ('4444') and the new value ('7727') but not the key
["4444","5555"]
after modification
["7727","5555"]
You can use FOR ORDINALITY in a JSON_TABLE:
SELECT *
FROM table_name t
CROSS APPLY JSON_TABLE(
t.json,
'$[*]'
COLUMNS
idx FOR ORDINALITY,
value NUMBER PATH '$'
);
Which, for the sample data:
CREATE TABLE table_name (id NUMBER PRIMARY KEY, json JSON);
INSERT INTO table_name (id, json)
SELECT 1, '["4444","5555"]' FROM DUAL;
Outputs:
ID
JSON
IDX
VALUE
1
["4444","5555"]
1
4444
1
["4444","5555"]
2
5555
Note: if you want the index to start from 0 then subtract 1.
If you want to get the values in PL/SQL then you can use JSON_ARRAY_T:
DECLARE
ja JSON_ARRAY_T := JSON_ARRAY_T.PARSE('["4444","5555"]');
BEGIN
FOR i IN 0 .. ja.get_size - 1 LOOP
DBMS_OUTPUT.PUT_LINE( ja.get_number(i) || ' = ' || i );
END LOOP;
END;
/
Which outputs:
4444 = 0
5555 = 1
db<>fiddle here
Here is a function I made to modify a value of a json array from a value not from a key (old value = '4444', new value = '7727')
nmr_cmpt_json := JSON_ARRAY_T.parse(nmrs_cmpts_tab);
i := 0;
loop
exit when i> nmr_cmpt_json.get_Size ;
if nmr_cmpt_json.get_String(i) = '4444' then
nmr_cmpt_json.put(i, '7727', TRUE);
nmrs_cmpts_tab:= nmr_cmpt_json .to_string;
dbms_output.put_line( 'nmr_cmpt_json = ' || nmr_cmpt_json.stringify);
exit;
end if;
i := i +1;
end loop;

How do I use a variable within another variable in an ORACLE SQL Loop?

I am attempting to use a loop in my Oracle SQL code that will change the name of the schema slightly each time the loop is run. I have tried the SQL code below, creating a NUMBER variable that counts up from 1 to 3 and then concatenating it with the schema name, but I keep receiving a long error message. The schema name should be 'x_rm_mt1_d' the first loop, 'x_rm_mt2_d' the second loop, and 'x_rm_mt3_d' on the final loop. The select statement in the code below is just a small example of what I am trying to do, I didn't think it would be pertinent to include the entire query that I am looking to use the variable for.
DECLARE
l_counter NUMBER := 0;
BEGIN
LOOP
l_counter := l_counter + 1;
IF l_counter > 3 THEN
EXIT;
END IF;
DEFINE StrSchema = 'x_rm_mt' || l_counter || '_d'
WITH aed AS (
SELECT DISTINCT SSN , STATE FROM (
--Effective Date During Quarter
SELECT *
FROM
(SELECT * FROM ADDRESS_EFF_DATE
WHERE STATE = 'VT'
AND EFF_DATE >= '01-JAN-20'
AND EFF_DATE < '01-APR-20'
AND ADDRESS_KEY = '0'
)aed
WHERE aed.EFF_DATE = (
SELECT MAX(aed2.EFF_DATE)
FROM ADDRESS_EFF_DATE aed2
WHERE aed2.CONTROL_ID = aed.CONTROL_ID
AND aed2.SSN = aed.SSN)
--Effective Date Prior to Quarter
UNION
SELECT *
FROM
(SELECT *
FROM Strschema.ADDRESS_EFF_DATE
WHERE STATE = 'VT'
AND EFF_DATE < '01-JAN-20'
AND ADDRESS_KEY = '0') aed
WHERE aed.EFF_DATE = (
SELECT MAX(aed1.EFF_DATE)
FROM Strschema.ADDRESS_EFF_DATE aed1
WHERE aed.ssn=aed1.ssn
AND aed.EFF_DATE < '01-JAN-20'
AND aed1.EFF_DATE < '01-JAN-20')) )
,
--Select all records from Employee Eff Date Where Latest Hire Date is before 4/1/20
--AND Term Date is Null or Term Date is Greater than 1/1/20 eed AS (
SELECT *
FROM
(SELECT *
FROM Strschema.EMPLOYEE_EFF_DATE eed
WHERE eed.CONTROL_ID = 'SMLMKT'
AND eed.EFF_DATE = (
SELECT MAX(eed1.EFF_DATE)
FROM Strschema.EMPLOYEE_EFF_DATE eed1
WHERE eed.SSN = eed1.SSN
AND eed1.CONTROL_ID = 'SMLMKT')
) eed3
WHERE eed3.LATEST_HIRE_DATE <= '04-APR-20'
AND (eed3.LAST_TERM_DATE > '01-JAN-20'
OR eed3.LAST_TERM_DATE is NULL) ) ,
ebe AS (
SELECT *
FROM Strschema.emp_ben_elects ebe
WHERE ebe.control_id = 'SMLMKT'
AND ebe.BENEFIT_ID = 'MEDICAL'
AND ebe.life_event_date = (
SELECT MAX(x.life_event_date)
FROM Strschema.employee_life_events x
WHERE x.ssn = ebe.ssn
AND x.control_id = 'SMLMKT'
AND ebe.p_company_id_i = x.p_company_id_i
AND x.life_event_status = 'C')
AND ebe.le_seq_no = (
SELECT MAX(x.le_seq_no)
FROM Strschema.employee_life_events x
WHERE x.ssn = ebe.ssn
AND x.control_id = 'SMLMKT'
AND ebe.p_company_id_i = x.p_company_id_i
AND x.life_event_status = 'C'
AND ebe.life_event_date = x.life_event_date)
)
SELECT cmp.NAME as "Client Name" , cssn.REAL_SSN as "Employee SSN"
--, aed.SSN as "FAKE SSN REMOVE" , eed.LAST_NAME as "Last Name" , eed.FIRST_NAME as "First Name" , aed.STATE as "Resident State" , eed.LATEST_HIRE_DATE as "Hire Date" , pi.DESCR1 as "Plan Name" , ebe.OPTION_ID as "Tier" , ebe.BENEFIT_EFF_DATE as "Coverage Start Date" , eed.LAST_TERM_DATE as "Coverage End Date"
--, eed.LAST_TERM_DATE
FROM eed INNER JOIN aed ON eed.SSN = aed.SSN
LEFT JOIN ebe ON eed.SSN = ebe.SSN
JOIN Strschema.COMP_SSN cssn ON eed.SSN = cssn.SSN
LEFT JOIN Strschema.PLAN_INFO pi ON ebe.PLAN_ID = pi.PLAN_ID AND ebe.BENEFIT_ID = pi.BENEFIT_ID
JOIN Strschema.COMPANY cmp ON eed.CURRENT_CO = cmp.COMPANY_ID
END LOOP;
END;
DECLARE the variable and then set its value in the loop:
DECLARE
TYPE employee_array IS TABLE OF EMPLOYEE%ROWTYPE;
-- Assuming the tables all have the same type.
l_StrSchema VARCHAR2(30);
t_employees employee_array;
BEGIN
FOR l_counter IN 1 .. 3 LOOP
l_StrSchema := 'x_rm_mt' || l_counter || '_d';
DBMS_OUTPUT.PUT_LINE( 'Loop ' || l_counter );
EXECUTE IMMEDIATE 'SELECT * FROM ' || l_Strschema || '.EMPLOYEE'
|| ' WHERE ROWNUM < 1000'
BULK COLLECT INTO t_employees;
FOR i IN 1 .. t_employees.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( t_employees(i).name );
END LOOP;
END LOOP;
END;
/
You also need to use dynamic SQL if you are dynamically setting the table's schema and will need to collect the rows into a data structure as you are working in PL/SQL (and not in the SQL scope). You can also use a FOR loop.
db<>fiddle

How to replace cursor inside Stored Procedure for faster Execution in MS SQL?

I have written the below code for splitting string, but it takes a long time to execute. Please help me re-write my code to optimize the query. I have tried hard to find the result, but i didn't get how to apply the logic to replace the cursor.
declare #table as nvarchar(50),#column as nvarchar(max),#delimiter
as nvarchar(1),#tablekey as nvarchar(max)
BEGIN
SET NOCOUNT ON;
set #table='QAT_Tsentences'
set #column='SentenceElID'
set #delimiter='|'
set #tablekey='ID'
declare #sql as nvarchar(max), #tabkey as nvarchar(max), #txt as
nvarchar(1000), #txtSplitted as nvarchar(255)
DECLARE #pos integer--, #delimiter as nchar(1)
DECLARE #Leftpiece nvarchar(255), #Rightpiece as nvarchar(255)
CREATE TABLE #t(tablekey nvarchar(max), txt nvarchar(1000))
set #sql= 'INSERT #t select '+#tablekey+','+#column+' from '+#table+' where
'+#column+' is not null'
exec(#sql)
drop table QAT_txtsplitted
CREATE TABLE QAT_txtsplitted(tablekey nvarchar(max), txt [nvarchar]
(max), txtSplitted [nvarchar](max), ID INT NOT NULL IDENTITY(1,1))
DECLARE c1 CURSOR
FOR
SELECT tablekey, txt
FROM #t
OPEN c1
FETCH NEXT FROM c1
INTO #tabkey,#txt
While (##Fetch_status = 0)
BEGIN
SET #Rightpiece = #txt
IF RIGHT(RTRIM(#Rightpiece),1) <> #delimiter
SET #Rightpiece = #Rightpiece + #delimiter
SET #pos = CHARINDEX(#delimiter, #Rightpiece)
WHILE #pos <> 0
BEGIN
SET #Leftpiece = LEFT(#Rightpiece, #pos - 1)
INSERT INTO QAT_txtsplitted (tablekey,txt,txtsplitted) VALUES
(#tabkey,#txt, #Leftpiece);
SET #Rightpiece = STUFF(#Rightpiece, 1, #pos, '')
SET #pos = CHARINDEX(#delimiter, #Rightpiece)
END
FETCH NEXT FROM c1
INTO #tabkey,#txt
END
CLOSE c1
DEALLOCATE c1
drop table #t
nchar(1),#tablekey as decimal(15))
print 'table results : QAT_txtsplitted'
END
Please find below my result where i have tried to split for a particular tablekey.
Here is part of a SP THAT I wrote to remove a cursor
BEGIN
DECLARE #Modules TABLE
(
ID INT IDENTITY(1,1)
, ModuleId INT
, ModuleShortName NVARCHAR(256)
, ModuleTableName NVARCHAR(260)
)
INSERT INTO #Modules select ModuleId,ModuleShortName ,ModuleTableName from Mods
DECLARE #Count INT, #Counter INT, #ModuleId INT, #ModuleTableName NVARCHAR(260)
SELECT #Count = COUNT(*) FROM #Modules m
SET #Counter = 1
WHILE #Counter <= #Count
BEGIN
SELECT #ModuleId = ModuleId, #ModuleTableName = ModuleTableName FROM #Modules m WHERE ID = #Counter -- extracting values from the table by counternum
-- do something with data
-- puttin inner logic
SET #Counter = #Counter + 1
END
Cursors can be poor performers in SQL Server which is designed for set-based operations. There are some methods to increase the performance of a cursor by applying additional arguments which can be read about here. If you only want to sequentially step through your cursor, then you could alleviate a lot of overhead by using the LOCAL FAST_FORWARD arguements.
DECLARE C1 CURSOR LOCAL FAST_FORWARD FOR <your query>
Another method, which could perform better, is a while loop.
DECLARE #cnt INT = 0;
Declare #t_CNT = Select Count(*) from t
WHILE #cnt < #t_CNT
BEGIN
// do your work here
SET #cnt = #cnt + 1;
END;

If statement inside Trigger not working SQL(Oracle)

My goal is to create a trigger that checks if the number you are trying to enter NR_RECIBO is in the table Doc_cabecalho with the attribute TIPO_DOC = 4
CREATE OR REPLACE TRIGGER ValidaRecibo
BEFORE INSERT ON Recibo
FOR EACH ROW
DECLARE val NUMBER;
BEGIN
SELECT COUNT(*) INTO val
FROM Doc_cabecalho
WHERE (TIPO_DOC = 4 AND NR_DOCUMENTO = :NEW.NR_RECIBO);
IF val = 0
THEN (-20502, ' Only from department 4 ');
END IF;
END ValidaRecibo;
Yet, this raises the following error:
PLS-00103: Encountered the symbol "," when expecting one of the following:
* & = - + < / > at in is mod remainder not rem <> or != or ~= >= <= <> and or like like2 like4 likec as between || member submultiset
IF val = 0 THEN
raise_application_error(-20502, ' Only from department 6 ');
And you should decide between DOC_TIPO and TIPO_DOC ;)

Errors with identifiers in PL/SQL

I am trying to run some PL/SQL code but it contains some errors about identifiers plese help me with it. The code is not running
DECLARE
a := customer.purchase%TYPE;
id := &employee.empno;
BEGIN
UPDATE employee SET salary = salary + 5000;
UPDATE employee SET bonus = bonus + 1000 WHERE empno = &id;
SAVEPOINT sumeet;
UPDATE customer SET purchase = purchase + 5000 WHERE custid = a;
SELECT SUM(purchase) INTO a;
IF (a < 11000) THEN
ROLLBACK sumeet;
END IF;
COMMIT;
END;
/
in addition to Alen's fix, instead of the ROLLBACK why don't you do :
UPDATE customer SET purchase = purchase + 5000
WHERE custid = a
AND (select sum(purchase) from customer) + 5000 < 11000;
COMMIT;
1: This is wrong:
DECLARE
a := customer.purchase%TYPE;
id := &employee.empno;
You don't put a := after the variable name, & is invalid, and employee.empno is not a valid data type. i.e.:
DECLARE
a customer.purchase%TYPE;
id employee.empno%TYPE;
2: You don't need & to refer to the id variable:
UPDATE employee SET bonus = bonus + 1000 WHERE empno = &id;
i.e.:
UPDATE employee SET bonus = bonus + 1000 WHERE empno = id;
Here's a tip: Combine your two updates on employee into one.
UPDATE employee
SET salary = salary + 5000,
bonus = bonus + case when empno = &id then 1000 else 0 end;
Also, start using meaningful variable names.
Try with this block, but first change the values (1, 2) in the declaration block.
DECLARE
a customer.purchase%TYPE := 1;
id employee.empno%TYPE := 2;
BEGIN
UPDATE employee SET salary = salary + 5000;
UPDATE employee SET bonus = bonus + 1000 WHERE empno = id;
SAVEPOINT sumeet;
UPDATE customer SET purchase = purchase + 5000 WHERE custid = a;
SELECT SUM(purchase) INTO a FROM customer;
IF (a < 11000) THEN
ROLLBACK sumeet;
END IF;
COMMIT;
END;
/

Resources