Insert not working using MERGE in SQL server - 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.

Related

Update multiple tables with one query

Record to update
select *
from Event_Measurable em
join Observation_Measurable om on em.Event_GUID = om.Event_GUID
where observation_guid in (8786975, 285886, 85976, 786976)
Update these records as follows:
set observation_value_text = '.',
observation_value_numeric = NULL,
om.status = 'D',
em.status = 'D',
I need help to update and set this values from the results of the query above. Can any help me. I have tried to update the table but it failed.
Try this:
update X set
observation_value_text = '.'
, observation_value_numeric = NULL
, om.status = 'D'
, em.status = 'D'
from
(
select
*
from Event_Measurable em
join Observation_Measurable om on em.Event_GUID = om.Event_GUID
where observation_guid in (8786975, 285886, 85976, 786976)
) X
Let me know if it works?
Your set list references multiple tables: that is invalid as only one table can be updated with one statement. You need to run 2 update statements so you either repeat the join in the second update or first put the result in a temp table and join that to the actual to-be-updated table. You might want to put the whole thing to a transaction to be able to roll back on error. See this reference.

How to implement incremental load using insert/update instead of merge in stored procedure?

I have implemented incremental load using merge, but I want using insert/update. Please help.
This is the stored procedure for the merge method. The stored procedure below loads data from source only when there are new records inserted, else it will discard. Updates only when there are updates in the record.
ALTER PROCEDURE [dbo].[LOAD_DIM_ADDRESS]
AS
BEGIN
MERGE [VINCE_RETAIL_TEST].[dbo].[DIM_ADDRESS] AS T
USING (SELECT
'1' as [COMPANY_KEY],
C.[CUSTOMER_KEY],
A.[ADDRESS_ID], A.[ADDRESS_TYPE],
'0' as [REGION_KEY],
'-1' as [COUNTRY_KEY],
'-1' as [STATE_KEY],
A.[CITY], A.[POSTAL_CODE]
FROM
[AX_STAGING].[dbo].[DIM_ADDRESS] A
INNER JOIN
[VINCE_RETAIL_TEST].[dbo].[DIM_CUSTOMER] C ON C.[CUSTOMER_ID] = A.[CUSTOMER_ID]
AND C.[ADDRESS_ID] = A.[ADDRESS_ID]) AS S
ON (T.[ADDRESS_ID] = S.[ADDRESS_ID] AND T.[ADDRESS_TYPE]=S.[ADDRESS_TYPE])
WHEN MATCHED
THEN
UPDATE
SET
T.[COMPANY_KEY] = S.[COMPANY_KEY],
T.[CUSTOMER_KEY] = S.[CUSTOMER_KEY],
T.[ADDRESS_ID] = S.[ADDRESS_ID],
T.[ADDRESS_TYPE] = S.[ADDRESS_TYPE],
T.[REGION_KEY] = S.[REGION_KEY],
T.[COUNTRY_KEY] = S.[COUNTRY_KEY],
T.[STATE_KEY] = S.[STATE_KEY],
T.[CITY] = S.[CITY],
T.[POSTAL_CODE] = S.[POSTAL_CODE]
WHEN NOT MATCHED BY TARGET
THEN
INSERT ([COMPANY_KEY], [CUSTOMER_KEY], [ADDRESS_ID], [ADDRESS_TYPE],
[REGION_KEY], [COUNTRY_KEY], [STATE_KEY], [CITY],
[POSTAL_CODE])
VALUES (S.[COMPANY_KEY], S.[CUSTOMER_KEY], S.[ADDRESS_ID], S.[ADDRESS_TYPE],
S.[REGION_KEY], S.[COUNTRY_KEY], S.[STATE_KEY], S.[CITY],
S.[POSTAL_CODE]);
END
Thanks in advance
You need to differentiate when to use INSERT, when to use UPDATE. So for example, only when (S.[ADDRESS_ID], S.[ADDRESS_TYPE]) does not exist in the target table, then do the INSERT. The code is listed. Similarly you can write the UPDATE part as a practice. :)
INSERT [VINCE_RETAIL_TEST].[dbo].[DIM_ADDRESS]
([COMPANY_KEY], [CUSTOMER_KEY], [ADDRESS_ID], [ADDRESS_TYPE],
[REGION_KEY], [COUNTRY_KEY], [STATE_KEY], [CITY],
[POSTAL_CODE])
SELECT S.[COMPANY_KEY], S.[CUSTOMER_KEY], S.[ADDRESS_ID], S.[ADDRESS_TYPE],
S.[REGION_KEY], S.[COUNTRY_KEY], S.[STATE_KEY], S.[CITY],
S.[POSTAL_CODE]
FROM (SELECT
'1' as [COMPANY_KEY],
C.[CUSTOMER_KEY],
A.[ADDRESS_ID], A.[ADDRESS_TYPE],
'0' as [REGION_KEY],
'-1' as [COUNTRY_KEY],
'-1' as [STATE_KEY],
A.[CITY], A.[POSTAL_CODE]
FROM
[AX_STAGING].[dbo].[DIM_ADDRESS] A
INNER JOIN
[VINCE_RETAIL_TEST].[dbo].[DIM_CUSTOMER] C ON C.[CUSTOMER_ID] = A.[CUSTOMER_ID]
AND C.[ADDRESS_ID] = A.[ADDRESS_ID]) AS S
WHERE (S.[ADDRESS_ID], S.[ADDRESS_TYPE]) NOT IN (SELECT [ADDRESS_ID],[ADDRESS_TYPE] FROM [VINCE_RETAIL_TEST].[dbo].[DIM_ADDRESS])

