Dedup and Combine Customer Data - sql

I'm trying to combine records based on the ClusterID to have an enriched data of customer records.
How will I be able to group the following using MS SQL? Coalesce won't work as records need to be on the same row to make it work, and if I have more than 2 matches per cluster it's going to be a tedious processing. Using max by ClusterId in all columns is a workaround I'm doing but I was hoping there is a more efficient way to do this.
Have:
ClusterID,CustomerNo,Name,Email,Mobile,Address,PostalCode,Passport,ProfileNo
100,NULL,Person,person#email.com,NULL,OfficeAdd,12345,NULL,123
100,456,Person,person#email.com,98765,HomeAdd,34567,P12345,NULL
**This is a result of the SSIS DQS Matching node (https://ssisdqsmatching.codeplex.com/). It can do the match, but cannot handle the survivorship portion to get the golden record.
Want:
ClusterID,CustomerNo,Name,Email,Mobile,Address,PostalCode,Passport,ProfileNo
100,456,Person,person#email.com,98765,OfficeAdd,12345,P12345,123
Any thoughts would be much appreciated. Thank you!

DECLARE #CLUSTERID VARCHAR(MAX),#CUSTOMERNO VARCHAR(MAX),#NAME VARCHAR(MAX),#EMAIL VARCHAR(MAX),#MOBILE VARCHAR(MAX)
DECLARE #POSTALCODE VARCHAR(MAX),#PASSPORT VARCHAR(MAX),#PROFILENO VARCHAR(MAX),#ADDRESS VARCHAR(MAX)
DECLARE #NCLUSTERID VARCHAR(MAX),#NCUSTOMERNO VARCHAR(MAX),#NNAME VARCHAR(MAX),#NEMAIL VARCHAR(MAX),#NMOBILE VARCHAR(MAX)
DECLARE #NPOSTALCODE VARCHAR(MAX),#NPASSPORT VARCHAR(MAX),#NPROFILENO VARCHAR(MAX),#NADDRESS VARCHAR(MAX)
DECLARE #NEW_TABLE TABLE ( ClusterID varchar(max) ,
CustomerNo varchar(max) ,
Name varchar(max) ,
Email varchar(max) ,
Mobile varchar(max) ,
Address varchar(max) ,
PostalCode varchar(max) ,
Passport varchar(max) ,
ProfileNo varchar(max)
)
DECLARE C CURSOR FOR
SELECT DISTINCT CLUSTERID FROM CUSTOMER
OPEN C
FETCH NEXT FROM C INTO #CLUSTERID
WHILE ##FETCH_STATUS=0
BEGIN
DECLARE D CURSOR FOR
select CustomerNo,Name,Email,Mobile,Address,PostalCode,Passport,ProfileNo from customer where ClusterID=#CLUSTERID
OPEN D
FETCH NEXT FROM D INTO #CustomerNo,#Name,#Email,#Mobile,#Address,#PostalCode,#Passport,#ProfileNo
WHILE ##FETCH_STATUS=0
BEGIN
IF #CustomerNo is not null SET #NCustomerNo=#CustomerNo
IF #CustomerNo IS NOT NULL SET #NCustomerNo= #CustomerNo ;
IF #Name IS NOT NULL SET #NName = #Name ;
IF #Email IS NOT NULL SET #NEmail = #Email ;
IF #Mobile IS NOT NULL SET #NMobile = #Mobile ;
IF #Address IS NOT NULL SET #NAddress = #Address ;
IF #PostalCode IS NOT NULL SET #NPostalCode= #PostalCode ;
IF #Passport IS NOT NULL SET #NPassport = #Passport ;
IF #ProfileNo IS NOT NULL SET #NProfileNo = #ProfileNo ;
FETCH NEXT FROM D INTO #CustomerNo,#Name,#Email,#Mobile,#Address,#PostalCode,#Passport,#ProfileNo
END
CLOSE D
DEALLOCATE D
INSERT INTO #NEW_TABLE VALUES (
#CLUSTERID,
#NCustomerNo ,
#NName ,
#NEmail ,
#NMobile ,
#NAddress ,
#NPostalCode ,
#NPassport ,
#NPROFILENO
)
FETCH NEXT FROM C INTO #CLUSTERID
END
CLOSE C
DEALLOCATE C
SELECT * FROM #NEW_TABLE

