Related
Hope everyone is doing good.
I have started working on snowflake. I am trying to create a stored and procedure and calling this SP. Stored procedure is created without any issue. While calling it is saying that unexpected invalid token.
JavaScript compilation error: Uncaught Syntax issue: Invalid or unexpected token in USP_USERS at INSERT INTO stg.users_Temp position 0
CREATE OR REPLACE PROCEDURE ods.usp_Users()
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
var sql_command =
"INSERT INTO stg.users_Temp
SELECT
LTRIM(RTRIM(id)) AS Id
, LTRIM(RTRIM(name)) AS Name
, LTRIM(RTRIM(divisionId)) AS DivisionId
, LTRIM(RTRIM(divisionName)) AS DivisionName
, LTRIM(RTRIM(department)) AS Department
, LTRIM(RTRIM(email)) AS Email
, LTRIM(RTRIM(state)) AS State
, LTRIM(RTRIM(title)) AS Title
, LTRIM(RTRIM(username)) AS Username
, LTRIM(RTRIM(managerId)) AS ManagerId
, LTRIM(RTRIM(employeeId)) AS employeeId
, LTRIM(RTRIM(employeeType)) AS employeeType
, LTRIM(RTRIM(officialName)) AS officialName
, LTRIM(RTRIM(dateHire)) AS dateHire
, LTRIM(RTRIM(LocationId)) AS LocationId
FROM stg.users;
MERGE INTO
ods.Users AS t
USING
stg.users_Temp s
ON
(
s.Id = t.Id
)
WHEN
MATCHED
THEN
UPDATE
SET
t.Name = s.Name
,t.DivisionId = s.DivisionId
,t.DivisionName = s.DivisionName
,t.Department = s.Department
,t.Email = s.Email
,t.State = s.State
,t.Title = s.Title
,t.Username = s.Username
,t.LocationId = s.LocationId
,t.ManagerId = s.ManagerId
,t.employeeId = s.employeeId
,t.employeeType = s.employeeType
,t.officialName = s.officialName
,t.dateHire = s.dateHire
,t.EtlLastUpdatedDate = CURRENT_TIMESTAMP() :: TIMESTAMP
WHEN NOT MATCHED
THEN INSERT
(
id
,Name
,DivisionId
,DivisionName
,Department
,Email
,State
,Title
,Username
,LocationId
,ManagerId
,employeeId
,employeeType
,officialName
,dateHire
,CurrentRecord
,EtlCreatedDate
)
VALUES
(
s.id
,s.Name
,s.DivisionId
,s.DivisionName
,s.Department
,s.Email
,s.State
,s.Title
,s.Username
,s.LocationId
,s.ManagerId
,s.employeeId
,s.employeeType
,s.officialName
,s.dateHire
,'1'
,CURRENT_TIMESTAMP() :: TIMESTAMP
);"
try {
snowflake.execute (
{sqlText: sql_command}
);
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err; // Return a success/error indicator.
}
TRUNCATE TABLE stg.users_Temp;
$$
;
//call ods.usp_Users();
While call SP it is throwing an error
Could someone help me on this issue.
Regards,
Khatija
You need to use the backtick (`) character to define multi-line strings:
CREATE OR REPLACE PROCEDURE ods.usp_Users()
RETURNS VARCHAR
LANGUAGE JAVASCRIPT
AS
$$
var sql_command =
`BEGIN
INSERT INTO stg.users_Temp
SELECT
LTRIM(RTRIM(id)) AS Id
, LTRIM(RTRIM(name)) AS Name
, LTRIM(RTRIM(divisionId)) AS DivisionId
, LTRIM(RTRIM(divisionName)) AS DivisionName
, LTRIM(RTRIM(department)) AS Department
, LTRIM(RTRIM(email)) AS Email
, LTRIM(RTRIM(state)) AS State
, LTRIM(RTRIM(title)) AS Title
, LTRIM(RTRIM(username)) AS Username
, LTRIM(RTRIM(managerId)) AS ManagerId
, LTRIM(RTRIM(employeeId)) AS employeeId
, LTRIM(RTRIM(employeeType)) AS employeeType
, LTRIM(RTRIM(officialName)) AS officialName
, LTRIM(RTRIM(dateHire)) AS dateHire
, LTRIM(RTRIM(LocationId)) AS LocationId
FROM stg.users;
MERGE INTO
ods.Users AS t
USING
stg.users_Temp s
ON
(
s.Id = t.Id
)
WHEN
MATCHED
THEN
UPDATE
SET
t.Name = s.Name
,t.DivisionId = s.DivisionId
,t.DivisionName = s.DivisionName
,t.Department = s.Department
,t.Email = s.Email
,t.State = s.State
,t.Title = s.Title
,t.Username = s.Username
,t.LocationId = s.LocationId
,t.ManagerId = s.ManagerId
,t.employeeId = s.employeeId
,t.employeeType = s.employeeType
,t.officialName = s.officialName
,t.dateHire = s.dateHire
,t.EtlLastUpdatedDate = CURRENT_TIMESTAMP() :: TIMESTAMP
WHEN NOT MATCHED
THEN INSERT
(
id
,Name
,DivisionId
,DivisionName
,Department
,Email
,State
,Title
,Username
,LocationId
,ManagerId
,employeeId
,employeeType
,officialName
,dateHire
,CurrentRecord
,EtlCreatedDate
)
VALUES
(
s.id
,s.Name
,s.DivisionId
,s.DivisionName
,s.Department
,s.Email
,s.State
,s.Title
,s.Username
,s.LocationId
,s.ManagerId
,s.employeeId
,s.employeeType
,s.officialName
,s.dateHire
,'1'
,CURRENT_TIMESTAMP() :: TIMESTAMP
);
END`
try {
snowflake.execute (
{sqlText: sql_command}
);
return "Succeeded."; // Return a success/error indicator.
}
catch (err) {
return "Failed: " + err; // Return a success/error indicator.
}
// TRUNCATE TABLE stg.users_Temp;
$$
;
PS: You can't user trancate as you tried to do, you should also call it using snowflake.execute. And when you run this proc, it would fail with "Failed: Multiple SQL statements in a single API call are not supported; use one API call per statement instead.". To overcome this issue, you can break up queries, and execute them separately or surround them in a BEGIN/END block.
I have one small question. How to resolve this problem.
First, I have this code below and he give error Ora 01403 no data found. This code was debugged and I find when he give this error. This place was marked.
Second, my question is How I can modify this code?
Thirdly, how I can take only one row from the cursor? I know that command "fetch" store data for new row in variable with %rowtype
Fourthly, main task is need to store data from cursor in different tables with diffrent FK For this task was wroten this code. If anyone have Idea how to do this I wiil be greate to see solution or explanation how to resolve this "question". Thanks.
DECLARE
CURSOR c_data IS
SELECT td.*
FROM ddf td
WHERE td.col1 IS NOT NULL
AND td.col2 IS NOT NULL
AND td.col3 IS NOT NULL
AND td.col4 IS NOT NULL
AND td.col5 IS NOT NULL
AND td.col6 IS NOT NULL
AND td.col7 IS NOT NULL
OR td.col8 IS NOT NULL
OR td.col9 IS NOT NULL
OR td.col10 IS NOT NULL;
c_id NUMBER;
r_number NUMBER;
ed NUMBER;
nid NUMBER;
ssd NUMBER;
iiy number := :p13ns;
BEGIN
FOR i IN c_data LOOP
c_id := 0;
r_number := 0;
r_number := random_number();
c_id := get_cd(i.col5);
INSERT INTO rrree (
efn, eii, ec, edd, tei, eln, emn, ebd, eid, eiid
) VALUES (
i.col1, i.col8 || i.col9, c_id, to_date(sysdate, 'MM/DD/YYYY'),
CASE
WHEN i.col8 IS NOT NULL THEN (
SELECT tei FROM vre WHERE tei = 1 ) ELSE (
SELECT tei FROM vre WHERE tei = 2 )
END, i.col3, i.col2, to_date(i.col4, 'MM/DD/YYYY'), to_date(i.col6, 'MM/DD/YYYY'), to_date(i.col7, 'MM/DD/YYYY')
);
COMMIT;
/* Second call for this select get error Ora-01403 no data found. */
SELECT DISTINCT( dii ) INTO ed
FROM vrt
WHERE upper(efn) LIKE upper(i.col1)
AND upper(eln) LIKE upper(i.col3)
AND upper(emn) LIKE upper(i.col2)
AND ROWNUM = 1;
/* end Second call for this select get error Ora-01403 no data found. */
SELECT DISTINCT( isd ) INTO nid
FROM vri
WHERE isd = iiy
AND ROWNUM = 1;
INSERT INTO rrre (
cal_id, dii
) VALUES (
1, ed
);
COMMIT;
SELECT DISTINCT( sd ) INTO ssd
FROM rrre
WHERE dii = ed
AND ROWNUM = 1;
INSERT INTO rrrs (
sid, sssi, sns, sts, sids, va, cdc, dii, sd, disv, isd
) VALUES (
to_date(sysdate, 'MM/DD/YYYY'), nid, r_number, to_date(sysdate + 5, 'MM/DD/YYYY'), 1, 1, 1, ed, ssd, to_date(sysdate, 'MM/DD/YYYY'), nid
);
COMMIT;
END LOOP;
END;
I am getting this error when I attempt to create a stored procedure:
SQL Server Database Error: Incorrect syntax near the keyword 'PROC'.
When I execute the SQL between the lines, everything works. However, when I attempt to execute the stored procedure, I get the error.
I'm new to stored procedures, so I am hoping that it is just a misplaced ; or GO.
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF EXISTS (SELECT object_id FROM AppGovernmentPrograms.Sys.Objects
WHERE type = 'P'
AND name = 'usp_AppGovernmentPrograms_rptObjectives')
BEGIN
DROP PROCEDURE dbo.usp_AppGovernmentPrograms_rptObjectives
END
GO
CREATE PROC [dbo].[usp_AppGovernmentPrograms_rptObjectives]
#ProgramYear INT,
#SummaryLevel VARCHAR(50)
AS
SET NOCOUNT ON;
WITH measuresPHS AS
(
SELECT
mi.definition_id, mi.metric_name
FROM
StgEpicClarityPHS.dbo.metric_info mi WITH (nolock)
WHERE
mi.definition_id IN (90020350, 90020300, 90020301, 90020302,
90020303, 90020304, 90020305, 90020306,
90020307, 90020308, 90020309, 90020310,
90020311, 90020351, 90020352, 90020353,
90020354, 90020355, 90020400)
)
SELECT
provCID, NPI, TIN, year_dt,
SUM([n90020350]) as "1.1n",
SUM([d90020350]) as "1.1d",
SUM([n90020300]) as "2.1n",
SUM([d90020300]) as "2.1d",
SUM([n90020301]) as "4.1n",
SUM([d90020301]) as "4.1d",
SUM([n90020302]) as "4.2n",
SUM([d90020302]) as "4.2d",
SUM([n90020303]) as "4.3n",
SUM([d90020303]) as "4.3d",
SUM([n90020304]) as "5.1n",
SUM([d90020304]) as "5.1d",
SUM([n90020305]) as "5.2n",
SUM([d90020305]) as "5.2d",
SUM([n90020306]) as "6.1n",
sum([d90020306]) as "6.1d"
, sum([n90020307]) as "6.2n"
, sum([d90020307]) as "6.2d"
, sum([n90020308]) as "6.3n"
, sum([d90020308]) as "6.3d"
, sum([n90020309]) as "7.1n"
, sum([d90020309]) as "7.1d"
, sum([n90020310]) as "7.2n"
, sum([d90020310]) as "7.2d"
, sum([n90020311]) as "7.3n"
, sum([d90020311]) as "7.3d"
, sum([n90020351]) as "8.1n"
, sum([d90020351]) as "8.1d"
, sum([n90020352]) as "8.2n"
, sum([d90020352]) as "8.2d"
, sum([n90020353]) as "8.3n"
, sum([d90020353]) as "8.3d"
, sum([n90020354]) as "8.4n"
, sum([d90020354]) as "8.4d"
, sum([n90020355]) as "8.5n"
, sum([d90020355]) as "8.5d"
, sum([n90020400]) as "IAn"
, sum([d90020400]) as "IAd"
From
(
Select
sfi.prov_target_id as provCID
, prov2.npi
, cmi.facility_group_id as TIN
, sum_data.year_dt
, 'n' + cast(sfi.definition_id as varchar(15)) as id1 -- use for numerator
, 'd' + cast(sfi.definition_id as varchar(15)) as id2 -- use for denominator
, sum_data.numerator_year
, sum_data.denominator_year
from StgEpicClarityPHS.dbo.sum_facts_info sfi with(nolock)
inner join StgEpicClarityPHS.dbo.sum_facts_info_2 sfi2 with(nolock) on sfi2.sum_facts_id = sfi.sum_facts_id
inner join measuresPHS m with(nolock) on m.definition_id = sfi.definition_id
inner join StgEpicClarityPHS.dbo.yearly_data sum_data with(nolock) on sum_data.sum_facts_id = sfi.sum_facts_id
left outer join StgEpicClarityPHS.dbo.cms_mu_info cmi with(nolock) on cmi.cms_mu_id = sfi2.tin_target_id
left outer join StgEpicClarityPHS.dbo.clarity_ser_2 prov2 with(nolock) on sfi.prov_target_id = prov2.prov_id
Where sfi.record_type_c = 1 -- standard summary, not a benchmark
and (sum_data.year_dt = cast(concat('01/01/',:ProgramYear) as datetime)) -- (always use the start date of the reporting period)
and (
(:SummaryLevel = 'NT' and sfi.compld_sum_level = '4^73') -- NPI/TIN (i.e. individual MIPS EC)
or
(:SummaryLevel = 'T' and sfi.compld_sum_level = '73') -- TIN (i.e. Group)
or
(:SummaryLevel = 'N' and sfi.compld_sum_level = '4') -- NPI only
)
) nd
pivot
( sum(NUMERATOR_YEAR)
for id1 in ([n90020350], [n90020300], [n90020301], [n90020302], [n90020303], [n90020304], [n90020305], [n90020306], [n90020307], [n90020308], [n90020309], [n90020310], [n90020311], [n90020351], [n90020352], [n90020353], [n90020354], [n90020355], [n90020400])
) as p1
pivot
( sum(DENOMINATOR_YEAR)
for id2 in ([d90020350], [d90020300], [d90020301], [d90020302], [d90020303], [d90020304], [d90020305], [d90020306], [d90020307], [d90020308], [d90020309], [d90020310], [d90020311], [d90020351], [d90020352], [d90020353], [d90020354], [d90020355], [d90020400])
) as p2
Group By provCID, NPI, TIN, year_dt
--------------------------------------------------------------------------------------------------------------------------------------
;
GO
Your problem may simply be that your query contains a colon at
cast(concat('01/01/',:ProgramYear) as datetime)
in your where clause. It should probably be a #ProgramYear
Also :SummaryLevel should be #SummaryLevel
I'm working on migrating from an Oracle 9i db to SQL Server and trying to work out and translate the one trigger we have to maintain a history of records. The History table has its own ID column and all other columns are the same as in the Current table.
Here is my trigger as is from the OraDb: (in the OraDb we are using a sequence to increment the "History" PK)
CREATE OR REPLACE TRIGGER "RC_CURRENT_T"
BEFORE INSERT OR UPDATE
ON RC_CURRENT
FOR EACH ROW
DECLARE var_date VARCHAR2(30 BYTE);
BEGIN
IF UPDATING THEN
var_date:=TO_CHAR(CURRENT_DATE, 'MM/DD/YYYY HH24:MI:SS');
INSERT
INTO RC_HISTORY
(
"ID" ,
REC_ID ,
STATUS_DESCR ,
REQBY_FULLNAME ,
REQBY_USERNAME ,
REQBY_EMAIL ,
REQBY_EMPLID ,
PCARD_TYPE ,
BUS_SGMT_DESCR ,
REQ_TYPE ,
REQ_CHNGAUTHBY ,
OP_CNTR_NAME ,
CARDHOLDER_NAME ,
CUSTODIAN_NAME ,
ED_ROLLUP_NUM ,
CARDHOLDER_ADDRESS ,
CARDHOLDER_CITY ,
CARDHOLDER_STATE ,
CARDHOLDER_ZIP ,
CARD_NUMBER ,
GL_CODE ,
GL_LOCATION ,
GL_DEPARTMENT ,
GL_ACCOUNT ,
GL_SVCSCODE ,
MONTH_LIMIT ,
LIMIT_RESTRICTIONS ,
CARD_LIMIT ,
REASON_COMMENTS ,
CREATE_DATE ,
APPROVER1_NAME ,
APPROVER1_USERNAME ,
APPROVER1_EMAIL ,
APPROVER1_EMPLID ,
APPROVER1_DATE ,
APPROVER2_NAME ,
APPROVER2_USERNAME ,
APPROVER2_EMAIL ,
APPROVER2_EMPLID ,
APPROVER2_DATE ,
ADMIN_LVL1 ,
ADMIN_LVL2 ,
ADMIN_LVL3 ,
ADMIN_LVL4 ,
ADMIN_LVL5 ,
ADMIN_LVL6 ,
KEYED_DATE ,
KEYED_BY_NAME ,
KEYER_CHANGE_TYPE ,
KEYER_COMMENTS ,
CARD_STATUS ,
KEYER_USERNAME ,
KEYER_EMPLID ,
USER_FULLNAME,
OPER_STREET,
OPER_CITY,
OPER_STATE,
OPER_ZIP,
USER_PHONE,
USER_EMAIL,
USER_HYCHY_SETUP,
USER_ASSIGNED_USERID,
USER_PROCESS_DATE,
USER_DISTRIBUTION,
USER_REQUESTTYPE,
REC_ENTRY_DATE
)
VALUES
(
RC_HISTORY_SEQ.nextval ,
:OLD.REC_ID ,
:OLD.STATUS_DESCR ,
:OLD.REQBY_FULLNAME ,
:OLD.REQBY_USERNAME ,
:OLD.REQBY_EMAIL ,
:OLD.REQBY_EMPLID ,
:OLD.PCARD_TYPE ,
:OLD.BUS_SGMT_DESCR ,
:OLD.REQ_TYPE ,
:OLD.REQ_CHNGAUTHBY ,
:OLD.OP_CNTR_NAME ,
:OLD.CARDHOLDER_NAME ,
:OLD.CUSTODIAN_NAME ,
:OLD.ED_ROLLUP_NUM ,
:OLD.CARDHOLDER_ADDRESS ,
:OLD.CARDHOLDER_CITY ,
:OLD.CARDHOLDER_STATE ,
:OLD.CARDHOLDER_ZIP ,
:OLD.CARD_NUMBER ,
:OLD.GL_CODE ,
:OLD.GL_LOCATION ,
:OLD.GL_DEPARTMENT ,
:OLD.GL_ACCOUNT ,
:OLD.GL_SVCSCODE ,
:OLD.MONTH_LIMIT ,
:OLD.LIMIT_RESTRICTIONS ,
:OLD.CARD_LIMIT ,
:OLD.REASON_COMMENTS ,
:OLD.CREATE_DATE ,
:OLD.APPROVER1_NAME ,
:OLD.APPROVER1_USERNAME ,
:OLD.APPROVER1_EMAIL ,
:OLD.APPROVER1_EMPLID ,
:OLD.APPROVER1_DATE ,
:OLD.APPROVER2_NAME ,
:OLD.APPROVER2_USERNAME ,
:OLD.APPROVER2_EMAIL ,
:OLD.APPROVER2_EMPLID ,
:OLD.APPROVER2_DATE ,
:OLD.ADMIN_LVL1 ,
:OLD.ADMIN_LVL2 ,
:OLD.ADMIN_LVL3 ,
:OLD.ADMIN_LVL4 ,
:OLD.ADMIN_LVL5 ,
:OLD.ADMIN_LVL6 ,
:OLD.KEYED_DATE ,
:OLD.KEYED_BY_NAME ,
:OLD.KEYER_CHANGE_TYPE ,
:OLD.KEYER_COMMENTS ,
:OLD.CARD_STATUS ,
:OLD.KEYER_USERNAME ,
:OLD.KEYER_EMPLID ,
:OLD.USER_FULLNAME,
:OLD.OPER_STREET,
:OLD.OPER_CITY,
:OLD.OPER_STATE,
:OLD.OPER_ZIP,
:OLD.USER_PHONE,
:OLD.USER_EMAIL,
:OLD.USER_HYCHY_SETUP,
:OLD.USER_ASSIGNED_USERID,
:OLD.USER_PROCESS_DATE,
:OLD.USER_DISTRIBUTION,
:OLD.USER_REQUESTTYPE,
var_date
) ;
END IF;
END;
I have attempted the follow but get the error message
The select list for the INSERT statement contains fewer items than the
insert list. The number of SELECT values must match the number of INSERT
columns.
Code:
CREATE TRIGGER RC_CURRENT_T
ON [dbo].[RC_CURRENT]
FOR UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[RC_HISTORY]
([REC_ID]
,[STATUS_DESCR]
,[REQBY_FULLNAME]
,[REQBY_USERNAME]
,[REQBY_EMAIL]
,[REQBY_EMPLID]
,[PCARD_TYPE]
,[BUS_SGMT_DESCR]
,[REQ_TYPE]
,[REQ_CHNGAUTHBY]
,[OP_CNTR_NAME]
,[CARDHOLDER_NAME]
,[CUSTODIAN_NAME]
,[ED_ROLLUP_NUM]
,[CARDHOLDER_ADDRESS]
,[CARDHOLDER_CITY]
,[CARDHOLDER_STATE]
,[CARDHOLDER_ZIP]
,[CARD_NUMBER]
,[GL_CODE]
,[GL_LOCATION]
,[GL_DEPARTMENT]
,[GL_ACCOUNT]
,[GL_SVCSCODE]
,[MONTH_LIMIT]
,[LIMIT_RESTRICTIONS]
,[CARD_LIMIT]
,[REASON_COMMENTS]
,[CREATE_DATE]
,[APPROVER1_NAME]
,[APPROVER1_USERNAME]
,[APPROVER1_EMAIL]
,[APPROVER1_EMPLID]
,[APPROVER1_DATE]
,[APPROVER2_NAME]
,[APPROVER2_USERNAME]
,[APPROVER2_EMAIL]
,[APPROVER2_EMPLID]
,[APPROVER2_DATE]
,[ADMIN_LVL1]
,[ADMIN_LVL2]
,[ADMIN_LVL3]
,[ADMIN_LVL4]
,[ADMIN_LVL5]
,[ADMIN_LVL6]
,[KEYED_DATE]
,[KEYED_BY_NAME]
,[KEYER_CHANGE_TYPE]
,[KEYER_COMMENTS]
,[CARD_STATUS]
,[KEYER_USERNAME]
,[KEYER_EMPLID]
,[USER_FULLNAME]
,[OPER_STREET]
,[OPER_CITY]
,[OPER_STATE]
,[OPER_ZIP]
,[USER_PHONE]
,[USER_EMAIL]
,[USER_HYCHY_SETUP]
,[USER_ASSIGNED_USERID]
,[USER_PROCESS_DATE]
,[USER_DISTRIBUTION]
,[USER_REQUESTTYPE]
,[REC_ENTRY_DATE]
)
SELECT * FROM RC_CURRENT
END
GO
I need to maintain the separate ID in the history table for reporting purposes.
Any assistance is very appreciated.
Thank you.
Looking at the old trigger (and the error message)RC_HISTORY has 1 extra field compared to RC_CURRENT so you have two options.
If [REC_ID] is an identity column with auto increment you can simply remove it from the list of columns to be inserted to
INSERT INTO [dbo].[RC_HISTORY]
([STATUS_DESCR]
,[REQBY_FULLNAME]
,[REQBY_USERNAME]
...
If [REC_ID] is not set to auto increment then you will have to provide a value to insert into the column
SELECT (SELECT MAX([REC_ID])+1 FROM RC_HISTORY), * FROM RC_CURRENT
I would suggest you try the first option and set the column to be an identity column if it isn't already, I also suggest you also use named columns instead of * for the select values.
I'm trying to write a procedure to sync users from Active directory into my local application database. From my code, I'm passing XML in the following format to the stored procedure:
<AdUsers>
<AdUser AccountSid="S-1-5-21-111111111-111111111-111111111-1111" DisplayName="Test User" EmailAddress="tuser#mail.local" ExchangeServerFk="4" ExchangeServer="https://mail.local" Department="" StatusFK="1" UserName="TUSER">
<AccountSids>
<Sid>S-1-5-21-111111111-111111111-111111111-1111</Sid>
</AccountSids>
</AdUser>
</AdUsers>
I'd like to do a sync between the XML and the rows in my tb_Mailboxes table with the following Stored Procedure:
#adUsers XML, #lastSyncBy VARCHAR (50), #lastSyncOn DATETIME, #defaultProfileId INT, #adDomainId INT
AS
begin try
BEGIN TRANSACTION
--First delete all the mailboxes exist in the database but not in the xml.
delete tb_Mailboxes
where AccountSid not in (
select
rtrim(element.value('text()[1]', 'varchar(100)')) as AccountSid
from
#adUsers.nodes('/AdUsers/AdUser/AccountSids/Sid') t(element)
) AND #adDomainId = AdDomainFk
--Then insert or update existing accounts
MERGE tb_Mailboxes as [target]
USING
(
select
rtrim(element.value('data(#AccountSid)', 'varchar(100)')) as AccountSid
,rtrim(element.value('data(#DisplayName)', 'varchar(100)')) as DisplayName
,rtrim(element.value('data(#EmailAddress)', 'varchar(500)')) as EmailAddress
,rtrim(element.value('data(#ExchangeServerFk)', 'varchar(100)')) as ExchangeServerFk
,rtrim(element.value('data(#ExchangeServer)', 'varchar(150)')) as ExchangeServer
,rtrim(element.value('data(#Department)', 'varchar(100)')) as Department
,rtrim(element.value('data(#StatusFK)', 'varchar(100)')) as StatusFK
,rtrim(element.value('data(#UserName)', 'varchar(100)')) as UserName
,element.query('AccountSids') as SidList
from
#adUsers.nodes('/AdUsers/AdUser') t(element)
) as [source]
on [target].AccountSid IN
(
SELECT rtrim(A.value('text()[1]', 'varchar(100)')) as CurSid
FROM [source].SidList.nodes('Sid') AS FN(A)
)
WHEN MATCHED THEN UPDATE SET
DisplayName = [source].DisplayName
,EmailAddress = [source].EmailAddress
,ExchangeServerFk = [source].ExchangeServerFk
,ExchangeServer = [source].ExchangeServer
,Department = [source].Department
,UserName = [source].UserName
/*,StatusFK = [source].StatusFK*/
,LastSyncOn = #lastSyncOn
,LastSyncBy = #lastSyncBy
WHEN NOT MATCHED THEN INSERT
(
AdDomainFk,
UserName,
DisplayName,
Department,
EmailAddress,
ExchangeServerFk,
ExchangeServer,
AccountSid,
IsAutoDeleteEnabled,
ProfileFk,
Settings,
QueueLastPickedUp,
QueueLastProcessed,
QueueLastFinished,
LastSyncOn,
LastSyncBy,
StatusFK
)
VALUES
(
#adDomainId
,[source].UserName
,[source].DisplayName
,[source].Department
,[source].EmailAddress
,[source].ExchangeServerFk
,[source].ExchangeServer
,[source].AccountSid
,0
,#defaultProfileId
,NULL
,NULL
,NULL
,NULL
,#lastSyncOn
,#lastSyncBy
,[source].StatusFK
);
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
However, the "NOT IN" in the delete section AND "IN" in the match section don't seem to work. Is this type of IN clause using multiple values in the XML even feasible? Is there a better approach to this problem that I'm missing?
The issue with your MERGE query is the join between the [source] and the [target] tables. Rather than joining the target and source tables using
ON [target].AccountSid IN
(
SELECT rtrim(A.value('text()[1]', 'varchar(100)')) as CurSid
FROM [source].SidList.nodes('Sid') AS FN(A)
)
use this instead:
ON [target].AccountSid = [source].AccountSid
[source] will be materialised as a table and you join to it like you would any other table. Your IN statement doesn't make much sense as it is a completely different entity so will equivalent to a kind of Cartesian join (FULL OUTER).
Another comment I would make is why the separate DELETE statement to remove mailboxes that no longer exist in the XML? Why not simply put the DELETE within the MERGE statement by using the following statement?
WHEN NOT MATCHED BY SOURCE
THEN DELETE
Applying all this, your MERGE statement becomes:
MERGE tb_Mailboxes AS [target]
USING
(SELECT RTRIM(element.value('data(#AccountSid)', 'varchar(100)')) AS AccountSid
, RTRIM(element.value('data(#DisplayName)', 'varchar(100)')) AS DisplayName
, RTRIM(element.value('data(#EmailAddress)', 'varchar(500)')) AS EmailAddress
, RTRIM(element.value('data(#ExchangeServerFk)', 'varchar(100)')) AS ExchangeServerFk
, RTRIM(element.value('data(#ExchangeServer)', 'varchar(150)')) AS ExchangeServer
, RTRIM(element.value('data(#Department)', 'varchar(100)')) AS Department
, RTRIM(element.value('data(#StatusFK)', 'varchar(100)')) AS StatusFK
, RTRIM(element.value('data(#UserName)', 'varchar(100)')) AS UserName
FROM #adUsers.nodes('/AdUsers/AdUser') t (element)) AS [source]
ON [target].AccountSid = [source].AccountSid
WHEN MATCHED
THEN UPDATE
SET DisplayName = [source].DisplayName
, EmailAddress = [source].EmailAddress
, ExchangeServerFk = [source].ExchangeServerFk
, ExchangeServer = [source].ExchangeServer
, Department = [source].Department
, UserName = [source].UserName
/*,StatusFK = [source].StatusFK*/
, LastSyncOn = #lastSyncOn
, LastSyncBy = #lastSyncBy
WHEN NOT MATCHED BY TARGET
THEN INSERT (AdDomainFk
, UserName
, DisplayName
, Department
, EmailAddress
, ExchangeServerFk
, ExchangeServer
, AccountSid
, IsAutoDeleteEnabled
, ProfileFk
, Settings
, QueueLastPickedUp
, QueueLastProcessed
, QueueLastFinished
, LastSyncOn
, LastSyncBy
, StatusFK)
VALUES (#adDomainId
, [source].UserName
, [source].DisplayName
, [source].Department
, [source].EmailAddress
, [source].ExchangeServerFk
, [source].ExchangeServer
, [source].AccountSid
, 0
, #defaultProfileId
, NULL
, NULL
, NULL
, NULL
, #lastSyncOn
, #lastSyncBy
, [source].StatusFK)
WHEN NOT MATCHED BY SOURCE
THEN DELETE;