Writing the SQL CASE statement to update a column based on the value of another column from another table

I need to update the values in a Column named "TravelAgencyID2" in "dbo.ReservationStay" based on the values of column "TravelAgencyTypeCode" of "dbo.TravelAgency".
The condition of the update is like this: If TravelAgencyTypeCode in dbo.TravelAgency is NOT EQUAL to 'DMC', then TravelAgencyID2 = TravelAgencyID (from dbo.ReservationStay), ELSE TravelAgencyID2 remains unchanged.
SO, you only need to update those rows WHERE a condition is met?
UPDATE rs
SET rs.TravelAgencyID2 = rs.TravelAgencyID
FROM ReservationStay rs
INNER JOIN TravelAgency ta on rs.TravelAgencyID2 = ta.ID
WHERE ta.TravelAgencyTypeCode != 'DMC'
This will only update the rows you need, without touching anything else.

Query with integers not working

I've been searching here on stackoverflow and other sources but not found a solution to this
The query below works as expected expect for when either custinfo.cust_cntct_id or custinfo.cust_corrcntct_id = '' (blank not NULL) then I get no results. Both are integer fields and if both have an integer value then I get results. I still want a value returned for either cntct_email or corrcntct_email even if custinfo.cust_cntct_id or custinfo.cust_corrcntct_id = blank
Can someone help me out in making this work? The database is PostgreSQL.
SELECT
cntct.cntct_email AS cntct_email,
corrcntct.cntct_email AS corrcntct_email
FROM
public.custinfo,
public.invchead,
public.cntct,
public.cntct corrcntct
WHERE
invchead.invchead_cust_id = custinfo.cust_id AND
cntct.cntct_id = custinfo.cust_cntct_id AND
corrcntct.cntct_id = custinfo.cust_corrcntct_id;
PostgreSQL won't actually let you test an integer field for a blank value (unless you're using a truly ancient version - 8.2 or older), so you must be using a query generator that's "helpfully" transforming '' to NULL or a tool that's ignoring errors.
Observe this, on Pg 9.2:
regress=> CREATE TABLE test ( a integer );
CREATE TABLE
regress=> insert into test (a) values (1),(2),(3);
INSERT 0 3
regress=> SELECT a FROM test WHERE a = '';
ERROR: invalid input syntax for integer: ""
LINE 1: SELECT a FROM test WHERE a = '';
If you are attempting to test for = NULL, this is not correct. You must use IS NOT NULL or IS DISTINCT FROM NULL instead. Testing for = NULL always results in NULL, which is treated as false in a WHERE clause.
Example:
regress=> insert into test (a) values (null);
INSERT 0 1
regress=> SELECT a FROM test WHERE a = NULL;
a
---
(0 rows)
regress=> SELECT a FROM test WHERE a IS NULL;
a
---
(1 row)
regress=> SELECT NULL = NULL as wrong, NULL IS NULL AS right;
wrong | right
-------+-------
| t
(1 row)
By the way, you should really be using ANSI JOIN syntax. It's more readable and it's much easier to forget to put a condition in and get a cartesian product by accident. I'd rewrite your query for identical functionality and performance but better readability as:
SELECT
cntct.cntct_email AS cntct_email,
corrcntct.cntct_email AS corrcntct_email
FROM
public.custinfo ci
INNER JOIN public.invchead
ON (invchead.invchead_cust_id = ci.cust_id)
INNER JOIN public.cntct
ON (cntct.cntct_id = ci.cust_cntct_id)
INNER JOIN public.cntct corrcntct
ON (corrcntct.cntct_id = ci.cust_corrcntct_id);
Use of table aliases usually keeps it cleaner; here I've aliased the longer name custinfo to ci for brevity.

