TSQL Update variable only if there is a value - sql-server

I have an update statement that looks like this:
UPDATE sessions
SET currentStep = #currentStep,
chosenDepartment = #chosenDepartment,
proposedTimes = #proposedTimes
WHERE sessionID = #sessionID
AND empID = #empID;
I use the same stored procedure for multiple updates as each step in my process gets an additional value along the way.
My question: is there a way to only do this :
chosenDepartment = #chosenDepartment
if #cosenDepartment actually contains a value?
If I do it every time, it will overwrite it with blank data if I don't pass anything to it.

Try this:
UPDATE sessions
SET currentStep = #currentStep,
chosenDepartment = CASE WHEN #chosenDepartment IS NOT NULL THEN #chosenDepartment ELSE chosenDepartment END,
proposedTimes = #proposedTimes
WHERE sessionID = #sessionID
AND empID = #empID;
or this
UPDATE sessions
SET currentStep = #currentStep,
chosenDepartment = COALESCE(#chosenDepartment, chosenDepartment),
proposedTimes = #proposedTimes
WHERE sessionID = #sessionID
AND empID = #empID;
Note #1: SQL Server will translate COALESCE(#chosenDepartment, chosenDepartment)
into CASE WHEN #chosenDepartment IS NOT NULL THEN #chosenDepartment ELSE chosenDepartment END.
Note #2: If you want to avoid NULL #chosenDepartment but also if you want to avoid the updates with empty strings or 0 then you could use NULLIF function thus: COALESCE(NULLIF(#chosenDepartment,''), chosenDepartment) or COALESCE(NULLIF(#chosenDepartment,0), chosenDepartment).

You may put if statement before tsql so if #chosenDepartment is blank then tsql won't run.

Related

Retrieve value of a column after update?

I update a counter (no autoincrement ... not my database ...) with this FDQuery SQL:
UPDATE CountersTables
SET Cnter = Cnter + 1
OUTPUT Inserted.Cnter
WHERE TableName = 'TableName'
I execute FDQuery.ExecSQL and it works: 'Cnter' is incremented.
I need to retrieve the new 'Counter' value but the subsequent command
newvalue := FDQuery.FieldByName('Cnter').AsInteger
Fails with error:
... EDatabaseError ... 'CountersTables: Field 'Cnter' not found.
What is the way to get that value?
TFDQuery.ExecSQL() is meant for queries that don't return records. But you are asking your query to return a record. So use TFDQuery.Open() instead, eg:
FDQuery.SQL.Text :=
'UPDATE CountersTables' +
' SET Cnter = Cnter + 1' +
' OUTPUT Inserted.Cnter' +
' WHERE TableName = :TableName';
FDQuery.ParamByName('TableName').AsString := 'TableName';
FDQuery.Open;
try
NewValue := FDQuery.FieldByName('Cnter').AsInteger;
finally
FDQuery.Close;
end;
If the database you are connected to does not support OUTPUT, UPDATE OUTPUT into a variable shows some alternative ways you can save the updated counter into a local SQL variable/table that you can then SELECT from.
You have also the RETURNING Unified support Ok, doc only shows INSERT SQL but UPDATE works too.
And I should use a substitution variable for tablename

SQL Server trigger for changing value of column of inserted row according to delta from other table after insert?

I have 2 tables in my SQL Server database, for example [Camera] and [CameraData]. How to write a trigger which will change value in [CameraData] after row is inserted into [CameraData] due to delta in [Camera].
For example we have 2 cameras in [Camera]:
Camera 1 with {id} = 1 and {delta} = null
Camera 2 with {id} = 2 and {delta} = 3
So when we have automated insert into table [CameraData], f.e. :
Id_camera = 2, angle = 30, Changed = null
In that case we need to check either we have delta in [Camera] on camera 2 and if that's true we need to modify insert to:
Id_camera = 2, angle = 33 (angle + Camera.Delta), Changed = True
Update 1
According to comment [3] is the column in table [CameraData] where angle is placed
CREATE TRIGGER Delta_Angle
ON CameraData
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE CameraData
SET DeltaFlag = 1, [3] = inserted.[3] + i.DeltaAngle
FROM CameraData h
INNER JOIN Camera i ON h.ID_Camera = i.ID_Camera
WHERE i.DeltaAngle != ''
END
This is very much a stab in the dark, as your sample SQL isn't at all representative of the data your describe in your question, however, maybe something like:
CREATE TRIGGER Delta_Angle ON CameraData
AFTER INSERT AS
BEGIN
SET NOCOUNT ON;
UPDATE C
SET Angle = C.Angle + i.delta
FROM Camera
JOIN inserted i ON C.CameraID = i.CameraID;
END
Notice that I refer to inserted; your trigger wasn't. Also, I'm not sure about your clause i.DeltaAngle != ''. Considering that DeltaAngle appears to be an int, it can never have a value of '' (however, '' would be implicitly converted to the value 0).
If this doesn't help, I (again) suggest you read Sean's link and update your post accordingly.

Manipulating the list of data from stored procedure to be used in another stored procedure

Currently I'm writing a stored procedureusing T-SQL in SQL Server. My script contains code to run another stored procedure to get list of data from a table. I want to manipulate the data, using the list of data to modify them for another purpose (e.g. summing up a column and adding more lists of data) from the stored procedure. A way that I know is to create a temporary table. But after that, I'm not so sure. Please help. thanks.
This is my code:
ALTER PROCEDURE [dbo].[AJU_Rpt_ARAgingSp]
(#Slsman_Starting slsmantype = NULL,
#Slsman_Ending slsmantype = NULL,
#Custnum_Starting custnumtype = NULL,
#Custnum_Ending custnumtype = NULL,
#CustType endusertypetype = NULL,
#CutOff_Date datetype = NULL,
#SumToCorp ListYesNoType = NULL, -- >> 0 = individual, 1 = corp customer
#ShowActive ListYesNoType = NULL, -- >> 0 = all trx, 1 = active only
#TransDomCurr ListYesNoType = NULL, -- >> 0 = dont convert, 1 = convert to local currency
#AgingBasis ArAgeByType = NULL, -->> i = invoice date, d = due date
#LeftToRight ListYesNoType = NULL, -- >> 0 = right to left, 1 = left to right
#CurrSite NVARCHAR(8),
#ShowDetailInfo NVARCHAR(1) = NULL)
AS
BEGIN
SET NOCOUNT ON
IF ISNULL(#CurrSite ,'') = ''
SET #CurrSite = (SELECT TOP 1 site_ref FROM parms_mst)
DECLARE #v_StartDate DateType
SET #Slsman_Starting = ISNULL(#Slsman_Starting, dbo.LowCharacter())
SET #Slsman_Ending = ISNULL(#Slsman_Ending, dbo.HighCharacter())
SET #Custnum_Starting = ISNULL(#Custnum_Starting, dbo.LowCharacter())
SET #Custnum_Ending = ISNULL(#Custnum_Ending, dbo.HighCharacter())
SET #v_StartDate = dbo.LowDate()
SET #CutOff_Date = GETDATE()
EXEC dbo.ApplyDateOffsetSp #v_StartDate OUT, NULL, 0
EXEC AJU_Rpt_DebtorSp
#CustNumStart = #Custnum_Starting
,#CustNumEnd = #Custnum_Ending
,#DistDateStart = #v_StartDate
,#DistDateEnd = #CutOff_Date
,#CurrCodeStart = NULL
,#CurrCodeEnd = NULL
,#SlsmanStart = #Slsman_Starting
,#SlsmanEnd = #Slsman_Ending
,#TerritoryStart = NULL
,#TerritoryEnd = NULL
,#CustTypeStart = NULL
,#CustTypeEnd = NULL
,#SiteGroup = #CurrSite
,#ConsolidatePayment = NULL
,#DisplayResult = 1
END
Since the initial result table is temporarily needed, you could do as follows:
In the invoking procedure, create a table variable with identical structure as returned by the invoked procedure,
Write within the invoking procedure a statement like the following:
INSERT INTO #_Tempo_Table
EXEC Invoked_Procedure (<params>) ;
Within the invoked procedure issue a SELECT that will return the records set.
If, in the other hand, you need to pass the initial table to the invoked procedure:
Create a type with a structure identical to the table that needs to be shared,
Add to the invoked procedure an argument of the type you just created (must be declared as READONLY),
Once you have the table with data within the invoking procedure, make the call to the invoked procedure passing the table variable as argument.
This methods will render the best performance (here I'm assuming that you are not passing a table with millions of records; if you do, it will still be the fastest way though you might need a lot of memory).

Insert not working using MERGE in SQL server

I have a stored proc with the below query to insert/update using a MERGE in SQL Server but the query works fine for update, but its not working for Insert.
Although I gets correct updated records in Target but for new inserts, it fails.
Basically, i have 4 tables.SUPPORT_STAFF_BAK is the target table which needs to be updated from source table UNIQUE_DUP_TEST based on few conditions from other two tables(REF_FUNCTION,DATA_PERIOD) which i tried to fulfill using joins.
Based on the conditions, we need to check in target, if the same ggid exists for current data_period we need to update it else we need to insert new record again, based on the condition.
MERGE SUPPORT_STAFF_BAK AS SUPP_STAFF
USING
(SELECT G_UNIQUE.[GLOBAL_ID],
G_UNIQUE.[FIRST_NAME],
G_UNIQUE.[LAST_NAME],
G_UNIQUE.[EMAIL],
G_UNIQUE.[Gender],
G_UNIQUE.[DATE_OF_BIRTH],
G_UNIQUE.[PRODUCTION_UNIT_CODE],
ORG.[LEGAL_ENTITY_COUNTRY_CODE],
ORG.[LEGAL_ENTITY_COUNTRY],
G_UNIQUE.[JOB_NAME],
ORG.[BU_CODE],
ORG.[BU_NAME],
ORG.[SBU_CODE],
ORG.[SBU_NAME],
G_UNIQUE.[GRADE_LETTER],
CASE
WHEN G_UNIQUE.[EMPLOYEE_STATUS] = 'A' THEN 'Active'
WHEN G_UNIQUE.[EMPLOYEE_STATUS] = 'S' THEN 'Suspended'
WHEN G_UNIQUE.[EMPLOYEE_STATUS]= 'T' THEN 'Terminated'
END AS [EMPLOYEE_STATUS],
CASE WHEN G_UNIQUE.[CATEGORY] = 'DSS' THEN G_UNIQUE.[CATEGORY_DETAIL] ELSE ''
END AS [CATEGORY],
G_UNIQUE.[CATEGORY_DETAIL],
G_UNIQUE.[FIRST_JOINING_DATE],
PERIOD.DATA_PERIOD_ID
FROM UNIQUE_DUP_TEST G_UNIQUE
INNER JOIN GDH_ORG ORG
ON G_UNIQUE.PRODUCTION_UNIT_CODE=ORG.PRODUCTION_UNIT_CODE
INNER JOIN REF_FUNCTION FUNC
ON G_UNIQUE.CATEGORY_DETAIL=FUNC.FUNCTION_CODE
INNER JOIN DATA_PERIOD PERIOD
ON FUNC.FUNCTION_ID=PERIOD.FUNCTION_ID
WHERE PERIOD.DATA_YEAR=YEAR(GETDATE()) AND PERIOD.DATA_MONTH=MONTH(GETDATE())
) AS G_SOURCE
ON SUPP_STAFF.GGID = G_SOURCE.GLOBAL_ID AND SUPP_STAFF.PRODUCTION_UNIT_CODE=G_SOURCE.PRODUCTION_UNIT_CODE
AND SUPP_STAFF.DATA_PERIOD_ID=G_SOURCE.DATA_PERIOD_ID
WHEN MATCHED THEN
UPDATE SET
[SUPP_STAFF].[FIRST_NAME] = G_SOURCE.[FIRST_NAME],
[SUPP_STAFF].[LAST_NAME] = G_SOURCE.[LAST_NAME],
[SUPP_STAFF].[EMAIL] = G_SOURCE.[EMAIL],
[SUPP_STAFF].[GENDER] = G_SOURCE.[Gender],
[SUPP_STAFF].[DATE_OF_BIRTH] = G_SOURCE.[DATE_OF_BIRTH],
[SUPP_STAFF].[LEGAL_ENTITY_COUNTRY_CODE] = G_SOURCE.[LEGAL_ENTITY_COUNTRY_CODE],
[SUPP_STAFF].[LEGAL_ENTITY_COUNTRY_NAME] = G_SOURCE.[LEGAL_ENTITY_COUNTRY],
[SUPP_STAFF].[GCM_ROLE] = G_SOURCE.[JOB_NAME],
[SUPP_STAFF].[BU_CODE] = G_SOURCE.[BU_CODE],
[SUPP_STAFF].[BU_NAME] = G_SOURCE.[BU_NAME],
[SUPP_STAFF].[SBU_CODE] = G_SOURCE.[SBU_CODE],
[SUPP_STAFF].[SBU_NAME] = G_SOURCE.[SBU_NAME],
[SUPP_STAFF].[GRADE] = G_SOURCE.[GRADE_LETTER],
[SUPP_STAFF].[EMPLOYEE_STATUS] = G_SOURCE.[EMPLOYEE_STATUS],
[SUPP_STAFF].[EMPLOYEE_CATEGORY] = G_SOURCE.[CATEGORY],
[SUPP_STAFF].[START_DATE] = G_SOURCE.[FIRST_JOINING_DATE],
[SUPP_STAFF].[UPDATE_DATE] = GETDATE(),
[SUPP_STAFF].[UPDATE_USER] = CASE WHEN G_SOURCE.[EMPLOYEE_STATUS]='Terminated' THEN 'Delete'
WHEN G_SOURCE.[EMPLOYEE_STATUS]<>'Terminated' THEN 'Update'
END,
[SUPP_STAFF].[SUPPORT_STAFF_FUNCTION] = CASE WHEN G_SOURCE.[EMPLOYEE_STATUS]='Terminated' THEN NULL
WHEN G_SOURCE.[EMPLOYEE_STATUS]<>'Terminated' THEN G_SOURCE.[CATEGORY_DETAIL]
END
WHEN NOT MATCHED AND G_SOURCE.[CATEGORY] = 'CC1'
AND G_SOURCE.[EMPLOYEE_STATUS] IN ('A, S')
THEN
INSERT( [GGID],
[FIRST_NAME],
[LAST_NAME],
[EMAIL],
[GENDER],
[DATE_OF_BIRTH],
[LEGAL_ENTITY_COUNTRY_CODE],
[LEGAL_ENTITY_COUNTRY_NAME],
[GCM_ROLE],
[BU_CODE],
[BU_NAME],
[SBU_CODE],
[SBU_NAME],
[GRADE],
[EMPLOYEE_STATUS],
[EMPLOYEE_CATEGORY],
[START_DATE],
[UPDATE_DATE],
[UPDATE_USER],
[SUPPORT_STAFF_FUNCTION]
)
VALUES (
G_SOURCE.[GLOBAL_ID],
G_SOURCE.[FIRST_NAME],
G_SOURCE.[LAST_NAME],
G_SOURCE.[EMAIL],
G_SOURCE.[Gender],
G_SOURCE.[DATE_OF_BIRTH],
G_SOURCE.[LEGAL_ENTITY_COUNTRY_CODE],
G_SOURCE.[LEGAL_ENTITY_COUNTRY],
G_SOURCE.[JOB_NAME],
G_SOURCE.[BU_CODE],
G_SOURCE.[BU_NAME],
G_SOURCE.[SBU_CODE],
G_SOURCE.[SBU_NAME],
G_SOURCE.[GRADE_LETTER],
G_SOURCE.[EMPLOYEE_STATUS],
G_SOURCE.[CATEGORY_DETAIL],
G_SOURCE.[FIRST_JOINING_DATE],
GETDATE(),
'Insert',
G_SOURCE.[CATEGORY_DETAIL]
)
OUTPUT $action,
INSERTED.GGID AS GGID;
SELECT ##ROWCOUNT;
One of your assumptions is wrong. Either the source query has less rows than you think, or there is a match, or the insert condition is not met. Otherwise the query is OK.
To debug this I'd insert the source query into a temp table and manually inspect its contents to make sure they are what you expect.
You can then join to the target to see if your inserts maybe are converted to updates (e.g. select * from Source join Target on ...). Internally, a MERGE is just a full outer join anyway and you can reproduce that manually.
Right now nobody can tell you the exact answer. You need to debug this yourself and examine your data.
Finally I found the error. The error was at the below 2 places -
CASE WHEN G_UNIQUE.[CATEGORY] = 'DSS' THEN G_UNIQUE.[CATEGORY_DETAIL] ELSE '' END AS [CATEGORY],
I replaced it with
CASE WHEN G_UNIQUE.[CATEGORY] = 'DSS' THEN G_UNIQUE.[CATEGORY_DETAIL] ELSE ''
END AS [EMPLOYEE_FUNCTION],
Also I included one more column in my Source query which was missing-
G_UNIQUE.[CATEGORY],
Also, there below wrong code
WHEN NOT MATCHED AND G_SOURCE.[CATEGORY] = 'CC1'
AND G_SOURCE.[EMPLOYEE_STATUS] IN ('A, S')
was replaced by the below correct code-
WHEN NOT MATCHED AND G_SOURCE.[CATEGORY] = 'CC1'
AND G_SOURCE.[EMPLOYEE_STATUS] IN ('Active', 'Suspended')
Actually, I was missing 1 source column and was checking the value for the same while inserting and hence the insert was failing.
Also,in the source for Employee_status i checked the values as A,S and T and then replaced them with Active,Suspended,Terminated but while inserting in the When not matched , i was checking the value for A,S,T which every time was returning false and hence insert failed.

For loop cursor in teradata

In my Teradata Stored Procedure, I want to have a for loop cursor against a dynamic sql.
Below is the code snippet
SET get_exclude_condition = '';
SET colum_id = 'SELECT MIN (parent_criteria_id) ,MAX (parent_criteria_id) FROM arc_mdm_tbls.intnl_mtch_criteria WHERE act_ind = 1 AND criteria_typ = ''Exclude'' AND mtch_technique_id ='||mtch_technique_id||';' ;
PREPARE input_stmt FROM colum_id;
OPEN flex_cursor;
FETCH flex_cursor INTO parent_criteria_id_min , parent_criteria_id_max ;
CLOSE flex_cursor;
SET get_exclude_condition = '';
WHILE (parent_criteria_id_min <= parent_criteria_id_max)
DO
SET get_exclude_condition = get_exclude_condition || '( ';
SET for_loop_stmt = 'SELECT criteria FROM arc_mdm_tbls.intnl_mtch_criteria WHERE act_ind = 1 AND mtch_technique_id ='||mtch_technique_id||' AND criteria_typ= ''Exclude'' AND parent_criteria_id ='||parent_criteria_id_min||';';
FOR for_loop_rule AS c_cursor_rule CURSOR FOR
for_loop_stmt
DO
Can I declare a for loop cursor like this ?
Or do I need to have something like this only ?
FOR for_loop_rule AS c_cursor_rule CURSOR FOR
SELECT rule_id
FROM arc_stage_tbls.assmt_scoring_rules
WHERE rule_typ = :v_RuleType
ORDER BY rule_id
DO
I mean can I first frame the dynamic sql and then have a for loop cursor on top of that or with the cursor declaration only I need to have a static sql query ?
Please clarify.
While you haven't posted everything that the stored procedure is trying to accomplish, it does appear that what you are asking can be accomplished using SET based logic and not looping through a cursor. If you need to parameterize the 'mtch_technique_id' you can use a Teradata macro which will allow you to maintain a SET based approach.
Here is the SQL for creating a macro that returns a result set based on my interpretation of what your snippet of the stored procedure is trying to accomplish:
REPLACE MACRO {MyDB}.Intnl_Mtch_Criteria(mtch_technique_id INTEGER) AS
(
SELECT criteria
FROM arc_mdm_tbls.intnl_mtch_criteria
WHERE act_ind = 1
AND (much_technique_id, criteria_typ) IN
(SELECT MIN((parent_criteria_id), MAX (parent_criteria_id)
FROM arc_mdm_tbls.intnl_mtch_criteria
WHERE act_ind = 1
AND criteria_typ = 'Exclude'
AND mtch_technique_id = :mtch_technique_id;
);

Resources