I guess this query solves your requirement
DECLARE #CLUSTERID VARCHAR(MAX),#CUSTOMERNO VARCHAR(MAX),#NAME VARCHAR(MAX),#EMAIL VARCHAR(MAX),#MOBILE VARCHAR(MAX)
DECLARE #POSTALCODE VARCHAR(MAX),#PASSPORT VARCHAR(MAX),#PROFILENO VARCHAR(MAX),#ADDRESS VARCHAR(MAX)
DECLARE #NCLUSTERID VARCHAR(MAX)=NULL,#NCUSTOMERNO VARCHAR(MAX)=NULL,#NNAME VARCHAR(MAX)=NULL,#NEMAIL VARCHAR(MAX)=NULL,#NMOBILE VARCHAR(MAX)=NULL
DECLARE #NPOSTALCODE VARCHAR(MAX)=NULL,#NPASSPORT VARCHAR(MAX)=NULL,#NPROFILENO VARCHAR(MAX)=NULL,#NADDRESS VARCHAR(MAX)=NULL
DECLARE #NEW_TABLE TABLE ( ClusterID varchar(max) ,
CustomerNo varchar(max) ,
Name varchar(max) ,
Email varchar(max) ,
Mobile varchar(max) ,
Address varchar(max) ,
PostalCode varchar(max) ,
Passport varchar(max) ,
ProfileNo varchar(max)
)
DECLARE C CURSOR FOR
SELECT DISTINCT CLUSTERID FROM CUSTOMER
OPEN C
FETCH NEXT FROM C INTO #CLUSTERID
WHILE ##FETCH_STATUS=0
BEGIN
DECLARE D CURSOR FOR
select CustomerNo,Name,Email,Mobile,Address,PostalCode,Passport,ProfileNo from customer where ClusterID=#CLUSTERID
OPEN D
FETCH NEXT FROM D INTO #CustomerNo,#Name,#Email,#Mobile,#Address,#PostalCode,#Passport,#ProfileNo
WHILE ##FETCH_STATUS=0
BEGIN
IF #CustomerNo is not null SET #NCustomerNo=#CustomerNo
IF #CustomerNo IS NOT NULL SET #NCustomerNo= #CustomerNo ;
IF #Name IS NOT NULL SET #NName = #Name ;
IF #Email IS NOT NULL SET #NEmail = #Email ;
IF #Mobile IS NOT NULL SET #NMobile = #Mobile ;
IF #Passport IS NOT NULL SET #NPassport = #Passport ;
IF #ProfileNo IS NOT NULL SET #NProfileNo = #ProfileNo ;
IF (#ADDRESS IS NOT NULL AND #NADDRESS IS NOT NULL)
BEGIN
SET #NAddress = #Address ;
SET #NPostalCode= #PostalCode ;
END
ELSE IF(#ADDRESS IS NOT NULL AND #NADDRESS IS NULL)
BEGIN
SET #NAddress = #Address ;
SET #NPostalCode= NULL ;
END
ELSE IF(#ADDRESS IS NULL AND #NADDRESS IS NOT NULL)
BEGIN
SET #NAddress = NULL ;
SET #NPostalCode= #PostalCode ;
END
FETCH NEXT FROM D INTO #CustomerNo,#Name,#Email,#Mobile,#Address,#PostalCode,#Passport,#ProfileNo
END
CLOSE D
DEALLOCATE D
INSERT INTO #NEW_TABLE VALUES (
#CLUSTERID,
#NCustomerNo ,
#NName ,
#NEmail ,
#NMobile ,
#NAddress ,
#NPostalCode ,
#NPassport ,
#NPROFILENO
)
FETCH NEXT FROM C INTO #CLUSTERID
END
CLOSE C
DEALLOCATE C
SELECT * FROM #NEW_TABLE

Related

SQL server issue - Create Function must be the only statement in the batch

I have a function which I have created in SQL but I am getting this error 'SQL server issue - Create Function must be the only statement in the batch'. I checked other similar topics but couldn't find anything wrong. I am using SQL Server 2012
CREATE FUNCTION GETLLPATH(#objectid FLOAT)
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #dir VARCHAR(MAX);
DECLARE #obj_id FLOAT;
DECLARE Name_Cursor CURSOR LOCAL FOR
SELECT A.Name, A.ParentID FROM OTCS_User.DTree A
WHERE A.DataID = #obj_id;
DECLARE
SET #dir = NULL;
SET #obj_id = #objectid;
WHILE 1=1 BEGIN
OPEN Name_Cursor;
FETCH Name_Cursor INTO #name;
IF ##FETCH_STATUS <> 0 BREAK or #name_NAME = 'Enterprise';
IF #dir IS NOT NULL BEGIN
SET #dir = (ISNULL(#name_NAME, '') + ':' + isnull(#dir, '')) ;
END
IF #dir IS NULL BEGIN
SET #dir = #name_NAME;
END
SET #obj_id = #name_PARENTID;
CLOSE Name_Cursor;
DEALLOCATE Name_Cursor;
END;
return(#dir);
END;
GO
I am also getting error for variables as 'Must declare Scalar variable', In the end there is one more error - 'Expecting conversation', request you to please help.
I believe you just have some bad code.
Try this:
CREATE FUNCTION GETLLPATH(
#objectid FLOAT
)
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #dir VARCHAR(MAX);
DECLARE
#obj_id FLOAT
, #name_NAME VARCHAR(50) -- or whatever your field size is.
, #name_PARENTID VARCHAR(50) -- again, whatever your field size is.
DECLARE Name_Cursor CURSOR LOCAL FOR
SELECT A.Name, A.ParentID FROM OTCS_User.DTree A WHERE A.DataID = #obj_id;
SET #dir = NULL; -- redundant.
SET #obj_id = #objectid; -- this can be set at declaration ( e.g. DELARE #obj_id FLOAT = #obj_id ).
WHILE ( 1 = 1 ) BEGIN
OPEN Name_Cursor;
FETCH Name_Cursor INTO #name;
IF ( ##FETCH_STATUS <> 0 OR #name_NAME = 'Enterprise' )
BREAK;
IF ( #dir IS NOT NULL ) BEGIN
SET #dir = (ISNULL(#name_NAME, '') + ':' + isnull(#dir, '')) ;
END
IF #dir IS NULL BEGIN
SET #dir = #name_NAME;
END
SET #obj_id = #name_PARENTID;
CLOSE Name_Cursor;
DEALLOCATE Name_Cursor;
END
RETURN #dir;
END
GO
On a personal note, I am never fond of using WHILE (1=1). Are you guaranteed to have an exit?
Also, I would highly recommend using an alternative to a cursor. Perhaps use a TABLE variable and loop through that like so:
CREATE FUNCTION GETLLPATH(
#objectid FLOAT
)
RETURNS VARCHAR(4000)
AS
BEGIN
-- declare variables --
DECLARE #id INT
, #dir VARCHAR(MAX)
, #obj_id FLOAT = #objectid
, #name_NAME VARCHAR(50)
, #name_PARENTID VARCHAR(50)
-- declare table variable --
DECLARE #data TABLE( [name] VARCHAR(50), [parent_id] VARCHAR(50), [id] INT IDENTITY (1,1) );
-- insert data --
INSERT INTO #data ( [name], [parent_id] )
SELECT A.Name, A.ParentID FROM OTCS_User.DTree A WHERE A.DataID = #obj_id;
-- for-each row... --
SET #id = 1;
WHILE ( #id <= ( SELECT MAX( id ) FROM #data ) )
BEGIN
-- current row --
SELECT
#name_NAME = [name]
, #name_PARENTID = [parent_id]
FROM #data WHERE [id] = #id;
-- do your work here...
-- next row --
SET #id = ( #id + 1 );
END
RETURN #dir;
END
GO
There are a couple of syntax errors even before I test your query:
The word 'DECLARE' should not appear in the middle without a variable name,
#name_Name and #name_PARENTID are undeclared
DECLARE #name_NAME varchar, #name_PARENTID int
(check for the variable types according to the source table)
IF ##FETCH_STATUS <> 0 BREAK or #name_NAME = 'Enterprise' is incorrect
IF ##FETCH_STATUS <> 0 or #name_NAME = 'Enterprise'
BREAK
"Create Function must be the only statement in the batch" - usually
means there is some syntax error.
This is, honestly, a total guess, and (most importantly) completely untested; due to the absence of sample data and expected results.
Anyway, like I said in the comments, a CURSOR and a Scalar-value Function are both really bad performers here. If you're trying to simply delimit your data with a colon (:), then you can use STUFF and FOR XML PATH.
Like I said, this is completed untested, but might get you on the right path:
CREATE FUNCTION dbo.GetllPath (#objectID int) --Is it really a float? I've used an int
RETURNS table
AS RETURN
SELECT STUFF((SELECT ':' + DT.[Name]
FROM OTCS_User.DTree DT
WHERE DT.DataID = #obj_id
--ORDER BY SOMETHING HERE!!!
FOR XML PATH('')),1,1,'') AS llPath;
GO

SQL Cursor to Insert into Temporary Table and Fetch From Temparory Table

Sir,
I have build a SQL query using Cursor as #AllRecords to insert values into Temporary Table & then Fetch values from that temporary table. But it showing me an error at last statement when I am fetching values from table (Error: incorrect syntax near #AllRecords). Below is my code:
DECLARE #ColName varchar(20)=null,
#Query varchar(MAX)=null,
#DepartmentName varchar(50)=null,
#deptt_code varchar(4)=null,
#DistrictId varchar(4)='0001',
#Deptt_Id char(4)=null,
#stYear varchar(4)=null,
#cYear varchar(4)=null,
#yr varchar(9)='2017-2018',
#tno int
BEGIN
set #stYear = SUBSTRING(#yr,0,5)
set #cYear = SUBSTRING(#yr,6,4)
--DECLARE & SET COUNTER
DECLARE #counter int
SET #counter = 1
--CREATE DYNAMIC TABLE WITH COLs
DECLARE #AllRecords table
(
department_name varchar(50),
project_name varchar(100),
department_code varchar(4)
)
--*** Declare Cursor
DECLARE cur_FetchDepartmentName CURSOR READ_ONLY
FOR
select deptt_code,deptt_name+'('+ RTRIM(LTRIM(deptt_short))+')' as dept_name from m_Department
where deptt_code in (select distinct department_code from t_Project_Details where district_id=#DistrictId
and financial_year=#yr)
OPEN cur_FetchDepartmetName
fetch next from cur_FetchDepartmetName into
#deptt_code, #DepartmentName
--LOOP UNTIL RECORDS ARE AVAILABLE
while ##FETCH_STATUS=0
BEGIN
if(#tno=0)
BEGIN
set #tno=1
insert into #AllRecords values(#DepartmentName,#deptt_code)
fetch next from cur_FetchDepartmetName into
#deptt_code,#DepartmentName
END
else
BEGIN
set #tno=#tno+1
insert into #AllRecords values(#DepartmentName,#deptt_code)
fetch next from cur_FetchDepartmetName into
#deptt_code,#DepartmentName
END
END
--CLOSE CURSOR
CLOSE cur_FetchDepartmetName
DEALLOCATE cur_FetchDepartmetName
select department_name, department_code from #AllRecords
Instead of answering what is error in this solution, I would like to offer a better solution to the problem. Use of cursor in this example is completely unnecessary, query can be more easily be written without it. It's a simple INSERT..SELECT statement and counting of records to set #tno can easily be done in the end.
BEGIN
set #stYear = SUBSTRING(#yr,0,5);
set #cYear = SUBSTRING(#yr,6,4);
--CREATE DYNAMIC TABLE WITH COLs
DECLARE #AllRecords table
(
department_name varchar(50),
project_name varchar(100), --what's the use of this column?
department_code varchar(4)
);
INSERT INTO #AllRecords (department_code, department_name)
select deptt_code,deptt_name+'('+ RTRIM(LTRIM(deptt_short))+')' as dept_name from m_Department
where deptt_code in (select distinct department_code from t_Project_Details where district_id=#DistrictId
and financial_year=#yr);
SELECT #tNo = COALESCE(#tno,0) + COUNT(*) FROM #AllRecords;
select department_name, department_code from #AllRecords;
END
Please check this article about cursors and how to avoid them:
Cursors and How to Avoid Them
Your SQL Query as below :
BEGIN
DECLARE #ColName VARCHAR(20)= NULL, #Query VARCHAR(MAX)= NULL, #DepartmentName VARCHAR(50)= NULL, #deptt_code VARCHAR(4)= NULL, #DistrictId VARCHAR(4)= '0001', #Deptt_Id CHAR(4)= NULL, #stYear VARCHAR(4)= NULL, #cYear VARCHAR(4)= NULL, #yr VARCHAR(9)= '2017-2018', #tno INT;
SET #stYear = SUBSTRING(#yr, 0, 5);
SET #cYear = SUBSTRING(#yr, 6, 4);
--DECLARE & SET COUNTER
DECLARE #counter INT;
SET #counter = 1;
--CREATE DYNAMIC TABLE WITH COLs
DECLARE #AllRecords TABLE
(department_name VARCHAR(50),
project_name VARCHAR(100),
department_code VARCHAR(4)
);
--*** Declare Cursor
DECLARE cur_FetchDepartmentName CURSOR READ_ONLY
FOR
SELECT deptt_code,
deptt_name+'('+RTRIM(LTRIM(deptt_short))+')' AS dept_name
FROM m_Department
WHERE deptt_code IN
(
SELECT DISTINCT
department_code
FROM t_Project_Details
WHERE district_id = #DistrictId
AND financial_year = #yr
);
OPEN cur_FetchDepartmetName;
FETCH NEXT FROM cur_FetchDepartmetName INTO #deptt_code, #DepartmentName;
--LOOP UNTIL RECORDS ARE AVAILABLE
WHILE ##FETCH_STATUS = 0
BEGIN
IF(#tno = 0)
BEGIN
SET #tno = 1;
INSERT INTO #AllRecords
(department_name,
department_code
)
SELECT #DepartmentName,
#deptt_code;
FETCH NEXT FROM cur_FetchDepartmetName INTO #deptt_code, #DepartmentName;
END;
ELSE
BEGIN
SET #tno = #tno + 1;
INSERT INTO #AllRecords
(department_name,
department_code
)
SELECT #DepartmentName,
#deptt_code;
FETCH NEXT FROM cur_FetchDepartmetName INTO #deptt_code, #DepartmentName;
END;
END;
--CLOSE CURSOR
CLOSE cur_FetchDepartmetName;
DEALLOCATE cur_FetchDepartmetName;
select department_name, department_code from #AllRecords
END;

Unable to Create the Complete Dynamic Query using NVARCHAR(Max)

I am using NVARCHAR(MAX) to create a dynamic query.Since NVARCHAR uses 2 bytes per character, Approximately 1 billion characters can include in NVARCHAR(MAX) variable (Link Reference) .
I tried by executing stored procedure in SQL Server itself, then executing the stored procedure through the application.
Both situation dynamic query is not exceeding those character length. But only part of the Dynamic query is get executed.Because of that stored procedure throw errors to the application.
Am I missing any code ?
USE [MyDemoDB]
GO
ALTER PROCEDURE [dbo].[sp_Apply]
(
#scenarioId INT,
#userId INT,
#bookId INT
)
AS
DECLARE #BucketId INT
DECLARE #HierarchyId NVARCHAR(10)
DECLARE #Year INT
DECLARE #Month INT
DECLARE #PlanningSeason NVARCHAR(20)
DECLARE #StructureId INT = 9
DECLARE #AllocStructureId INT = 11
DECLARE #UpdatedUser INT = 2
DECLARE #InsertOne NVARCHAR(MAX)=''
DECLARE #AreaSchema NVARCHAR(40)
DECLARE #AreaCode NVARCHAR(20)
DECLARE #EmptyValue NVARCHAR(20)
SET #AreaCode = ''
SET #AreaSchema = '[dbo]'
SET #InsertOne = '
DECLARE #FGSupplySeqId INT
DECLARE #FGSupplyId NVARCHAR(10)
DECLARE #PlannedQty DECIMAL(18,2)
DECLARE #ConfirmdQty DECIMAL(18,2)
DECLARE #Year INT
DECLARE #Month INT
DECLARE #Season NVARCHAR(20)
DECLARE #MerchantId NVARCHAR(50)
DECLARE #UpdatedUser INT
DECLARE #HierarchyId NVARCHAR(10)
DECLARE #BucketId INT
DECLARE #ProductNo NVARCHAR(100)
DECLARE #LocationNo NVARCHAR(100)
SET #BucketId = '+ CAST(#BucketId AS VARCHAR) + '
SET #UpdatedUser = '+ CAST(#userId AS VARCHAR) + '
IF #BucketId = 1
BEGIN
DECLARE Supplys
CURSOR FOR
SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + #AreaSchema + '.[FGSupply]
WHERE PlanningScenarioId ='+ CONVERT(VARCHAR(10),#scenarioId)+ '
OPEN Supplys
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Allocations
CURSOR FOR
SELECT #FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year,Season,ProductNo,LocationNo
FROM '+ #AreaSchema +'.[FGAllocation]
WHERE FGSupplySeqId = #FGSupplySeqId
OPEN Allocations
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Season,#ProductNo,#LocationNo
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #FGAllocationId NVARCHAR(10)
DECLARE #AllocStatus INT
SET #FGAllocationId = ''E''
SET #AllocStatus= 0
SELECT #FGAllocationId = FGAllocationId,#AllocStatus=Status
FROM ' + #AreaSchema+'.[SN_PLANNING_FGAllocation]
WHERE [HierarchyId]=#HierarchyId AND [MerchantNo]=#MerchantId AND YEAR = #Year AND [Month] IS NULL
IF #FGAllocationId = ''E''
BEGIN
-- IF #AllocStatus <> 5
INSERT INTO'+ #AreaSchema+'.[SN_PLANNING_FGAllocation]
(FinishedGoodSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
VALUES(#FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#Year,#Season,0,1,#UpdatedUser,#UpdatedUser,GETDATE(),GETDATE(),#ProductNo,#LocationNo)
END
ELSE
BEGIN
-- IF #AllocStatus <> 5
UPDATE ' + #AreaSchema + '.[SN_PLANNING_FGAllocation]
SET PlannedQty = #PlannedQty ,ConfirmedQty=#ConfirmdQty,UpdatedBy=#UpdatedUser, UpdatedOn=GETDATE()
WHERE FGAllocationId = #FGAllocationId
END
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Season,#ProductNo,#LocationNo
END
CLOSE Allocations
DEALLOCATE Allocations
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
END
CLOSE Supplys
DEALLOCATE Supplys
END
IF #BucketId = 2
BEGIN
DECLARE Supplys
CURSOR FOR
SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + #AreaSchema + '.[FGSupply]
WHERE PlanningScenarioId ='+ CONVERT(VARCHAR(10),#scenarioId)+ 'AND Month IS NOT NULL
OPEN Supplys
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Allocations
CURSOR FOR
SELECT #FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year, Month,Season,#ProductNo,#LocationNo
FROM '+ #AreaSchema +'.[FGAllocation]
WHERE FGSupplySeqId = #FGSupplySeqId AND Month IS NOT NULL
OPEN Allocations
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Month,#Season,#ProductNo,#LocationNo
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #FGAllocationId1 NVARCHAR(10)
SET #FGAllocationId1 = ''E''
SELECT #FGAllocationId1 = FGAllocationId,#AllocStatus=Status
FROM ' + #AreaSchema+'.[SN_PLANNING_FGAllocation]
WHERE [HierarchyId]=#HierarchyId AND [MerchantNo]=#MerchantId AND YEAR = #Year AND [Month] = #Month
IF #FGAllocationId1 = ''E''
BEGIN
-- IF #AllocStatus <> 5
INSERT INTO'+ #AreaSchema+'.[SN_PLANNING_FGAllocation]
(FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Month,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
VALUES(#FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#Year,#Month,#Season,0,1,#UpdatedUser,#UpdatedUser,GETDATE(),GETDATE(),#ProductNo,#LocationNo)
END
ELSE
BEGIN
-- IF #AllocStatus <> 5
UPDATE ' + #AreaSchema + '.[SN_PLANNING_FGAllocation]
SET PlannedQty = #PlannedQty ,ConfirmedQty=#ConfirmdQty,UpdatedBy=#UpdatedUser, UpdatedOn=GETDATE()
WHERE FGAllocationId = #FGAllocationId1
END
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Month,#Season,#ProductNo,#LocationNo
END
CLOSE Allocations
DEALLOCATE Allocations
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
END
CLOSE Supplys
DEALLOCATE Supplys
END'
print #InsertOne
EXEC(#InsertOne)
Yes, you may be facing the issue because of nvarchar limit is 4000 characters.
I also face this issue and resolved by concatenate the string and then execute.
If you select or print it only show 4000 character, but if you concatenate or append the string it must append (till 8000 character). So don't bother about this, you do not print or select just append and execute and its definitely work.
In this link this is explain.
declare #sql Nvarchar(max),
#a nvarchar(max),
#b nvarchar(max);
select #sql =N'', #a = N'a', #b = N'b';
select #sql = #sql +replicate(#a,4000) + replicate(#b, 6000);
select len(#sql)
There is one rule for this :-
SET #dynamicSQL = [concatenate various unicode strings and nvarchar
variables totalling over 4000 characters] -- MAKE SURE AT LEAST ONE OF
THE UNICODE STRINGS IS NVARCHAR(MAX), YOU CAN CAST ANY ONE ARGUMENT.
You can check this link also.
https://dba.stackexchange.com/questions/18483/varcharmax-field-cutting-off-data-after-8000-characters-sql-server-2008
Updated
I show your entire code and want to explain some things.
First of all, why you want dynamic query. Code shows that you can do it without dynamic query and also there is so much nested cursor (try to ignore it with simple query)
Still if you want to go, then I remove you extra code (I don't think that remove space will work, I had 4 union query and its very huge length and its work with this strategy after verify each part in separate window)
a.Here is another option before you read below. rather than define parameter in query, you can pass this parameter too.
begin tran
create table table1 ( id int, value varchar(10) )
insert into table1 values( 1,'001')
insert into table1 values(2, '002')
insert into table1 values( 3,'003')
insert into table1 values( 4,'004')
declare #sql nvarchar(max) , #temp nvarchar(50) = '1,2,3', #tempIntSingleValue nvarchar(50) = '2'
select * from table1
set #sql = 'select * from table1 where id in ( ' + #temp + ')'
print #sql
exec sp_executesql #sql
set #sql = 'select * from table1 where id in ( #tempInner)'
print #sql
exec sp_executesql #sql , N'#tempInner int', #tempInner = #tempIntSingleValue
rollback
b. you used same parameter in your dynamic query. so I think issue with you have to give either default value or assign value at run-time. so while concatenating your string not become null. See this example below. I am define all character to '' and int to numeric value and at last print which print something. If we not define it never print blank due to concatenate set null value.
declare #scenarioId INT = 1 ,
#userId INT = 5,
#bookId INT = 1
DECLARE #BucketId INT = 0
DECLARE #HierarchyId NVARCHAR(10)
DECLARE #Year INT
DECLARE #Month INT
DECLARE #PlanningSeason NVARCHAR(20)
DECLARE #StructureId INT = 9
DECLARE #AllocStructureId INT = 11
DECLARE #UpdatedUser INT = 2
DECLARE #InsertOne NVARCHAR(MAX) =''
DECLARE #AreaSchema NVARCHAR(40)
DECLARE #AreaCode NVARCHAR(20)
DECLARE #EmptyValue NVARCHAR(20)
SET #AreaCode = ''
SET #AreaSchema = '[dbo]'
SET #InsertOne =
'DECLARE #FGSupplySeqId INT = 5
DECLARE #FGSupplyId NVARCHAR(10) = ''''
DECLARE #PlannedQty DECIMAL(18,2) = ''''
DECLARE #ConfirmdQty DECIMAL(18,2) = ''''
DECLARE #Year INT = 2015
DECLARE #Month INT = 7
DECLARE #Season NVARCHAR(20) = ''''
DECLARE #MerchantId NVARCHAR(50) = ''''
DECLARE #UpdatedUser INT
DECLARE #HierarchyId NVARCHAR(10) = ''''
DECLARE #BucketId INT = 0
DECLARE #ProductNo NVARCHAR(100)= ''''
DECLARE #LocationNo NVARCHAR(100)
SET #BucketId = '+ CAST(#BucketId AS VARCHAR) + '
SET #UpdatedUser = '+ CAST(#userId AS VARCHAR) + '
IF #BucketId = 1
BEGIN
DECLARE Supplys
CURSOR FOR
SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + #AreaSchema + '.[FGSupply]
WHERE PlanningScenarioId ='+ CONVERT(VARCHAR(10),#scenarioId)+ '
OPEN Supplys
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Allocations
CURSOR FOR
SELECT #FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year,Season,ProductNo,LocationNo
FROM '+ #AreaSchema +'.[FGAllocation]
WHERE FGSupplySeqId = #FGSupplySeqId
OPEN Allocations
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Season,#ProductNo,#LocationNo
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #FGAllocationId NVARCHAR(10)
DECLARE #AllocStatus INT
SET #FGAllocationId = ''E''
SET #AllocStatus= 0
SELECT #FGAllocationId = FGAllocationId,#AllocStatus=Status
FROM ' + #AreaSchema+'.[SN_PLANNING_FGAllocation]
WHERE [HierarchyId]=#HierarchyId AND [MerchantNo]=#MerchantId AND YEAR = #Year AND [Month] IS NULL
IF #FGAllocationId = ''E''
BEGIN
-- IF #AllocStatus <> 5
INSERT INTO'+ #AreaSchema+'.[SN_PLANNING_FGAllocation]
(FinishedGoodSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
VALUES(#FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#Year,#Season,0,1,#UpdatedUser,#UpdatedUser,GETDATE(),GETDATE(),#ProductNo,#LocationNo)
END
ELSE
BEGIN
-- IF #AllocStatus <> 5
UPDATE ' + #AreaSchema + '.[SN_PLANNING_FGAllocation]
SET PlannedQty = #PlannedQty ,ConfirmedQty=#ConfirmdQty,UpdatedBy=#UpdatedUser, UpdatedOn=GETDATE()
WHERE FGAllocationId = #FGAllocationId
END
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Season,#ProductNo,#LocationNo
END
CLOSE Allocations
DEALLOCATE Allocations
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
END
CLOSE Supplys
DEALLOCATE Supplys
END
IF #BucketId = 2
BEGIN
DECLARE Supplys
CURSOR FOR
SELECT [FGSupplySeqId],[FGSupplyId] FROM ' + #AreaSchema + '.[FGSupply]
WHERE PlanningScenarioId ='+ CONVERT(VARCHAR(10),#scenarioId)+ 'AND Month IS NOT NULL
OPEN Supplys
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Allocations
CURSOR FOR
SELECT #FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,ConfirmedQty,Year, Month,Season,#ProductNo,#LocationNo
FROM '+ #AreaSchema +'.[FGAllocation]
WHERE FGSupplySeqId = #FGSupplySeqId AND Month IS NOT NULL
OPEN Allocations
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Month,#Season,#ProductNo,#LocationNo
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #FGAllocationId1 NVARCHAR(10)
SET #FGAllocationId1 = ''E''
SELECT #FGAllocationId1 = FGAllocationId,#AllocStatus=Status
FROM ' + #AreaSchema+'.[SN_PLANNING_FGAllocation]
WHERE [HierarchyId]=#HierarchyId AND [MerchantNo]=#MerchantId AND YEAR = #Year AND [Month] = #Month
IF #FGAllocationId1 = ''E''
BEGIN
-- IF #AllocStatus <> 5
INSERT INTO'+ #AreaSchema+'.[SN_PLANNING_FGAllocation]
(FGSupplyId,FGHierarchyId,MerchantNo,PlannedQty,Year,Month,Season,Status,IsActive,CreatedBy,UpdatedBy,CreatedOn,UpdatedOn,ProductNo,LocationNo)
VALUES(#FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#Year,#Month,#Season,0,1,#UpdatedUser,#UpdatedUser,GETDATE(),GETDATE(),#ProductNo,#LocationNo)
END
ELSE
BEGIN
-- IF #AllocStatus <> 5
UPDATE ' + #AreaSchema + '.[SN_PLANNING_FGAllocation]
SET PlannedQty = #PlannedQty ,ConfirmedQty=#ConfirmdQty,UpdatedBy=#UpdatedUser, UpdatedOn=GETDATE()
WHERE FGAllocationId = #FGAllocationId1
END
FETCH NEXT
FROM Allocations
INTO #FGSupplyId,#HierarchyId,#MerchantId,#PlannedQty,#ConfirmdQty,#Year,#Month,#Season,#ProductNo,#LocationNo
END
CLOSE Allocations
DEALLOCATE Allocations
FETCH NEXT
FROM Supplys
INTO #FGSupplySeqId,#FGSupplyId
END
CLOSE Supplys
DEALLOCATE Supplys
END'
print #InsertOne
Make sure your variable don't contain quotes or double quote them.
declare #value nvarchar(100) = 'abcd''efgh'
declare #sql nvarchar(max)
-- tons of QUOTE !!!
Set #sql = N'select ''' + REPLACE(#value, '''', '''''') + '''';
print #sql
exec sp_executesql #sql
Put brackets around your object names and at least one space between into and DB:
INSERT INTO'+ #AreaSchema+'.[SN_PLANNING_FGAllocation]
-- space is missing
or you can use the QUOTENAME function QUOTENAME(#AreaSchema)
Use N'' and NVARCHAR(...) or '' and VARCHAR() but don't mix
You declare everything as NVARCHAR and then concatenate it with ''. It should be N''. You also convert/cast everything as VARCHAR. It should be NVARCHAR(...).
Dynamic SQL use a NVARCHAR string.
Make sure variable are set or have a default value
SET #BucketId = '+ CAST(#BucketId AS nVARCHAR) + '
This will return NULL because #BucketId is not set in your stored procedure. Either use a ISNULL, set it or give it a default value
You should use EXEC sp_executesql and declare variables when they are used as such
https://msdn.microsoft.com/fr-fr/library/ms188001(v=sql.120).aspx
EXEC sp_executesql #yourquery, N'#UserID int, ...', #userID = #UserId ...
Keep concatenation only when it is needed (dynamic object names)
Finally
Before trying to execute it, make sure your print output the correct query (print only show the 1st 4000 characters). If the query is incomplete, it probably means that your have an error in the code near the line where it stop.
I tried your proc and it returns nothing (NULL) the way it is unless I first init all the variables. There are also truncations because you do not use N''

SQL Server trigger's cursor is not working

I created a cursor inside a trigger and it is not working properly. Please help me fix it
Create trigger Posts_Raw_To_Queue_Trigger ON SendNotificationPostsRaw FOR INSERT
AS
BEGIN
DECLARE #PostID uniqueidentifier
DECLARE #UserID uniqueidentifier
DECLARE #ProfID int
DECLARE #Email nvarchar(100)
DECLARE #CreationTime datetime
DECLARE #SpecialityID int
SELECT #ProfID= ProfessionalID,#Email= Email from Professionals where UserID=#UserID
SELECT #PostID = I.PostID,#UserID = I.UserID ,#CreationTime =I.CreationTime FROM INSERTED I
DECLARE post_relation_cursor CURSOR FOR select CategoryId from PostCategoryRelations where PostId=#PostID;
OPEN post_relation_cursor;
FETCH NEXT FROM post_relation_cursor INTO #SpecialityID
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO SendNotificationPostsQueue (UserID,PostID,SpecialityID,TemplateID,CreationTime,SendTime,JSONParameters) Values(#UserID,#PostID,1,1,'04/11/2013','04/11/2013','')
FETCH NEXT FROM post_relation_cursor INTO #SpecialityID;
END;
CLOSE post_relation_cursor;
DEALLOCATE post_relation_cursor;
END
If I remove cursor and insert dummy values into SendNotificationPostsQueue, it works. So there is problem with my cursor... Please tell me why cursor is not working?
It doesn't appear that you need to use a cursor at all and would be better off not using one in almost all cases. Simply replace the body of your trigger (the part between begin and end) with a standard insert:
INSERT INTO SendNotificationPostsQueue (UserID,PostID,SpecialityID,TemplateID,CreationTime,SendTime,JSONParameters)
SELECT
i.UserID,
i.PostID,
1,
1,
'04/11/2013', -- Might want i.CreationTime or current_timestamp
'04/11/2013',
''
FROM INSERTED i
-- possibly want "LEFT JOIN Professionals p on i.UserID = p.UserID" here to grab other info
Note how I am not using the values clause of an insert which can only insert one row. I'm putting a select statement as part of the insert, thus inserting as many rows as the select returns. This means that we don't need to use a cursor, and don't need a bunch of variables to power the cursor.
One issue with your current code, as #automatic has mentioned, is that you're assuming INSERTED only holds one row. If it has more than one, then you'll throw an error when you try to assign a column to a variable.
For reasons of elegance, maintainability, and performance, I strongly urge you to abandon this cursor and run a simple insert (since that's all that your cursor is doing anyway).
Possible problem is here -
CREATE TRIGGER dbo.Posts_Raw_To_Queue_Trigger
ON SendNotificationPostsRaw
-- for view
INSTEAD OF INSERT
-- OR
-- for table
AFTER INSERT
AS BEGIN
DECLARE
#PostID UNIQUEIDENTIFIER
, #UserID UNIQUEIDENTIFIER
, #ProfID INT
, #Email NVARCHAR(100)
, #CreationTime DATETIME
, #SpecialityID INT
SELECT #ProfID = ProfessionalID
, #Email = Email
FROM Professionals
WHERE UserID = #UserID
-- this posible return invalid result (random record from inserted sequence)
SELECT #PostID = I.PostID
, #UserID = I.UserID
, #CreationTime = I.CreationTime
FROM INSERTED I
DECLARE post_relation_cursor CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT CategoryID
FROM dbo.PostCategoryRelations
WHERE PostId = #PostID;
OPEN post_relation_cursor;
FETCH NEXT FROM post_relation_cursor INTO #SpecialityID
WHILE ##FETCH_STATUS=0 BEGIN
INSERT INTO SendNotificationPostsQueue (
UserID
, PostID
, SpecialityID
, TemplateID
, CreationTime
, SendTime
, JSONParameters
)
SELECT
#UserID
, #PostID
, #SpecialityID --- !!!
, 1
, '04/11/2013'
, '04/11/2013'
, ''
FETCH NEXT FROM post_relation_cursor INTO #SpecialityID;
END;
CLOSE post_relation_cursor;
DEALLOCATE post_relation_cursor;
END
Update
If I understand you correctly, the business logic must be like this:
CREATE TRIGGER dbo.Posts_Raw_To_Queue_Trigger
ON dbo.SendNotificationPostsRaw
[INSTEAD OF]/[AFTER] INSERT
AS BEGIN
SET NOCOUNT ON;
DECLARE
#PostID UNIQUEIDENTIFIER
, #UserID UNIQUEIDENTIFIER
, #ProfID INT
, #Email NVARCHAR(100)
, #CreationTime DATETIME
, #SpecialityID INT
DECLARE cur CURSOR LOCAL READ_ONLY FAST_FORWARD FOR
SELECT
i.PostID
, i.UserID
, ProfID = p.ProfessionalID
, p.Email
, i.CreationTime
, pcr.CategoryID
FROM INSERTED i
JOIN dbo.Professionals p ON i.UserID = p.UserID
JOIN dbo.PostCategoryRelations pcr ON i.PostID = pcr.PostID
OPEN cur
FETCH NEXT FROM cur INTO
#PostID
, #UserID
, #ProfID
, #Email
, #CreationTime
, #SpecialityID
WHILE ##FETCH_STATUS = 0 BEGIN
INSERT INTO dbo.SendNotificationPostsQueue
(
UserID
, PostID
, SpecialityID
, TemplateID
, CreationTime
, SendTime
, JSONParameters
)
SELECT
#UserID
, #PostID
, #SpecialityID
, 1
, #CreationTime
, #CreationTime
, ''
FETCH NEXT FROM cur INTO
#PostID
, #UserID
, #ProfID
, #Email
, #CreationTime
, #SpecialityID
END
CLOSE cur
DEALLOCATE cur
END

Pivoting SQL Server 2005 for unknown number of rows

My table is a dynamic one. E.g.:
id SUBJECT
1 his
2 math
3 sci
4 opt
5 ENG
6 SOC
The number of rows is not limited. There could be a hundred. I want output like this:
ID 1 2 3 4 5 6
HIS MATH SCI OPT ENG SOC
I could use a pivot query, but I would have to know the number of columns. How can I do this without knowing the number of columns in advance?
i got an answer but it's very tricky
create a table for all your records
count the records
create a table with that much number of columns
create a comma separated variable for the table which has records
then split the comma separated variables into multiple columns
here is the code
DECLARE #HEADDESC NVARCHAR(150)
DROP TABLE #HEADS
CREATE TABLE #HEADS
(
ID INT IDENTITY
,HEADS NVARCHAR(150)
,NU INT
)
DECLARE #NO INT;
SET #NO = 0
DECLARE C1 CURSOR FOR (
SELECT HEADDESC
FROM GMC.FEEHEAD_MASTER
WHERE CODE = 'GF' AND HEADDESC <> '')
OPEN C1
FETCH NEXT FROM C1 INTO #HEADDESC
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #HEADDESC
SET #NO = #NO+1
INSERT INTO #HEADS(HEADS,NU)
VALUES(#HEADDESC,#NO)
FETCH NEXT FROM C1 INTO #HEADDESC
END
--SELECT * FROM #HEADS
CLOSE C1
DEALLOCATE C1
DECLARE #COLNO INT
SET #COLNO = (SELECT COUNT(*) FROM #HEADS)
DECLARE #COLUMNS VARCHAR(8000)
SELECT #COLUMNS = COALESCE(#COLUMNS +','+ CAST(HEADS AS VARCHAR) ,
CAST(HEADS AS VARCHAR))
FROM #HEADS
--GROUP BY HEADS
DECLARE #value NVARCHAR(100)
SET #value = ',1,STUDENTIDNO,STUDENTNAME,'
SET #COLUMNS = #VALUE+#COLUMNS
SET #COLNO = #COLNO+4
--SELECT #COLUMNS
DROP TABLE #HEADSCOMMA
CREATE TABLE #HEADSCOMMA(HEADS NVARCHAR(3000))
INSERT INTO #HEADSCOMMA VALUES (#COLUMNS)
DROP TABLE #TEMP
CREATE TABLE #TEMP(COL1 NVARCHAR(1000))
DECLARE #SQL NVARCHAR(MAX)
DECLARE #COL NVARCHAR(1000)
DECLARE #COL1 INT
DECLARE #COLNAME NVARCHAR(1000)
SET #COL1 = 2
SET #COL = 'COL'
PRINT #COL1
--SET #COLNAME = #COL +CAST(#COL1 AS NVARCHAR(10))
WHILE #COL1 < =#COLNO
BEGIN
SET #COLNAME = #COL +CAST(#COL1 AS NVARCHAR(100))
PRINT #COLNAME
SET #SQL = 'ALTER TABLE #TEMP ADD '+#COLNAME+' NVARCHAR(100)'
EXEC(#SQL)
SET #COL1= #COL1+1
END
--SELECT * FROM #HEADSCOMMA -- COMMA SEPERATED VALUES
DECLARE #S VARCHAR(8000), #DATA VARCHAR(8000)
--DROP TABLE #NORMALISEDTABLE
--CREATE TABLE #NORMALISEDTABLE (HEADS NVARCHAR(200))
SELECT #S=''
WHILE EXISTS (SELECT * FROM #HEADSCOMMA WHERE HEADS>#S)
BEGIN
SELECT #S=HEADS FROM #HEADSCOMMA WHERE HEADS>#S
PRINT #S
SELECT #DATA=''''+REPLACE(#S,',',''',''')+''''
PRINT #DATA
INSERT INTO #TEMP
EXEC('SELECT '+#DATA)
END
SELECT * FROM #temp
will give the records
Dynamic SQL is an option.