Ho to update a field in tableA if it contains certain string that is present in another field(in reference table)? - sql

I am trying to standardise col_value(field) if it contains the string that is present in reference table. The valid_value will be replaced with invalid and the whole string will be updated to updated_col_value in TableA.
Table structures:
Basetabe: TableA
col_value,updated_col_value
ReferenceTable
Invalid_value, Valid_value
Currently to achieve this, i have written a cursor where i am passing every record and execute update statement. Which is why number of records= number of update statement. It makes my proc really really slow.
I just wanna make it in a single update statement instead of executing updating statement for each time. Could you please help.
Below is my current proc
SET #SQL_STR = 'SELECT col_value FROM TABLEA'
SET #VSQL = 'SET #CURSOR = CURSOR FORWARD_ONLY STATIC FOR ' + #SQL_STR + ' OPEN #CURSOR;'
EXEC SYS.SP_EXECUTESQL
#VSQL
,N'#CURSOR CURSOR OUTPUT'
,#update_column_cusror OUTPUT
fetch next from #update_column_cusror into #Col_value_variable
WHILE ##FETCH_STATUS = 0
BEGIN
set #updated_col_value_v=''
DECLARE get_valid_value_cursor CURSOR FOR
SELECT Invalid_value FROM REFERENCETABLE
open get_valid_value_cursor
fetch next from get_valid_value_cursor into #invalid_value_variable
WHILE ##FETCH_STATUS = 0
Begin
select #Char_to_replace = Valid_Value from REFERENCETABLE where Invalid_value=#invalid_value_variable
if #updated_col_value_v=''
set #replaced_value = replace(#Col_value_variable,#invalid_value_variable,#Char_to_replace)
else
set #replaced_value = replace(#updated_col_value_v,#invalid_value_variable,#Char_to_replace)
set #updated_col_value_v = #replaced_value
SET #SQL= '
update TABLEA
set updated_col_value='''+#updated_col_value_v+'''
where col_value='''+#Col_value_variable+'''
EXECUTE(#SQL)
fetch next from get_valid_value_cursor into #invalid_value_variable
End
close get_valid_value_cursor
deallocate get_valid_value_cursor

Could you please check following SQL Server Trigger on main table which validates entries and corrects from reference table
I hope it helps
create trigger ValidateEntries on TableA after Insert, Update
as
begin
update TableA
set
col_value = r.Valid_value,
updated_col_value = i.col_value
from inserted as i
inner join ReferenceTable r
on i.col_value = r.Invalid_value
where TableA.col_value = i.col_value
end
go

Related

Transaction context in use by another session - SQL SERVER

Good afternoon,
I'm creating a trigger to be present on 3 databases, one of which is on a linkedserver.
When inserting data in one of the databases, the objective is to replicate the information in the remaining databases.
Whenever I do an insert, I get the error 'Transaction context in use by another session.'
Anyone knows why this is happening? And is there a way to surprass this?
DROP TRIGGER Insertdata
USE A
GO
SET ANSI_NULLS ON
SET ANSI_WARNINGS ON
SET QUOTED_IDENTIFIER OFF
GO
CREATE TRIGGER Insertdata ON DATA WITH ENCRYPTION FOR INSERT AS
SET NOCOUNT ON
if(Upper((SELECT DB_NAME()))=UPPER('A')) Begin
----------------------------------------------------------------------------------------------
--------------------------------------------- Insert Data ------------------------------------
----------------------------------------------------------------------------------------------
--Create variable for database name and query variable
DECLARE #DB_Name VARCHAR(100) -- database name
DECLARE #Local VARCHAR(1) -- database name
DECLARE #query VARCHAR(4000) -- query variable
--Declare the cursor
DECLARE db_cursor CURSOR LOCAL FOR
-- Populate the cursor with the selected database name
select t.name,t.islocal from (
Select name, '1' as islocal from master.sys.databases WHERE name in ('A','B','C')
union all
select name, '0' as islocal from LinkedServer.master.sys.databases WHERE name in ('A','B','C')
)t
OPEN db_cursor
--Moves the cursor to the first point and put that in variable name
FETCH NEXT FROM db_cursor INTO #DB_Name,#Local;
-- while loop to go through all the DB selected
WHILE ##FETCH_STATUS = 0
BEGIN
if(#Local ='0') begin
Set #DB_Name = 'LinkedServer.' + #DB_Name
end
-- Check if received data exists
SET #query = N'Select id from ' + #DB_Name +'.dbo.data where data.id='''+(Select inserted.id from inserted)+''''
EXEC(#query)
--If doesnt exists, then insert data
if ##ROWCOUNT < 1 begin
SET #query = N'INSERT INTO ' + #DB_Name +'.dbo.data(id,name,surname)
values('+''''+(select inserted.id from inserted)+''''
+','+''''+(select convert(varchar,inserted.name) from inserted)+''''
+','+''''+(select convert(varchar,inserted.surname) from inserted)+''''
+')'
EXEC (#query)
--Fetch the next record from the cursor
end
FETCH NEXT FROM db_cursor INTO #DB_Name,#Local
END
--Close and deallocate cursor
CLOSE db_cursor
DEALLOCATE db_cursor
End
GO
SET ANSI_NULLS OFF
SET ANSI_WARNINGS OFF
SET QUOTED_IDENTIFIER OFF
GO
EDIT:
I think the problem here is the fact, that my trigger is being executed on different databases. For example, the current database is A, so the trigger should execute on B and C, but I realized that when A calls B, B executes the trigger aswell. C doesn't executes because gives the error on B. Thought db_name() would fix this, but guess it doesn't

Running a procedure from the IDE goes, while if a trigger calls it goes wrong

I have created a trigger, it will run when the user change the document status. The SQL version where I worked is the 15.0.2000.5 and everything goes right. Instead, the customer has the 12.0.4100.1 and the trigger give me some error with the procedure.
The procedure scope is to create a new line in the current document. When the trigger run the procedure insert the new line but some fields are not correctly compiled and in the software, used from the customer, these new lines I can't see.
I try the procedure without the trigger and does its job. I try the trigger code in my IDE and perform correctly its tasks.
I have try to debug saving some data in temporary table to see input and output result but they seem correct.
I thought it was a permission issued but I have created and run this one with the same user, however I try this piece of code:
BEGIN TRY
EXEC(N'
USE [master];
GRANT CONTROL SERVER TO [administrator];
');
END TRY
BEGIN CATCH
DECLARE #DoNothing INT;
END CATCH;
Anyone can say me other issues? Maybe the SQL version doesn't allow some features?
If you need me code.
This is the trigger code:
CREATE TRIGGER <trigger name> ON <document table>
AFTER UPDATE
AS
SET NOCOUNT ON;
-- control status changed
IF UPDATE(Status)
BEGIN
DECLARE #optionCode VARCHAR(6) = 'TRPREV'
DECLARE #documentsType VARCHAR(100)
DECLARE #released VARCHAR(4) = '001'
DECLARE #notReleased VARCHAR(4) = '002'
DECLARE #documentIndex INT
DECLARE #newStatus VARCHAR(4)
-- get all the orders code
SET #documentsType = (SELECT TOP 1 ValueOption FROM Options WHERE OP_Cle = #optionCode)
SELECT #doCpt = DO_Cpt, #newStatus = DO_Status
FROM inserted
INNER JOIN TypeDoc TD ON TD.TD_Code = DO_TypeDoc AND CHARINDEX(TD_Code, #documentsType) > 0
-- delete the previsionelle
IF ( #newStatus = #notReleased )
BEGIN
-- delete some lines
END
ELSE
BEGIN
-- create the previsionelle
IF ( #newStatus = #released OR ISNULL(#newStatus, '') = '' )
BEGIN
EXEC crea_previsionali #doCpt
END
END
END -- update if
GO
This is the procedure code:
CREATE PROCEDURE <procedure name>
#DoCpt AS INT
AS
-- declared some variables
DECLARE #Cursor as CURSOR;
-- preare the cursors
SET #Cursor = CURSOR FOR
SELECT Cpt,TypeDoc,ItemType,DateEch,PrixHT,PrixHTMB,ShipToAddressType,MarginUC_Base
FROM Lignes
WHERE DOCpt=#DoCpt
AND Previsionnelle=0
ORDER BY DOCpt,No
OPEN #Cursor;
FETCH NEXT FROM #Cursor INTO #T_LiCpt,#T_TypeDoc,#T_ItemType,#T_DateEch,#T_PrixHT,#T_PrixHTMB,#T_ShipToAddressType,#T_MarginUC_Base;
WHILE ##FETCH_STATUS = 0
BEGIN
IF EXISTS (SELECT GR_Pere AS C FROM vw_GrammaireIT WITH(NOLOCK)
WHERE (GR_Type = 0)
AND (GR_Prev = 1)
AND (GR_Fils = #T_TypeDoc)
AND (ItemType = #T_ItemType))
BEGIN
PRINT cast(#T_Cpt as VARCHAR (50))
SELECT #PROV_TD_Code=TD_Code, #PROV_TD_TypePrix=TD_TypePrix, #PROV_TD_ImpStock=ISNULL(TD_ImpStock, 0)
, #PROV_TD_reliquat=ISNULL(TD_Reliquat, 0), #PROV_TD_Transfer=ISNULL(TD_Transfer, 0)
FROM vw_GrammaireIT WITH(NOLOCK)
INNER JOIN TypeDoc WITH(NOLOCK) ON Pere=Code
WHERE Fils=#T_TypeDoc and Prev=1 AND ItemType=#T_ItemType
exec sp_CreateProvisional #T_LI_DateEch
,#T_PrixHTMB
,#T_PrixHT
,#T_ShipToAddressType
,#prov
,#PROV_TD_Code
,#PROV_TD_ImpStock
,1
,#T_LiCpt
,#T_MarginUC_Base
,#PROV_TD_reliquat
END
FETCH NEXT FROM #Cursor INTO #T_LiCpt,#T_TypeDoc,#T_ItemType,#T_DateEch,#T_PrixHT,#T_PrixHTMB,#T_ShipToAddressType,#T_MarginUC_Base;
CLOSE #Cursor;
DEALLOCATE #Cursor;
GO

Run a Script Each time a table is modified

I have created a batch script which prints information on the table into a label and saves it into pdf. At the moment, I am giving the script a number, which is the ItemCode and it prints out the rest of the information in the table.
Well now I'm going much further, my goal is to run the script each time the table is modified, or a new row is added or even if a single field is modified. When this happens it would check which row has been modified and It would run the script with the ItemCode which has been modified.
Been looking for something similar to this but couldn't find anything precise enough, so any help would be nice!
The code below is part of a trigger I am using. It writes old values and new values into a special table to track all important changes. The updated_idx is send with the SQL command to tell, what user is doing it.:
USE [Demo] -- database
GO
/****** Object: Trigger [dbo].[update_Address] Script Date: 26.07.2018 11:16:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[update_Address] ON [dbo].[Address] for UPDATE AS
DECLARE #fieldname varchar(128) = '- empty -'
DECLARE #newValue varchar(2048) = '- empty -'
DECLARE #oldValue varchar(2048)= '- empty -'
DECLARE #Updated int = datediff(s,'01/01/1970',SYSUTCDATETIME())
DECLARE #Updated_IDX int = -1
DECLARE #ID int = -1
DECLARE db_cursor CURSOR FOR SELECT Address_id, Updated_IDX FROM inserted
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #ID, #Updated_IDX -- this takes the current id
WHILE ##FETCH_STATUS = 0
BEGIN
SET NOCOUNT ON
If UPDATE([ZipCode])
BEGIN
SELECT #OldValue=b.ZipCode, #NewValue=a.ZipCode FROM inserted a, deleted b
IF #NewValue <> #OldValue
BEGIN
INSERT INTO TransactionLog ([ID],[TableName],[Type],[FieldName],[newValue],[oldValue],[Updated],[Updated_IDX]) values (#ID,'Address','U','ZipCode',#newValue,#oldValue,#Updated,#Updated_IDX);
END
END
If UPDATE([City])
BEGIN
SELECT #OldValue=b.City, #NewValue=a.City FROM inserted a, deleted b
IF #NewValue <> #OldValue
BEGIN
INSERT INTO TransactionLog ([ID],[TableName],[Type],[FieldName],[newValue],[oldValue],[Updated],[Updated_IDX]) values (#ID,'Address','U','City',#newValue,#oldValue,#Updated,#Updated_IDX);
END
END
FETCH NEXT FROM db_cursor INTO #ID, #Updated_IDX
End
CLOSE db_cursor
DEALLOCATE db_cursor

Trigger that triggers when a single column (boolean) is updated from TRUE to FALSE (not vice versa)

I have limited experience creating trigger. Here is one I created recently:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[NameTrigger]
ON [dbo].[dbUSNs]
after INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
declare #URNs varchar(100)
declare #Forenames varchar(200)
declare #Surname varchar(100)
declare #Reference int
set #URNs = ''
set #Forenames = ''
set #Surname = ''
set #Reference = 0
declare PersonCursor cursor FOR
select
Replace(Replace(Replace(Forenames,char(39),''),'-',''),' ',''),
Replace(Replace(Replace(Surname,char(39),''),'-',''),' ',''),
Reference,USN from inserted
open PersonCursor
FETCH NEXT FROM PersonCursor INTO #Forenames, #Surname, #Reference, #URNs
WHILE ##FETCH_STATUS = 0
BEGIN
update Person set ForenamesCleansed = #Forenames, SurnameCleansed= #Surname, URNsCleansed=#URNs
where reference = #Reference
FETCH NEXT FROM PersonCursor INTO #Forenames, #Surname, #Reference,#URNs
END
CLOSE PersonCursor
DEALLOCATE PersonCursor
END
I want to created a trigger that is triggered when a specific column (called update) changes from 0 to 1. Is this possible?
You're using Sql Server so:
you have two special tables:
INSERTED and DELETED.
These tables works in this way:
When you apply an INSERT statement in the trigger you'll find full the table INSERTED
When you apply an UPDATE statement in the trigger you'll find full the table INSERTED and DELETED
When you apply an DELETE statement in the trigger you'll find full the table DELETED
In the INSERTED table you'll find all new value, in the DELETED, you'll find the old value, so when you write a trigger you can check the old and the new value of your boolean field, so when the condition is satisfacted you can apply the other changes you want.
You just need an extra IF within your cursor logic:
FETCH NEXT FROM PersonCursor INTO #Forenames, #Surname, #Reference, #URNs
WHILE ##FETCH_STATUS = 0
BEGIN
IF (inserted.[Update] = 0 AND deleted.[Update] = 1) --Added
BEGIN
update Person set ForenamesCleansed = #Forenames, SurnameCleansed= #Surname, URNsCleansed=#URNs
where reference = #Reference
FETCH NEXT FROM PersonCursor INTO #Forenames, #Surname, #Reference,#URNs
END
END
END
You may want to invert the logic, there is a mismatch between the question and its title regarding the change direction.

SET NOCOUNT ON and Cursors

I have a stored proc that calls several store procs, each of which insert dummy data into a single table each. It works fine except that for each loop in the cursor a single row of results is dispayed - just showing ClubcardId = 2, ClubcardId = 3 etc.
I have used the SET NOCOUNT ON but this doesn't seem to help. I'm looking for this stored proc to create several million rows so, SQL printing the result for each row will be an issue.
Could anyone please advise how to prevent the output from being displayed. I have copied the parent stored proc below. I can be sure that the display is not coming from the child stored proc - lap_CreateClubcardTransaction.
If I change:
DECLARE Clubcard_Cursor CURSOR FAST_FORWARD FOR
SELECT ClubcardId FROM Clubcard
...to:
DECLARE Clubcard_Cursor CURSOR FAST_FORWARD FOR
SELECT ClubcardId as 'TEST' FROM Clubcard
...then the I get the value 'TEST' displayed for each row of the cursor.
Here's the parent stored proc:
ALTER PROCEDURE [dbo].[lap_CreateDummyData]
AS
SET NOCOUNT ON
DECLARE #NumberOfCustomers bigint
DECLARE #NumberOfTransactions bigint
SET #NumberOfCustomers = 50000
SET #NumberOfTransactions = 10
EXEC lap_CreateCustomer #NumberOfCustomers = #NumberOfCustomers;
EXEC lap_CreateCustomerPreference #NumberOfCustomers = #NumberOfCustomers;
EXEC lap_CreateClubCard #NumberOfCustomers = #NumberOfCustomers;
EXEC lap_CreateClubCardOffer #NumberOfCustomers = #NumberOfCustomers;
--get static data details to use when creating transaction records
DECLARE #TransactionType tinyint
DECLARE #TransactionReasonID tinyint
DECLARE #TescoStoreID int
DECLARE #PartnerID bigint
DECLARE #PartnerOutletID bigint
DECLARE #ClubcardID bigint
SET #TransactionType = (SELECT TOP 1 TransactionType FROM TransactionType)
SET #TransactionReasonID = (SELECT TOP 1 TransactionReasonID FROM TransactionReason)
SET #TescoStoreID = (SELECT TOP 1 TescoStoreId FROM TescoStore)
SET #PartnerID = (SELECT TOP 1 PartnerID FROM PartnerOutlet)
SET #PartnerOutletID = (SELECT TOP 1 PartnerOutletID FROM PartnerOutlet)
DECLARE Clubcard_Cursor CURSOR FAST_FORWARD FOR
SELECT ClubcardId FROM Clubcard
OPEN Clubcard_Cursor
FETCH NEXT FROM Clubcard_Cursor
INTO #ClubcardID SET NOCOUNT ON
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC lap_CreateClubcardTransaction #NumberOfTransactions = #NumberOfTransactions, #ClubcardID = #ClubcardID, #TransactionType = #TransactionType, #TransactionReasonID = #TransactionReasonID, #TescoStoreId = #TescoStoreID, #PartnerID = #PartnerID, #PartnerOutletID = #PartnerOutletID;
FETCH NEXT FROM Clubcard_Cursor;
END;
CLOSE Clubcard_Cursor;
DEALLOCATE Clubcard_Cursor;
You need to direct the FETCH into variable inside the loop as well:
WHILE ...
BEGIN
...
FETCH NEXT FROM Clubcard_Cursor INTO #ClubcardID
END
Under no circumstances would I use a cursor to insert a million rows one row at a time. That will take hours. This is an example of a poor use of a cursor. Create a proc that will do a set-based operation.
SET NOCOUNT ON is useless inside the fetch, so remove it from there. It seems lap_CreateClubcardTransaction contains a SELECT statement inside its code. Can you check if this is true?