Oracle Triggers Update at Another Table

I am trying to create a trigger in Oracle. I know sql but i have never created trigger before. I have this code:
create or replace trigger "PASSENGER_BOOKING_T1"
AFTER
insert on "PASSENGER_BOOKING"
for each row
begin
IF (:NEW.CLASS_TYPE == 'ECO')
SELECT F.AVL_SEATS_ECOCLASS,F.FLIGHT_ID INTO SEAT, FLIGHT_INFO
FROM BOOKING B, JOURNEY_FLIGHT J, FLIGHT F
WHERE B.JOURNEY_ID = J.JOURNEY_ID and F.FLIGHT_ID = J.FLIGHT_ID;
UPDATE FLIGHT
SET AVL_SEATS_ECOCLASS = (SEAT-1)
WHERE FLIGHT_ID = FLIGHT_INFO;
END IF;
end;​
This trigger fires when there is an insert in Passenger_Booking table. And seating capacity is reduced by one (which is at different table).
Select query should be alright but there is something wrong in somewhere.
Could anyone suggest anything?
I changed the body part to this but still having issues:
UPDATE FLIGHT
SET AVL_SEATS_ECOCLASS =
(SELECT F.AVL_SEATS_ECOCLASS FROM BOOKING B, JOURNEY_FLIGHT J, FLIGHT F WHERE B.JOURNEY_ID = J.JOURNEY_ID and F.FLIGHT_ID = J.FLIGHT_ID;
);
An IF statement needs a THEN
In PL/SQL, you use an = to test for equality, not ==
You need to declare the variables that you are selecting into
When I do those three things, I get something like this
create or replace trigger PASSENGER_BOOKING_T1
AFTER insert on PASSENGER_BOOKING
for each row
declare
l_seat flight.seat%type;
l_flight_id flight.flight_id%type;
begin
IF (:NEW.CLASS_TYPE = 'ECO')
THEN
SELECT F.AVL_SEATS_ECOCLASS,F.FLIGHT_ID
INTO l_seat, l_flight_id
FROM BOOKING B,
JOURNEY_FLIGHT J,
FLIGHT F
WHERE B.JOURNEY_ID = J.JOURNEY_ID
and F.FLIGHT_ID = J.FLIGHT_ID;
UPDATE FLIGHT
SET AVL_SEATS_ECOCLASS = (l_seat-1)
WHERE FLIGHT_ID = l_flight_id;
END IF;
end;​
Beyond those syntax errors, I would be shocked if the SELECT INTO statement was correct. A SELECT INTO must return exactly 1 row. Your query should almost certainly return multiple rows since there are no predicates that would restrict the query to a particular flight or a particular booking. Presumably, you want to join to one or more columns in the PASSENGER_BOOKING table.
Additionally, if this is something other than a homework assignment, make sure you understand that this sort of trigger does not work correctly in a multi-user environment.
just a wild guess
edit as Justin pointed out (thanks Justin) equality check
create or replace trigger "PASSENGER_BOOKING_T1"
AFTER
insert on "PASSENGER_BOOKING"
for each row
declare
v_flight_id FLIGHT.FLIGHT_ID%TYPE;
begin
IF (:NEW.CLASS_TYPE = 'ECO') THEN
SELECT F.ID into v_flight_id
FROM BOOKING B, JOURNEY_FLIGHT J, FLIGHT F
WHERE B.ID = :NEW.BOOKING_ID -- note that I've made this up
AND B.JOURNEY_ID = J.JOURNEY_ID AND F.FLIGHT_ID = J.FLIGHT_ID;
UPDATE FLIGHT
SET AVL_SEATS_ECOCLASS = (SEAT-1)
WHERE ID = v_flight_id;
END IF;
end;​

Resources