Stored procedure not working while calling in snowflake - snowflake-cloud-data-platform

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.

Related

MSSQL | JDBI 3 | NoRowsSelect For Count Query After Update

Java class creates a prepared statement and binds value
MS-SQL is the data store used
Jars Used
jdbi3-core-3.8.2
HikariCP: 3.4.1
mssql-jdbc:7.2.2.jre8
DECLARE #status VARCHAR(max)
DECLARE #destination VARCHAR(max) = 'xyz'
DECLARE #attributes_var NVARCHAR(max)
-- status , current_destination , container_id
UPDATE dbo.container_master
SET #status= status, status = 'DELETED', #destination= current_destination , current_destination = null , #attributes_var = JSON_QUERY(attributes , '$')
WHERE container_id = 'cont_id'
-- current_destination ,
SELECT count(container_id) AS count
FROM dbo.container_master
CROSS APPLY OPENJSON (attributes ) WITH (value NVARCHAR(100) '$')
where current_destination = #destination
and status <> 'DELETED'
AND attributes = JSON_QUERY(#attributes_var, '$')
Count Query when executed Returns No Rows , ResultSet is null
Throwing a Non-TransientExecption
Query statement = handle.createQuery(query);
this.bindParameters(statement, queryParameters);
ResultBearing queryResults = (ResultBearing)statement.execute(ResultProducers.returningResults());
results = (List)queryResults.map(this.mapMapper).stream().map(this::toBytes).collect(Collectors.toList());
Last Line Throws a NonTransient Exception
Options Tried
When Only a simple select queries are executed it works
Things Working
Update Fired.
Query executed successfully
Issue
Handling of ResultSets

How can I use LIKE operator instead of IN operator for multiple commas contains string in stored procedure

I just made the stored procedure for search items before the procedure I was doing this all via LINQ in C# e.g
//orders
if (objParam.OrderNumber != null && objParam.OrderNumber.Count > 0)
{
foreach (var orderNumber in objParam.OrderNumber)
{
orderNumbersBuilder.Append(',' + orderNumber.ToString());
}
}
var orderNbrs = orderNumbersBuilder.ToString().Trim(',').Split(',');
//Searching
(objParam.OrderNumber.Count == 0 || orderNbrs.Any(a => i.tblOrderMaster.OrderNumber.Contains(a)))
Now I want to do with the stored procedure. I'm getting the result with IN operator but I want to use LIKE operator e.g
SELECT * FROM tblOrderMaster WHERE TrxNumber LIKE '%' + (SELECT * FROM STRING_SPLIT('1330,1329',',')) + '%'
I've multiple filters so I don't want to use function and subqueries e.g
--Params
#Account NVARCHAR(MAX) = NULL,
#OrderNumber NVARCHAR(MAX) = NULL,
#Carrier NVARCHAR(MAX) = NULL,
#ItemCode NVARCHAR(MAX) = NULL,
#OrderType NVARCHAR(MAX) = NULL,
#PONumber NVARCHAR(MAX) = NULL,
#SONumber NVARCHAR(MAX) = NULL
--columns start
--columns end
--Where condtions
(#ACCOUNT IS NULL OR #Account = '' OR partners.PartnerCode IN (select * from string_split(#ACCOUNT,','))) -- multi select filters started
AND
(#OrderNumber IS NULL OR #OrderNumber = '' OR orderMaster.OrderNumber IN (select * from string_split(#OrderNumber,',')))
AND
(#Carrier IS NULL OR #Carrier = '' OR carrier.Description IN (select * from string_split(#Carrier,',')))
AND
(#ItemCode IS NULL OR #ItemCode = '' OR itemMaster.ItemCode IN (select * from string_split(#ItemCode,',')))
AND
(#OrderType IS NULL OR #OrderType = '' OR orderMaster.OrderType IN (select * from string_split(#OrderType,',')))
AND
(#PONumber IS NULL OR #PONumber = '' OR orderMaster.PONumber IN (select * from string_split(#PONumber,',')))
AND
(#SONumber IS NULL OR #SONumber = '' OR orderMaster.SONumber IN (select * from string_split(#SONumber,',')))
You would need to use subqueries; the fact you don't want to doesn't change this with your current design. Using the query with the literal values you have, it would look like this:
SELECT *
FROM dbo.tblOrderMaster OM
WHERE EXISTS (SELECT 1
FROM STRING_SPLIT('1330,1329', ',') SS
WHERE OM.TrxNumber LIKE '%' + SS.[Value] + '%')
If you really don't want to use subqueries, then use table type parameters and then you can perform a JOIN:
SELECT OM.*
FROM dbo.tblOrderMaster OM
JOIN #YourTableVariable YTV ON OM.TrxNumber LIKE '%' + YTV.SearchValue + '%'

Facing an issue in MERGE SQL query

I have a requirement, insert/update data from staging table stgTbl to another table T2. If exists update, no matter how many duplicates are there. Similarly, if not exists insert directly T2. Very Simple. Since the staging table stgTb1 is scheduled job everyday. Sometime I would get multiple duplicate rows as well. So because of duplicates, merge statement producing an error:
"The MERGE statement attempted to UPDATE or DELETE the same row more
than once. This happens when a target row matches more than one source
row".
I tried with checksum but still getting because i may done wrongly in checksum. There is no primary key in both the tables (i removed, because otherwise i am getting primary key constraint error). Based on columns (account and ref_key2) should go with insert/update. I have tried with if else with exists also but somehow this is also not working if in case there are 0 records in the target table T2. SQL masters could solve this issue. Appreciate their knowledge share.
CREATE OR ALTER Trigger [dbo].[tr_Merge_Personal_Expense] on [dbo].
[Personal_Expense_Staging]
AFTER INSERT
AS
BEGIN
DECLARE #SummaryOfChanges TABLE(Change nvarchar(20));
MERGE [dbo].[Personal_Expense] AS TARGET
USING (
SELECT *,
CHECKSUM(
ISNULL(CONVERT(nvarchar (3) ,[Client]),'0'),
ISNULL(CONVERT(nvarchar (50),[Text]),'0'),
ISNULL(CONVERT(nvarchar (10),[Cost_Center]),'0'),
ISNULL(CONVERT(nvarchar (2) ,[Posting_Period]),'0'),
ISNULL(CONVERT(nvarchar (10),[Profit]),'0') ,
ISNULL(CONVERT(nvarchar (2) ,[Document_Type]),'0'),
ISNULL(CONVERT(nvarchar (4) ,[Company_Code]),'0') ,
-- ISNULL(CONVERT(nvarchar (10),[Account]),'0'),
ISNULL(CONVERT(nvarchar (20),[Amount_In_Doc_Curr]),'0'),
ISNULL(CONVERT(nvarchar (5) ,[Document_Currency]),'0') ,
ISNULL(CONVERT(nvarchar (20),[Amount_In_local_Curr]),'0') ,
ISNULL(CONVERT(nvarchar (20),[Amount_In_Grp_Curr]),'0'),
ISNULL(CONVERT(nvarchar (25),[Group]),'0'),
[Posting_Date],
ISNULL(CONVERT(nvarchar (18), [Assignment]),'0'),
ISNULL(CONVERT(nvarchar (16), [Reference]),'0'),
ISNULL(CONVERT(nvarchar (20), [Reference_Key1]),'0') ,
--[Reference_Key2] [nvarchar](12) NOT NULL,
ISNULL(CONVERT(nvarchar (20), [Reference_Key3]),'0') ,
ISNULL(CONVERT(nvarchar (20), [Document_Number]),'0') ,
ISNULL(CONVERT(nvarchar (25), [Document_Header_Text]),'0')
) AS [HashChecksum]
FROM
[dbo].[Personal_Expense_Staging]
) AS SOURCE
ON (
TARGET.[Reference_Key2] = SOURCE.[Reference_Key2] AND
TARGET.[Account] = SOURCE.[Account]
)
----- Update
WHEN MATCHED AND ( TARGET.[HashChecksum] <> SOURCE.[HashChecksum] )
THEN
UPDATE SET
TARGET.[client] = SOURCE.[client],
TARGET.[text] = SOURCE.[text],
TARGET.[cost_center] = SOURCE.[cost_center],
TARGET.[posting_period] = SOURCE.[posting_period],
TARGET.[profit] = SOURCE.[profit],
TARGET.[document_type]= SOURCE.[document_type],
TARGET.[company_code] = SOURCE.[company_code],
TARGET.[Account] = SOURCE.[Account],
TARGET.[Amount_In_Doc_Curr] = SOURCE.[Amount_In_Doc_Curr],
TARGET.[document_currency] = SOURCE.[document_currency],
TARGET.[Amount_In_local_Curr] = SOURCE.[Amount_In_local_Curr],
TARGET.[Amount_In_Grp_Curr] = SOURCE.[Amount_In_Grp_Curr],
TARGET.[group] = SOURCE.[group],
TARGET.[posting_date] = SOURCE.[posting_date],
TARGET.[assignment] = SOURCE.[assignment],
TARGET.[reference] = SOURCE.[reference],
TARGET.[document_header_text] = SOURCE.[document_header_text],
TARGET.[Last_updated_DateTime] = GETDATE(),
TARGET.[HashChecksum] = SOURCE.[HashChecksum]
-- Insert
WHEN NOT MATCHED THEN
INSERT (
[Client],
[text],
[cost_center],
[posting_period],
[profit],
[document_type],
[company_code],
[Account],
[Amount_In_Doc_Curr],
[document_currency],
[Amount_In_local_Curr],
[Amount_In_Grp_Curr],
[group],
[posting_date],
[assignment],
[reference],
[reference_key2],
[reference_key3],
[document_number],
[document_header_text],
[Last_updated_DateTime],
[HashChecksum]
)
VALUES (
SOURCE.[client],
SOURCE.[text],
SOURCE.[cost_center],
SOURCE.[posting_period],
SOURCE.[profit],
SOURCE.[document_type],
SOURCE.[company_code],
SOURCE.[Account],
SOURCE.[Amount_In_Doc_Curr],
SOURCE.[document_currency],
SOURCE.[Amount_In_local_Curr],
SOURCE.[Amount_In_Grp_Curr],
SOURCE.[group],
SOURCE.[posting_date],
SOURCE.[assignment],
SOURCE.[reference],
SOURCE.[reference_key2],
SOURCE.[reference_key3],
SOURCE.[document_number],
SOURCE.[document_header_text],
GETDATE(),
SOURCE.[HashChecksum]
)
OUTPUT $action INTO #SummaryOfChanges;
DECLARE #RowsProcessed INT = 0;
SELECT
#RowsProcessed = ISNULL([INSERT],0) + ISNULL([UPDATE],0) + ISNULL([DELETE],0)
FROM (
SELECT COUNT(*) ChangeCount, Change as ChangeType
FROM #SummaryOfChanges
GROUP BY Change
)Main
PIVOT (
MAX(ChangeCount)
FOR ChangeType IN ([INSERT],[UPDATE],[DELETE])
)Pvt;
SELECT #RowsProcessed AS RowsProcessed;
CREATE OR ALTER TRIGGER [dbo].[tr_merge_personal_expense]
ON personal_expense_staging
AFTER INSERT AS
----- CASE 1: IF (ACCOUNT AND REF_KEY2) ARE MATCHED THEN UPDATE
IF EXISTS
(
SELECT
*
FROM dbo.personal_expense p
INNER JOIN inserted e
ON p.[account] = e.[account] AND p.[reference_key2] = e.
[reference_key2]
)
BEGIN
UPDATE target
SET
target.[document_number] = source.[document_number],
target.[client] = source.[client],
target.[text] = source.[text],
target.[cost_center] = source.[cost_center],
target.[posting_period] = source.[posting_period],
target.[profit] = source.[profit],
target.[document_type] = source.[document_type],
target.[company_code] = source.[company_code],
target.[amount_in_doc_curr] = source.[amount_in_doc_curr],
target.[document_currency] = source.[document_currency],
target.[amount_in_local_curr] = source.[amount_in_local_curr],
target.[amount_in_grp_curr] = source.[amount_in_grp_curr],
target.[group] = source.[group],
target.[posting_date] = source.[posting_date],
target.[assignment] = source.[assignment],
target.[reference] = source.[reference],
target.[reference_key1] = source.[reference_key1],
target.[reference_key3] = source.[reference_key3],
target.[document_header_text] = source.[document_header_text],
target.[last_updated_datetime] = GETDATE()
FROM personal_expense target
INNER JOIN inserted source
ON target.[account] = source.[account]
AND target.[reference_key2] = source.[reference_key2];
END;
------ CASE 2: IF (ACCOUNT AND REF_KEY2) ARE NOT MATCHED THEN INSERT
IF NOT EXISTS
(
SELECT
*
FROM dbo.personal_expense p
INNER JOIN inserted e
ON p.[account] = e.[account] AND p.[reference_key2] = e.
[reference_key2]
)
BEGIN
INSERT INTO dbo.personal_expense
(
[client],
[text],
[cost_center],
[posting_period],
[profit],
[document_type],
[company_code],
[account],
[amount_in_doc_curr],
[document_currency],
[amount_in_local_curr],
[amount_in_grp_curr],
[group],
[posting_date],
[assignment],
[reference],
[reference_key1],
[reference_key2],
[reference_key3],
[document_number],
[document_header_text],
[last_updated_datetime]
)
SELECT
source.[client],
source.[text],
source.[cost_center],
source.[posting_period],
source.[profit],
source.[document_type],
source.[company_code],
source.[account],
source.[amount_in_doc_curr],
source.[document_currency],
source.[amount_in_local_curr],
source.[amount_in_grp_curr],
source.[group],
source.[posting_date],
source.[assignment],
source.[reference],
source.[reference_key1],
source.[reference_key2],
source.[reference_key3],
source.[document_number],
source.[document_header_text],
GETDATE()
FROM inserted source;
END;
SO, you know that of duplicates, merge statement producing an error. Then simply remove the duplicates in your source clause, using DISTINCT:
SELECT DISTINCT *
FROM [dbo].[Personal_Expense_Staging]

Error creating stored procedure in SQL Server with PIVOT operator

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

Using T-SQL XML input element values for an IN Clause

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;

Resources