Code inside SQL Cursor will only execute first FETCH - sql

I have a SQL Cursor which I'm having issues with. When I remove the IF #debug = 1 statement from inside the cursor, only the first record from the FETCH will get updated but if I leave the IF #debug = 1 all the required records are updated. Any idea as to why this is happening, I know most likely something is wrong with my Cursor? Code is below:
DECLARE Verify_Shipment_Cur CURSOR LOCAL FAST_FORWARD READ_ONLY FOR
SELECT DISTINCT lpd_shipment_id, lpd_po_number, lpd_customer_id, lpd_sku, lpd_lottable01, lpd_lottable02, lpd_lottable03, lpd_putaway_zone, lpd_pdt
FROM PO_DETAIL01(NOLOCK)
WHERE lpd_shipment_id = #i_SHIPMENT_ID
AND lpd_po_number = #i_POKEY
AND lpd_customer_id = #i_CUSTOMER_ID
AND lpd_status = #AvailableStatus
OPEN Verify_Shipment_Cur
WHILE #ShipmentSKUCount >= #ShipmentSKUCountCur
BEGIN
FETCH NEXT FROM Verify_Shipment_Cur INTO #ShipmentID, #POKey, #CustomerID, #SKU, #Lottable01, #Lottable02, #Lottable03, #PutawayZone, #PDT
IF EXISTS(SELECT 1 FROM PO_DETAIL(NOLOCK) WHERE pd_asn_number = #i_SHIPMENT_ID AND pd_po_number = #i_POKEY
AND pd_sku = #SKU AND pd_type = #ShmtType AND pd_ordered_qty <> pd_received_qty)
BEGIN
UPDATE PO_DETAIL
SET pd_adjusted_qty = pd_ordered_qty - pd_received_qty
WHERE pd_asn_number = #i_SHIPMENT_ID
AND pd_po_number = #i_POKEY
AND pd_sku = #SKU
AND pd_type = #ShmtType
END
UPDATE PO_DETAIL
SET pd_lottable01 = #Lottable01
, pd_lottable02 = #Lottable02
, pd_lottable03 = #Lottable03
, pd_lottable04 = ''
, pd_lottable05 = #Date
, pd_putaway_zone = #PutawayZone
, pd_pdt = #PDT
, pd_status = #VerifiedStatus
WHERE pd_asn_number = #i_SHIPMENT_ID
AND pd_po_number = #i_POKEY
AND pd_sku = #SKU
AND pd_type = #ShmtType
UPDATE PO_DETAIL01
SET lpd_status = #VerifiedStatus
WHERE lpd_shipment_id = #i_SHIPMENT_ID
AND lpd_po_number = #i_POKEY
AND lpd_customer_id = #i_CUSTOMER_ID
AND lpd_status = #AvailableStatus
IF #debug = 1
BEGIN
SELECT #ShipmentSKUCount AS SKUCOUNT
, #ShipmentSKUCountCur AS SKUCOUNTCUR
, #SKU AS SKU
, #ShipmentID AS SHIPMENT
, #POKey AS POKEY
END
SET #ShipmentSKUCountCur = #ShipmentSKUCountCur + 1
END
CLOSE Verify_Shipment_Cur
DEALLOCATE Verify_Shipment_Cur

It looked ok to me but i obviously dont have your data to assist. Can I recommend putting a few print statements in various parts of your cursor. That way you can see how the code is actually flowing. It doesnt help but thats what I would do.

Please, can you try to define explicilty both variables: set #ShipmentSKUCount=[initial value],set #ShipmentSKUCountCur=[initial or constant value] and see what will happen?
Also, I found there is no checking for a ##FETCH_STATUS. It may also result into reading same row twice or more.
Please, give a feedback.

Related

Slow performance for update cursor

I´m running this code to update my warehouse quantities based on the day before (yesterday). It works, but it is veeery slow.
Each day, the system generates around 50k+ rows, each one being for an address (position) on the warehouse.
So, the cursor gets each address and then search for the quantity the day before, then updates.
Example:
Adress 01B134010 on 08/04/2019 had quantity 500
Adress 01B134010 on 09/04/2019 had quantity 450
So, when the code runs, it updates the column "DIF_DIA" with -50
Today, i´ve run the code and just for one brach (47k+ rows) it took more than 1 hour.
Is there a better/faster way to do it?
DECLARE #ARMAZEM VARCHAR(2),
#FILIAL VARCHAR(3),
#CLIENTE VARCHAR(6),
#GRPESTOQUE VARCHAR(6),
#DATA AS VARCHAR(8),
#ENDERECO as VARCHAR(30),
#DIFERENCA AS FLOAT
-- reinicia variaveis
SET #armazem = ''
SET #FILIAL = ''
SET #CLIENTE = ''
SET #GRPESTOQUE = ''
SET #DATA = ''
SET #ENDERECO = ''
SET #DIFERENCA = ''
DECLARE CUR_UPD_DIF_DIA CURSOR FOR
SELECT CODIGO_ARMAZEM,
CODIGO_FILIAL,
CODIGO_CLIENTE,
GRP_EST_COD,
DATA_SALDO,
CODIGO_ENDERECO
FROM WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO
WHERE DATA_SALDO >= '20190401'
and CODIGO_FILIAL = '106'
AND DIF_DIA IS NULL
FOR UPDATE OF DIF_DIA
OPEN CUR_UPD_DIF_DIA
FETCH NEXT FROM CUR_UPD_DIF_DIA INTO #ARMAZEM,
#FILIAL,
#CLIENTE,
#GRPESTOQUE,
#DATA,
#ENDERECO
WHILE ##FETCH_STATUS = 0
BEGIN
-- pega a quantidade deste endereço ontem
SELECT #DIFERENCA = IsNull(QUANTIDADE_PALLET,0)
FROM WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO T1
WHERE T1.DATA_SALDO = dateadd(day, -1 , #DATA)
AND T1.GRP_EST_COD = #GRPESTOQUE
AND T1.CODIGO_CLIENTE = #CLIENTE
AND T1.CODIGO_ARMAZEM = #ARMAZEM
AND T1.CODIGO_ENDERECO = #ENDERECO
AND T1.CODIGO_FILIAL = #FILIAL
-- e atualiza o endereço na data de hoje, tendo o valor da DIF_DIA a quantidade atual - a quantidade de ontem, assim, tem a diferença.
-- se positivo, incrementou o endereço. Se negativo, diminuiu do endereço. Válido, na prática, somente para BLOCADO pois PP sempre terá apenas "1" de quantidade
UPDATE WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO
SET DIF_DIA = (QUANTIDADE_PALLET - #DIFERENCA)
WHERE CURRENT OF CUR_UPD_DIF_DIA
SET #DIFERENCA = 0
FETCH NEXT FROM CUR_UPD_DIF_DIA INTO #ARMAZEM,
#FILIAL,
#CLIENTE,
#GRPESTOQUE,
#DATA, #ENDERECO
END
CLOSE CUR_UPD_DIF_DIA
DEALLOCATE CUR_UPD_DIF_DIA
A set based approach will outperform a cursor like this orders of magnitude.
Based on your query, the set based approach could be something like this.
Disclaimer: the query is untested.
UPDATE WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO
SET DIF_DIA = (QUANTIDADE_PALLET - (
SELECT IsNull(QUANTIDADE_PALLET,0)
FROM WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO T1
WHERE T1.DATA_SALDO = dateadd(day, -1 , WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.DATA_SALDO)
AND T1.GRP_EST_COD = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.GRP_EST_COD
AND T1.CODIGO_CLIENTE = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_CLIENTE
AND T1.CODIGO_ARMAZEM = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_ARMAZEM
AND T1.CODIGO_ENDERECO = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_ENDERECO
AND T1.CODIGO_FILIAL = WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO.CODIGO_FILIAL
))
WHERE DATA_SALDO >= '20190401'
and CODIGO_FILIAL = '106'
AND DIF_DIA IS NULL
This is another option to optimize your UPDATE by doing a single operation instead of going Row-By-Agonizing-Row. Hopefully, I got the correct logic.
UPDATE atual
SET DIF_DIA = atual.QUANTIDADE_PALLET - IsNull(ontem.QUANTIDADE_PALLET,0)
FROM WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO atual
LEFT
JOIN WMS_OCUPACAO_ARMAZEM_DETALHE_V2_ESTATICO ontem ON atual.DATA_SALDO = dateadd(day, 1 , ontem.DATA_SALDO)
AND atual.GRP_EST_COD = ontem.GRP_EST_COD
AND atual.CODIGO_CLIENTE = ontem.CODIGO_CLIENTE
AND atual.CODIGO_ARMAZEM = ontem.CODIGO_ARMAZEM
AND atual.CODIGO_ENDERECO = ontem.CODIGO_ENDERECO
AND atual.CODIGO_FILIAL = ontem.CODIGO_FILIAL
WHERE atual.DATA_SALDO >= '20190401'
AND atual.CODIGO_FILIAL = '106'
AND atual.DIF_DIA IS NULL;

Why are the scalar variables undeclared inside cursor?

I wrote a code snippet that is supposed to read a mapping table (BcwmWDConfig) and insert/update values of an outdated table (Items) in an old database (BCWM99) to a newer table in another database (WebDET99.dbo.Items).
I am getting errors that my supposedly declared (at least I thought so) variables are not declared.
DECLARE #v_bcwm_id INT = 0,
#v_wd_guid VARCHAR = '';
DECLARE bcwm_config_read_cursor CURSOR FOR
SELECT c.id, c.guid
FROM BcwmWDConfig c
WHERE c.guid IS NOT NULL;
PRINT '----READING CONFIG----';
OPEN bcwm_config_read_cursor
FETCH NEXT FROM bcwm_config_read_cursor INTO #v_bcwm_id, #v_wd_guid
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT '----READ CONFIG FOR ID: '+#v_bcwm_id+' AS GUID '+#v_wd_guid+'----'
PRINT '----INSERTING ITEMS WITH PARENT GUID: '+#v_wd_guid+'----'
INSERT INTO WebDET99.dbo.Items
SELECT
i.Id, i.Text,
CAST(#v_wd_guid AS VARCHAR(50)),
CAST(i.Linked_Feld AS VARCHAR(50)),
i.Linked_Item, i.Order
FROM
BCWM99.dbo.Items i
WHERE
i.Parent_Feld = #v_bcwm_id
PRINT '----INSERTED ITEMS WITH PARENT GUID: '+#v_wd_guid+'-----'
FETCH NEXT FROM bcwm_config_read_cursor INTO #v_bcwm_id, #v_wd_guid
END
CLOSE bcwm_config_read_cursor;
DEALLOCATE bcwm_config_read_cursor;
SET #v_bcwm_id = 0, #v_wd_guid = '';
OPEN bcwm_config_read_cursor
FETCH NEXT FROM bcwm_config_read_cursor
INTO #v_bcwm_id, #v_wd_guid
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT '----READ CONFIG FOR ID: '+#v_bcwm_id+' AS GUID '+#v_wd_guid+'----'
PRINT '----UPDATING ITEMS WITH LINKED FIELD: '+#v_wd_guid+'----'
UPDATE WebDET99.dbo.Items i
SET i.Linked_Feld = #v_wd_guid
WHERE i.Parent_Feld = CAST(#v_bcwm_id AS VARCHAR(50))
PRINT '----UPDATED ITEMS WITH LINKED FIELD: '+#v_wd_guid+'-----'
FETCH NEXT FROM bcwm_config_read_cursor
INTO #v_bcwm_id, #v_wd_guid
END
CLOSE bcwm_config_read_cursor;
DEALLOCATE bcwm_config_read_cursor;
SET #v_bcwm_id = 0,
#v_wd_guid = '';
Why are they undeclared inside the cursors? Also I am pretty sure I have some smaller syntax errors in my code, I am not used to SQL Server, maybe someone finds some other issues with this procedure.
Your code is sysntactically incorrect.
The first error: you cannot use Order (reserved word) without square brackets:
SELECT
i.Id, i.Text,
CAST(#v_wd_guid AS VARCHAR(50)),
CAST(i.Linked_Feld AS VARCHAR(50)),
i.Linked_Item, i.[Order] -----------------------------square brackets!
Second error:
SET #v_bcwm_id = 0, #v_wd_guid = '';
Should be:
SET #v_bcwm_id = 0; set #v_wd_guid = ''; ------------the same in the last row
The last error: get rid of "i":
UPDATE WebDET99.dbo.Items
SET Linked_Feld = #v_wd_guid
WHERE Parent_Feld = CAST(#v_bcwm_id AS VARCHAR(50))
Only then your code can be compiled.

Adding Trailing Zeros to an int field in SQL

In the process of resequencing my web items, I am trying to append 4 zeros on the end of each item sequence number. Everything I try does not work, it seems to automatically remove the trailing zeros because of formatting. This is an int field. The only thing I got to work for 1 item as a test was actually doing a replace such as:
update sacatalogitem
set itm_seq = replace(itm_seq, '8021', '80210000')
where itm_id = '13' and ctg_id = '917'
I have tried using a cursor with the replace and it strips the trailing zeros when executing this:
declare #SEQ int
declare #ID int
declare #CTG int
declare cur cursor for
select a.itm_seq, a.itm_id, a.ctg_id from sacatalogitem as a
join saitem as b on b.itm_id = a.itm_id
where a.cat_id = '307' and a.itm_id = '13' and a.ctg_id = '917'
open cur
fetch next from cur into #SEQ,#ID,#CTG
While (##FETCH_STATUS=0)
Begin
Update sacatalogitem
set itm_seq = replace(itm_seq, #SEQ, #SEQ + '0000')
where itm_id = #ID and ctg_id = #CTG
Update saitem
set itm_import_action = 'P'
where itm_id = #ID
fetch next from cur into #SEQ,#ID,#CTG
end
close cur
deallocate cur
This also fails for me:
update sacatalogitem
set itm_seq = itm_seq + '0000'
where itm_id = '13' and ctg_id = '917'
Any help would be appreciated for this small frustrating problem.
If it is an integer field, MULTIPY IT BY 10000.
update sacatalogitem
set itm_seq = itm_seq * 10000
where itm_id = '13' and ctg_id = '917'
You could try
cast(cast (itemsec as varchar) + '0000' as int)
but frankly #VJHill 's answer is neater

SQL if else stored procedure

I'm writing a stored procedure and it's working for if but not for else. For if I'm getting the correct ID value but for else it's just giving me null.
SELECT #ID = ID FROM PRODUCTS WHERE SN = #SN
SELECT #Chip_ID = Chip_ID FROM PRODUCTS WHERE SN = #SN
SELECT #Power = Power From Module_Cycle WHERE ChipID = #Chip_ID
SELECT #Test_ID=Test_ID FROM TestEE_Mod WHERE ID=#ID AND #TypeID = TypeID
IF(#Test_ID IS NOT NULL)
BEGIN
IF(#TypeID = '3')
BEGIN
SELECT #Temp_TestID=TestID FROM TempCycle WHERE ChipID = #Chip_ID AND #Power = 'false'
BEGIN
UPDATE TestEE_Mod SET Temp_TestID = #Temp_TestID WHERE ID = #ID AND TypeID = #TypeID
END
END
ELSE
BEGIN
SELECT #Temp_TestID=TestID FROM TempCycle WHERE ChipID = #Chip_ID AND #Power = 'true'
BEGIN
UPDATE TestEE_Mod SET Temp_TestID = #Temp_TestID WHERE ID = #ID AND TypeID = #TypeID
END
END
END
Most likely the issue lies in the #Power variable. In your two SELECT statements, the only difference is that one includes #Power = 'false' in the WHERE clause, while the other includes #Power = 'true'.
Since #Power is a variable, not an actual column in your TempCycle table, I'm guessing you actually meant to filter by Power = #Power in both of them.

triggers are not firing when I use bulk copy in code

I have a trigger called "updateFriendlyURLTitle" in dbo.Aritcle table. When a individual article inserted, that trigger working fine.
But in article importing process: I've used the following codes. This codes make copy article but it doesn't fire the trigger to generate FriendlyUrl.
private void WriteArticlesToDatabase<TData>(DataSet ds, SqlTableDetails tableDetails, IEnumerable<TData> newArticles, SqlTransaction transaction)
{
var dt = WriteToDataTable(ds, tableDetails.Table, newArticles);
using (var bulkCopy = new SqlBulkCopy(_destConnection, SqlBulkCopyOptions.FireTriggers, transaction))
{
bulkCopy.DestinationTableName = tableDetails.ToString();
bulkCopy.WriteToServer(dt);
}
}
My trigger is like below:
ALTER TRIGGER [dbo].[updateFriendlyURLTitle] ON [dbo].[Articles]
AFTER INSERT, UPDATE
AS
IF ##ROWCOUNT > 0
BEGIN
IF COLUMNS_UPDATED() != 0x0000200000100000 -- columns other than newsCounterViews have been updated
BEGIN
DECLARE #oldestfulllucenebuild AS DATETIME
DECLARE #deletedNewsID INT
DECLARE #newsStatus BIT
DECLARE #maxcalcimp AS FLOAT
DECLARE #insertedCalculatedImportance INT
DECLARE #insertedNormalisedCalculatedImportance INT
DECLARE #insertedSeoURLTitle VARCHAR(255)
select #oldestfulllucenebuild = min(luceneIndexCreatedDate)
from Lucene_Indexes
where luceneIndexType = 'news'
select #oldestfulllucenebuild = dateAdd(year,10,#oldestfulllucenebuild)
select #maxcalcimp = cast(#oldestfulllucenebuild as float) * 48 *100 --the max importance
select #insertedCalculatedImportance = inserted.newsCalculatedImportance,
#insertedNormalisedCalculatedImportance = inserted.newsNormalisedCalculatedImportance,
#insertedSeoURLTitle = inserted.newsSeoURLTitle
from inserted
--if the current statement is updating the importance or seo columns then do not perform this query (so it doesn't get stuck in a loop)
IF (NOT UPDATE(newsCalculatedImportance)) AND (NOT UPDATE(newsNormalisedCalculatedImportance)) AND (NOT UPDATE(newsSeoURLTitle))
OR
--if it is inserting a new record then perform the query
(#insertedCalculatedImportance = 0 AND #insertedNormalisedCalculatedImportance is null AND #insertedSeoURLTitle = '')
BEGIN
update Articles
set newsCalculatedImportance = cast(cast(inserted.newsArticledate as float )*48 + inserted.newsimportance AS int)
, newsNormalisedCalculatedImportance = (1/ #maxcalcimp) * cast(cast(inserted.newsArticledate as float )*48 + inserted.newsimportance AS int)
, newsSeoURLTitle = LEFT(dbo.getSEOURLTitle(inserted.newstitle), 255)
from Articles inner join inserted on
Articles.newsid = inserted.newsid
END
SELECT #deletedNewsID = newsID, #newsStatus = newsStatus
FROM inserted
IF(#newsStatus = 0)
BEGIN
DELETE FROM tbl_DenormalisedNews WHERE newsid = #deletedNewsID
DELETE FROM News_Deleted_DateTime
WHERE NewsID = #deletedNewsID
INSERT INTO News_Deleted_DateTime
VALUES (#deletedNewsID, getDate())
END
ELSE
BEGIN
--news status is 1, remove it from the news_deleted_datetime table if it exists
DELETE FROM News_Deleted_DateTime
WHERE NewsID = #deletedNewsID
END
-- newsImage1 optimisation: if newsImage1 = '' THEN has_image = FALSE ELSE has_image = TRUE
IF UPDATE(newsImage1)
UPDATE Articles SET
has_image = CASE WHEN Articles.newsImage1 = '' THEN CAST(0 AS bit) ELSE CAST(1 AS bit) END
FROM
Articles INNER JOIN inserted ON Articles.newsid = inserted.newsid
END
END
Does anyone knows how to fix this issue ?