How to DISPLAY horizontal data into vertical columns in SQL? - sql-server

I have tables Employees, Documents, Transactions, from_to table.
Employee can send a document to other employee and send a copy of the document to other employee.
data will be displayed in transaction table as follow:
TransId - Document Id - EmployeeId - from_toId
1 1 5 1 (From)
2 1 6 2 (To)
3 1 10 2 (CC)
Now; I want the data above to be displayed as follow:
DocId From To CC
1 Jo(5) Fo(6) Do(10)
I know that we need something to do with "Pivot Table". BUT I DON'T KNOW HOW.
Waiting for your feedback.

I have a solution with a Cursor which is dynamic:
CREATE TABLE #PRERESULT(
[TransId] int,
DocumentID int ,
EmployeeId int ,
from_toId [nvarchar](10)
)
INSERT INTO #PRERESULT ([TransId],DocumentID,EmployeeId,from_toId)
VALUES
(1,1,5,'1 (From)'),
(2,1,6,'2 (To)'),
(3,1,10,'2 (CC)')
CREATE TABLE #RESULT (
DocID int,
[From] nvarchar(15),
[To] nvarchar(15),
CC nvarchar(15))
INSERT INTO #RESULT (DocID)
SELECT DocumentID
FROM #PRERESULT
GROUP BY DocumentID
DECLARE #Documentid int,#Employee int, #Alias nvarchar(10),#SQL nvarchar(250)
DECLARE C_FromTo CURSOR
FOR
SELECT DocumentID,EmployeeID
FROM #PRERESULT
OPEN C_FromTo
FETCH NEXT FROM C_FromTo INTO #Documentid, #Employee
While (##Fetch_status = 0)
BEGIN
SET #Alias = (SELECT SUBSTRING(from_toId,PATINDEX('%(%',from_toId)+1,(LEN(from_toId)-PATINDEX('%(%',from_toId)-1)) FROM #PRERESULT WHERE #Employee = EmployeeId)
SET #SQL = 'UPDATE #RESULT
SET ['+#Alias+'] = '+Convert(nvarchar(50),#Employee)+'
WHERE '+Convert(nvarchar(50),#Documentid)+' = DocID'
EXEC (#SQL)
FETCH NEXT FROM C_FromTo INTO #Documentid, #Employee
END
CLOSE C_FromTo
DEALLOCATE C_FromTO
SELECT * FROM #RESULT
DROP TABLE #PRERESULT
DROP TABLE #RESULT
Gives you this:
DocID |From |To |CC
1 |5 |6 |10
Hope this will help you
Have a nice day & Greets from Switzerland
Etienne

I think this reflects your table, although I am using names instead of IDs for the last two columns:
CREATE TABLE [dbo].[Transaction](
[TransId] [int] NOT NULL,
[DocId] [int] NOT NULL,
[EmpId] [nvarchar](10) NOT NULL,
[FromToId] [nchar](10) NOT NULL
) ON [PRIMARY]
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(1,1,'Jo','From')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(2,1,'Fo','To')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(3,1,'Do','CC')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(4,2,'Jo','From')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(5,2,'Bo','To')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(6,2,'Zo','CC')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(7,3,'Bo','From')
INSERT INTO [Transaction] ([TransId],[DocId],[EmpId],[FromToId])VALUES(8,3,'Go','To')
Then this query will give you the results you requested:
SELECT DISTINCT
t.DocId
,x.[From]
,y.[To]
,z.Cc
FROM [Transaction] t
LEFT JOIN
(
SELECT DocId
,CASE WHEN FromToId = 'From' THEN EmpId END AS [From]
FROM [Transaction]
WHERE CASE WHEN FromToId = 'From' THEN EmpId END IS NOT NULL
) x ON t.DocId = x.DocId
LEFT JOIN
(
SELECT DocId
,CASE WHEN FromToId = 'To' THEN EmpId END AS [To]
FROM [Transaction]
WHERE CASE WHEN FromToId = 'To' THEN EmpId END IS NOT NULL
) y ON t.DocId = y.DocId
LEFT JOIN
(
SELECT DocId
,CASE WHEN FromToId = 'CC' THEN EmpId END AS [Cc]
FROM [Transaction]
WHERE CASE WHEN FromToId = 'Cc' THEN EmpId END IS NOT NULL
) z ON t.DocId = z.DocId
DocId From To Cc
1 Jo Fo Do
2 Jo Bo Zo
3 Bo Go NULL

Related

SQL Server cursor - cannot assign variable properly [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I use SQL Server 2016. I need to move users from a legacy system to a new one. But before bringing all users from the legacy system, I need to check if they do not already exist in our new system. Such a determination is done based on comparison of users' email addresses in each of the system.
So if a user does not exist in the new system, I need to insert such a record into the new system from the legacy one. But if user does exist, I need to update some columns in the new system with data from the legacy one.
To accomplish it I'm using a cursor with SCOPE_IDENTITY().
Also I need to log newly inserted and existing updated records in each cursor iteration. The problem is that my code does not differentiate new and existing records hence it inserts all user id in the Log table twice. One time in case the userID (#ContactID) IS NULL (not existing in the new system) and another when userID (#ContactID) IS NOT NULL - existing in the new system.
In my sample below I have 1 records that does not exists in the new system and 3 do match already. So in the IF (#CoantactID IS NULL) I meant to insert only that newly inserted into the MyDB.dbo.Contact table record and in the IF (#CoantactID IS NOT NULL) insert into the Log table only the tree records matched (existing) in the new system.
But the problem is that it inserts all four ContactIDs into the log table even in the this IF (#ContactID IS NULL) amd then again for in IF (#ContactID IS NOT NULL)
;USE [MyDB];
GO
-- exec dbo.sp_UserMigration_Users_Copy
DROP PROCEDURE IF EXISTS dbo.sp_UserMigration_Users_Copy
GO
;USE [MyDB];
GO
CREATE PROCEDURE dbo.sp_UserMigration_Users_Copy
#UserID UNIQUEIDENTIFIER = NULL
--,#ContactID INT
AS
DECLARE #Email nvarchar(100),
#ContactID INT;
-- #UserID UNIQUEIDENTIFIER,
DECLARE #SysID INT
SET #SysId = 17511; -- system contactID
---- creating log table
--CREATE TABLE #T
--(
-- UserID UNIQUEIDENTIFIER NOT NULL,
-- Email NVARCHAR(50) NOT NULL
--);
SELECT * INTO #T
FROM MyDB.dbo.User
WHERE UserID IN (
'0604C514',
'C1FDAF34',
'23BABE2D',
'EBA21D10'
);
IF NOT EXISTS (select * from MyDB.sys.tables where name = N'UserIDContactIDMigrationLog')
CREATE TABLE MyDB.dbo.UserIDContactIDMigrationLog
(
UserID UNIQUEIDENTIFIER /* CONSTRAINT [PK_UserIDContactIDMigrationLog_UserID] PRIMARY KEY */ NOT NULL, -- somehow inserts duplicate UserIDs
ContactId INT /*UNIQUE CONSTRAINT [UNIQUE_ContactId] */ NOT NULL,
CreatedDt DATETIME2 NULL,
UpdatedDt DATETIME2 NULL,
UpdatedFlag BIT /*CONSTRAINT [DF_MigratedFlag] DEFAULT(0) */ NULL
);
-- -----------------------------------------------------
-- Cursor: For all contacts to be migrated
-- -----------------------------------------------------
IF (SELECT CURSOR_STATUS('global','user_cursor')) >= -1
BEGIN
IF (SELECT CURSOR_STATUS('global','user_cursor')) > -1
BEGIN
CLOSE user_cursor
END
DEALLOCATE user_cursor
END
DECLARE user_cursor CURSOR
FOR
SELECT a.UserID, LTRIM(RTRIM(a.Email)) AS Email
FROM #T a WITH (NOLOCK)
--WHERE a.UserID = #UserID
-- begin cursor loop
OPEN user_cursor
FETCH NEXT FROM user_cursor INTO #UserID, #Email
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ContactID = (
SELECT c.ContactId
FROM MyDB.dbo.Contact c
LEFT OUTER JOIN MyDB.dbo.Login l ON c.ContactId = l.ContactId
WHERE (LTRIM(RTRIM(c.ContactEmailTx)) = #Email
OR LTRIM(RTRIM(l.ContactLoginNameTx)) = #Email
--OR l.ContactLoginNameTx LIKE #Email+'%' -- this concept does not work in case of leading space at the ContactLoginNameTx so need to use the code below
OR SUBSTRING(LTRIM(RTRIM(l.ContactLoginNameTx)), -1, CHARINDEX('.INACTIVE.', l.ContactLoginNameTx)) = #Email)
AND (c.ContactEmailTx != '' OR c.ContactEmailTx IS NOT NULL OR l.ContactLoginNameTx != '' OR l.ContactLoginNameTx IS NOT NULL)
)
-- check if a contact is new
IF (#ContactID IS NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Contact]
(
[ContactFirstNameTx],
[ContactLastNameTx],
[ContactEmailTx],
[ContactTitleTx],
[ContactCreateDt],
[ContactCreateByID],
[ContactCreateLoginTypeID]
)
SELECT
[FirstName],
[LastName],
[Email],
[Title],
--[AccountId],
[UserCreatedDate],
#SysId AS [ContactCreateByID], -- as as flag for import [ContactCreateLoginTypeID]
-10 AS [ContactCreateLoginTypeID]
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
SET #ContactID = SCOPE_IDENTITY()
END
-- select * from [MyDB].[dbo].[Contact] where contactid in ( 1051364, 466440, 560466, 618576)
-- -----------------------------------------------------
--MyDB.dbo.Login
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Login]
(
ContactID,
[ContactLoginNameTx], -- V.C. this is email. the field is has a unique constraint
[ContactLoginActiveIn],
[ContactLoginCreateDt],
[ContactLoginCreateByID],
[ContactLoginCreateLoginTypeID],
[ContactLoginPasswordLastChangeDt],
[ContactLoginLastLoginDt],
[ContactLoginPasswordExclusionIn] -- this is not nullable and BIT
)
SELECT
#ContactID AS ContactID,
[Email],
CASE WHEN [UserStatusID] IN (1, 3) THEN 1 ELSE 0 END AS [ContactLoginActiveIn], -- v.c. in Onvia 1 is Active, 2 - Inactive, 3 - Invited, 4 - Inactive By System, 5 - Pending Registration
[UserCreatedDate],
#SysId AS ContactLoginCreateByID, -- as as flag for Onvia import[ContactCreateLoginTypeID]
-10 AS [ContactLoginCreateLoginTypeID],
[UserLastPasswordChangedDate],
[UserLastLoginDate],
0 as ContactLoginPasswordExclusionIn -- this is not nullable and BIT
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
AND NOT EXISTS (select * from MyDB.dbo.Login z where z.ContactID = #ContactID)
-- -----------------------------------------------------
-- Activate any inactive users
-- -----------------------------------------------------
IF EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 0 and ContactID = #ContactID)
AND NOT EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 1 and ContactID = #ContactID)
BEGIN
UPDATE [MyDB].[dbo].[Login]
SET ContactLoginNameTx = #Email,
ContactLoginActiveIn = (select case when [UserStatusID] IN (1, 3) then 1 else 0 end
from #T o where o.UserID = #UserID)
WHERE ContactID = #ContactID
END
-- -----------------------------------------------------
--MyDB.dbo.ContactPhoneNumber
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[PhoneNumber]
(
ContactID,
PhoneNumberTypeID,
ContactPhoneNumberValueTx
)
SELECT
#ContactID AS ContactID,
1 AS PhoneNumberTypeID,
ISNULL([PhoneNumber1],'') AS ContactPhoneNumberValueTx
FROM #T o WITH (NOLOCK)
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[PhoneNumber] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
--MyDB.dbo.ContactOrg
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Org]
(
ContactID,
OrgID
)
SELECT
#ContactID AS ContactID,
b.OrgID AS OrgID
FROM #T o WITH (NOLOCK)
INNER JOIN ImportQueue.dbo.OnviaAccountIDOrgIDMigrationLog b WITH (NOLOCK) ON o.AccountId = b.OnviaAccountID
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[Org] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for new users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
CreatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
0
)
-- -----------------------------------------------------
-- EXISTING MATCHED USERS
-- -----------------------------------------------------
-- check if a contact is existing
IF (#ContactID IS NOT NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
UPDATE MyDB.dbo.Contact
SET ContactFirstNameTx =
CASE
WHEN ContactFirstNameTx NOT IN ('', NULL) THEN c.FirstName
ELSE ContactFirstNameTx
END,
ContactLastNameTx =
CASE
WHEN ContactLastNameTx IN ('', NULL) THEN c.LastName
ELSE ContactLastNameTx
END,
ContactEmailTx =
CASE
WHEN ContactEmailTx IN ('', NULL) THEN c.Email
ELSE ContactEmailTx
END,
ContactTitleTx =
CASE
WHEN ContactTitleTx IN ('', NULL) THEN c.Title
ELSE ContactTitleTx
END,
[ContactCreateDt] =
CASE
WHEN ContactCreateDt IN ('', NULL) THEN c.UserCreatedDate
ELSE ContactCreateDt
END,
[ContactModifyByID] = #SysId,
[ContactModifyLoginTypeID] = - 10
FROM #T c
WHERE UserID = #UserID
AND ContactID = #ContactID;
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for existing matched users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
UpdatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
1
);
FETCH NEXT FROM user_cursor INTO #UserID, #Email
-- end cursor loop
END
END
CLOSE user_cursor
DEALLOCATE user_cursor
GO
Instead of Cursors try the below code.
- exec dbo.sp_UserMigration_Users_Copy
DROP PROCEDURE IF EXISTS dbo.sp_UserMigration_Users_Copy
GO
;USE [MyDB];
GO
CREATE PROCEDURE dbo.sp_UserMigration_Users_Copy
#UserID UNIQUEIDENTIFIER = NULL
--,#ContactID INT
AS
DECLARE #Email nvarchar(100),
#ContactID INT;
DECLARE #SysID INT
SET #SysId = 17511;
CREATE TABLE #T
(
ID INT IDENTITY(1,1) NOT NULL,
UserID NVARCHAR(50)NOT NULL,
Email NVARCHAR(50) NOT NULL
);
INSERT INTO #T
SELECT *
FROM MyDB.dbo.User
WHERE UserID IN (
'0604C514',
'C1FDAF34',
'23BABE2D',
'EBA21D10'
);
IF NOT EXISTS (select * from MyDB.sys.tables where name = N'UserIDContactIDMigrationLog')
CREATE TABLE MyDB.dbo.UserIDContactIDMigrationLog
(
UserID UNIQUEIDENTIFIER /* CONSTRAINT [PK_UserIDContactIDMigrationLog_UserID] PRIMARY KEY */ NOT NULL, -- somehow inserts duplicate UserIDs
ContactId INT /*UNIQUE CONSTRAINT [UNIQUE_ContactId] */ NOT NULL,
CreatedDt DATETIME2 NULL,
UpdatedDt DATETIME2 NULL,
UpdatedFlag BIT /*CONSTRAINT [DF_MigratedFlag] DEFAULT(0) */ NULL
);
DECLARE #COUNT INT
DECLARE #I INT = 1
DECLARE #UserID NVARCHAR(50) ;
DECLARE #Email NVARCHAR(50)
SELECT #COUNT = COUNT(*) FROM #T
WHILE(#I <= #COUNT)
BEGIN
SELECT #UserID = UserID, #Email= LTRIM(RTRIM(Email)) FROM #T WHERE ID = #I
SELECT #ContactID = (
SELECT c.ContactId
FROM MyDB.dbo.Contact c
LEFT OUTER JOIN MyDB.dbo.Login l ON c.ContactId = l.ContactId
WHERE (LTRIM(RTRIM(c.ContactEmailTx)) = #Email
OR LTRIM(RTRIM(l.ContactLoginNameTx)) = #Email
--OR l.ContactLoginNameTx LIKE #Email+'%' -- this concept does not work in case of leading space at the ContactLoginNameTx so need to use the code below
OR SUBSTRING(LTRIM(RTRIM(l.ContactLoginNameTx)), -1, CHARINDEX('.INACTIVE.', l.ContactLoginNameTx)) = #Email)
AND (c.ContactEmailTx != '' OR c.ContactEmailTx IS NOT NULL OR l.ContactLoginNameTx != '' OR l.ContactLoginNameTx IS NOT NULL)
)
-- check if a contact is new
IF (#ContactID IS NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Contact]
(
[ContactFirstNameTx],
[ContactLastNameTx],
[ContactEmailTx],
[ContactTitleTx],
[ContactCreateDt],
[ContactCreateByID],
[ContactCreateLoginTypeID]
)
SELECT
[FirstName],
[LastName],
[Email],
[Title],
--[AccountId],
[UserCreatedDate],
#SysId AS [ContactCreateByID], -- as as flag for Onvia import[ContactCreateLoginTypeID]
-10 AS [ContactCreateLoginTypeID]
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
SET #ContactID = SCOPE_IDENTITY()
END
-- select * from [MyDB].[dbo].[Contact] where contactid in ( 1051364, 466440, 560466, 618576)
-- -----------------------------------------------------
--MyDB.dbo.Login
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Login]
(
ContactID,
[ContactLoginNameTx], -- V.C. this is email. the field is has a unique constraint
[ContactLoginActiveIn],
[ContactLoginCreateDt],
[ContactLoginCreateByID],
[ContactLoginCreateLoginTypeID],
[ContactLoginPasswordLastChangeDt],
[ContactLoginLastLoginDt],
[ContactLoginPasswordExclusionIn] -- this is not nullable and BIT
)
SELECT
#ContactID AS ContactID,
[Email],
CASE WHEN [UserStatusID] IN (1, 3) THEN 1 ELSE 0 END AS [ContactLoginActiveIn], -- v.c. in Onvia 1 is Active, 2 - Inactive, 3 - Invited, 4 - Inactive By System, 5 - Pending Registration
[UserCreatedDate],
#SysId AS ContactLoginCreateByID, -- as as flag for Onvia import[ContactCreateLoginTypeID]
-10 AS [ContactLoginCreateLoginTypeID],
[UserLastPasswordChangedDate],
[UserLastLoginDate],
0 as ContactLoginPasswordExclusionIn -- this is not nullable and BIT
FROM #T o WITH (NOLOCK)
WHERE UserID = #UserID
AND NOT EXISTS (select * from MyDB.dbo.Login z where z.ContactID = #ContactID)
-- -----------------------------------------------------
-- Activate any inactive users
-- -----------------------------------------------------
IF EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 0 and ContactID = #ContactID)
AND NOT EXISTS (select * from MyDB.dbo.Login where ContactLoginActiveIn = 1 and ContactID = #ContactID)
BEGIN
UPDATE [MyDB].[dbo].[Login]
SET ContactLoginNameTx = #Email,
ContactLoginActiveIn = (select case when [UserStatusID] IN (1, 3) then 1 else 0 end
from #T o where o.UserID = #UserID)
WHERE ContactID = #ContactID
END
-- -----------------------------------------------------
--MyDB.dbo.ContactPhoneNumber
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[PhoneNumber]
(
ContactID,
PhoneNumberTypeID,
ContactPhoneNumberValueTx
)
SELECT
#ContactID AS ContactID,
1 AS PhoneNumberTypeID,
ISNULL([PhoneNumber1],'') AS ContactPhoneNumberValueTx
FROM #T o WITH (NOLOCK)
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[PhoneNumber] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
--MyDB.dbo.ContactOrg
-- -----------------------------------------------------
INSERT INTO [MyDB].[dbo].[Org]
(
ContactID,
OrgID
)
SELECT
#ContactID AS ContactID,
b.OrgID AS OrgID
FROM #T o WITH (NOLOCK)
INNER JOIN ImportQueue.dbo.OnviaAccountIDOrgIDMigrationLog b WITH (NOLOCK) ON o.AccountId = b.OnviaAccountID
WHERE o.UserID = #UserID
AND NOT EXISTS (select * from [MyDB].[dbo].[Org] z where z.ContactID = #ContactID);
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for new users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
CreatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
0
)
-- -----------------------------------------------------
-- EXISTING MATCHED USERS
-- -----------------------------------------------------
-- check if a contact is existing
IF (#ContactID IS NOT NULL)
BEGIN
-- -----------------------------------------------------
-- MyDB.dbo.Contact
-- -----------------------------------------------------
UPDATE MyDB.dbo.Contact
SET ContactFirstNameTx =
CASE
WHEN ContactFirstNameTx NOT IN ('', NULL) THEN c.FirstName
ELSE ContactFirstNameTx
END,
ContactLastNameTx =
CASE
WHEN ContactLastNameTx IN ('', NULL) THEN c.LastName
ELSE ContactLastNameTx
END,
ContactEmailTx =
CASE
WHEN ContactEmailTx IN ('', NULL) THEN c.Email
ELSE ContactEmailTx
END,
ContactTitleTx =
CASE
WHEN ContactTitleTx IN ('', NULL) THEN c.Title
ELSE ContactTitleTx
END,
[ContactCreateDt] =
CASE
WHEN ContactCreateDt IN ('', NULL) THEN c.UserCreatedDate
ELSE ContactCreateDt
END,
[ContactModifyByID] = #SysId,
[ContactModifyLoginTypeID] = - 10
FROM #T c
WHERE UserID = #UserID
AND ContactID = #ContactID;
-- -----------------------------------------------------
---- MyDB.dbo.UserIDContactIDMigrationLog
---- log output for existing matched users
-- -----------------------------------------------------
INSERT INTO MyDB.dbo.UserIDContactIDMigrationLog
(
UserID,
ContactId,
UpdatedDt,
UpdatedFlag
)
VALUES
(
#UserID,
#ContactID,
GETDATE(),
1
);
END
SET #I = #I+1;
END

Cursor poor performance issue

I have a query which runs very slow. After some observations, I feel that it is because of using cursor in the query.
CREATE PROCEDURE [dbo].[spTest] (#INwww varchar(6)) AS
declare #curwwwbegdate datetime
declare #prevwwwbegdate datetime
declare #PrevwwwNum varchar(6)
set NOCOUNT ON
set #CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = #Inwww)
set #PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < #curwwwbegdate
order by begindate desc)
CREATE TABLE #ContRevExp (Inum int, lwww varchar(6),BookNum varchar(13), ldate datetime, lllLoc varchar(8), currentrev varchar(20), currentexp varchar(20), oldrev varchar(20), oldexp varchar(20), ContFileLoc varchar(100), bookloc varchar(3))
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
create table #LocTable (loc varchar(3))
insert into #LocTable
SELECT LocCode from Location WHERE STATUS = 1 AND CCC = 1
DECLARE LocCursor INSENSITIVE CURSOR FOR
select * from #LocTable
OPEN LocCursor
declare #Loc varchar(3)
FETCH NEXT FROM LocCursor into #Loc
WHILE (##FETCH_STATUS = 0)
BEGIN
insert into #wwwdates (wdLoc, wdwww, wdBegDate, wdEndDate)
select #Loc,#Inwww,
(select top 1 wwwllldate from cccswwwllled where lllwww = #PrevwwwNum and branch = #Loc),
(select top 1 wwwllldate from cccswwwllled where lllwww = #Inwww and branch = #Loc)
FETCH NEXT FROM LocCursor into #Loc
END
deallocate LocCursor
Drop table #LocTable
DECLARE LocwwwCursor INSENSITIVE CURSOR FOR
select wdLoc, wdBegDate, wdEndDate from #wwwdates
declare #INFILELOC as varchar(3)
declare #INBEGDATE datetime
declare #INENDDATE datetime
OPEN LocwwwCursor
FETCH NEXT FROM LocwwwCursor into #INFILELOC, #INBEGDATE, #INENDDATE
WHILE (##FETCH_STATUS = 0)
BEGIN
declare #INVNUM int
declare #INDATE datetime
create table #cccs (cInvNum int)
insert into #cccs
Select distinct cccInv_Num from cccswwwllled
left join cccsexport on cccsExport.inv_num = cccinv_num
where cccswwwllled.Branch = #INFILELOC
and (cccswwwllled.impexp <> 1 or cccswwwllled.impexp is null)
and lllwww < #INwww and ContBookIndicator = 'C'
and cccsexport.lastupdate >= #INBEGDATE and cccsexport.lastupdate < #INENDDATE
DECLARE cccCursor INSENSITIVE CURSOR FOR
select * from #cccs
OPEN cccCursor
declare #cnum int
FETCH NEXT FROM cccCursor into #cnum
WHILE (##FETCH_STATUS = 0)
BEGIN
insert into #ContRevExp (Inum , lwww ,BookNum, ldate , lllloc , currentrev , currentexp, oldrev, oldexp, contfileloc, bookloc )
select top 1 cccInv_Num, lllwww,
(Select top 1 cccNumber from cccsExport where inv_num = #cnum and lastupdate <= #INENDDATE order by lastupdate desc ),
wwwlllDate,CE.FileNum,
0,
--case when CE.STATUS = 0 then '0' else convert(varchar(20),CE.TotalCost + CE.AllinRpoCost) end,
case when CE.STATUS = 0 then '0' else convert(varchar(20),CE.TotalExpense) end,
0,
--(Select top 1 convert(varchar(20),TotalCost + Allinrpocost) from cccsExport where inv_num = #cnum and lastupdate <= #INBEGDATE order by lastupdate desc ),
(Select top 1 convert(varchar(20),TotalExpense) from cccsExport where inv_num = #cnum and lastupdate <= #INBEGDATE order by lastupdate desc ),
'Cont/File/Loc',
BookLocation from cccswwwllled as CWL
left join cccsexport as CE on CE.inv_num = CWL.cccInv_Num
where CWL.cccInv_Num = #cnum
and CWL.lllwww < #INwww and CWL.ContBookIndicator = 'C'
and CE.lastupdate >= #INBEGDATE and CE.lastupdate <= #INENDDATE
order by CE.lastupdate desc
FETCH NEXT FROM cccCursor into #cnum
END
deallocate cccCursor
drop table #cccs
FETCH NEXT FROM LocwwwCursor into #INFILELOC, #INBEGDATE, #INENDDATE
END
deallocate LocwwwCursor
drop table #wwwdates
select bookloc,lwww,booknum,Inum,oldrev,oldexp,currentrev,currentexp,lllloc,
case when contfileloc is null then 'NOT' ELSE contfileloc end as 'Cont' from #ContRevExp
where (currentrev <> oldrev) or (currentexp <> oldexp)
order by bookloc, booknum
drop table #ContRevExp
I am not strong on cursor. My question is how to modify the code to improve the performance?
EDIT
The real problem is that I use ado.net to retrieve the data in a web application, the entire time to download data from server to the client is about 10 seconds, By using telerik justtrace tool, I found the stored procedure spent 99% time.
In the query, I found there is a nest cursors usage. cccCursor and LocwwwCursor. It means that there are two while loops, one is inside the other one. I guess that is the main reason, so I hope to replace the cursors with other skills.
SAMPLE DATA:
Because there are a lot of tables. I try to make the logic clear. We get the data from table Location:
The temp table #LocTable only has one column. The sample data is
AAA
BBB
CCC
DDD
EEE
FFF
GGG
I believe LocCursor is dealing each value of above data.
The input of the stored procedure is Inwww, let say it is 200218.
We have the query set #CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = #Inwww) will return a datetime 2002-04-28 00:00:00.000.
Another query set #PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < #curwwwbegdate
order by begindate desc) will return 200217. The temp table #wwwdates has the sample data as below.
wdLoc wdWeek wdBegDate wdEndDate
AAA 200218 NULL NULL
BBB 200218 NULL NULL
CCC 200218 NULL NULL
DDD 200218 2002-05-06 13:39:31.000 2002-05-07 16:52:44.000
EEE 200218 NULL NULL
FFF 200218 2002-05-06 13:39:40.000 2002-05-07 16:53:42.000
GGG 200218 NULL NULL
I think that the cursor LocwwwCursor takes the value from the temp table #wwwdatae then insert #cccs. I haven't got the sample data yet.
The final result likes(I just demonstrate it with one row)
ARL ARL 200510 IETARL0510087 150547816 $0.00 155.21 $0.00 155.00 ARL SOMETHING
There are unnecessary loops for aggerates in the SP above. Most of your logic could be condensed and made more performant with grouping and aggregations. For example, The whole block below could be drastically simplified.
REPLACE THIS ....
declare #prevwwwbegdate datetime
declare #PrevwwwNum varchar(6)
set NOCOUNT ON
set #CurwwwBegDate = (select Begindate from wwwNUM where wwwnumber = #Inwww)
set #PrevwwwNum = (SELECT TOP 1 wwwNUMBER from wwwNUM WHERE BEGINDATE < #curwwwbegdate
order by begindate desc)
CREATE TABLE #ContRevExp (Inum int, lwww varchar(6),BookNum varchar(13), ldate datetime, lllLoc varchar(8), currentrev varchar(20), currentexp varchar(20), oldrev varchar(20), oldexp varchar(20), ContFileLoc varchar(100), bookloc varchar(3))
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
create table #LocTable (loc varchar(3))
insert into #LocTable
SELECT LocCode from Location WHERE STATUS = 1 AND CCC = 1
DECLARE LocCursor INSENSITIVE CURSOR FOR
select * from #LocTable
OPEN LocCursor
declare #Loc varchar(3)
FETCH NEXT FROM LocCursor into #Loc
WHILE (##FETCH_STATUS = 0)
BEGIN
insert into #wwwdates (wdLoc, wdwww, wdBegDate, wdEndDate)
select #Loc,#Inwww,
(select top 1 wwwllldate from cccswwwllled where lllwww = #PrevwwwNum and branch = #Loc),
(select top 1 wwwllldate from cccswwwllled where lllwww = #Inwww and branch = #Loc)
FETCH NEXT FROM LocCursor into #Loc
END
deallocate LocCursor
Drop table #LocTable
WITH THIS ....
create table #wwwdates (wdLoc varchar(3),wdwww varchar(6), wdBegDate datetime, wdEndDate datetime)
insert into #wwwdates
SELECT
LocCode,#Inwww,MAX(Prev_MaxDate),MAX(In_MaxDate)
FROM
(
SELECT LocCode,
Prev_MaxDate=CASE WHEN cccswwwllled.lllwww=#PrevwwwNum THEN wwwllldate ELSE NULL END,
In_MaxDate=CASE WHEN cccswwwllled.lllwww=#Inwww THEN wwwllldate ELSE NULL END
from
Location
INNER JOIN cccswwwllled ON cccswwwllled.lllwww IN(#PrevwwwNum,#Inwww) AND cccswwwllled.Branch=Location.LocCode
WHERE Location.STATUS = 1 AND Location.CCC = 1
)AS X
GROUP BY
LocCode
You should remove cursors and create set-based solution. You and mssql engine are not strong in cursor solutions :).
Rething what you would like to do. Join wwwNUM + lllwww + Location into view something like + check indexes on wwwnumber, branch, LocCode + STATUS + CCC (depends on data types)
create view MyViewWithNameWhichDescriptContain
as
select
l.LocCode,
n.wwwNUMBER Inwww,
n.Begindate InwwwDate,
c.wwwllldate Inwww
from Location l, wwwNUM n, cccswwwllled c
where l.[STATUS] = 1 AND l.CCC = 1
and l.LocCode = c.branch
and c.lllwww = n.wwwnumber
select from this view:
if object_id('tempdb..#wwwMyDatesPerLocCode') is not null drop table #wwwMyDatesPerLocCode
select *
into #wwwMyDatesPerLocCode
from (
select *, row_number() over(partition by Inwww order by InwwwDate desc OrbyCol
from MyViewWithNameWhichDescriptContain) t
where OrbyCol in (1,2)
create view cccswwwllled + cccsexport lets say llledExported
make selfjoin to access descendant (wwwMyDatesPerLocCode where OrbyCol = 1 + wwwMyDatesPerLocCode where OrbyCol = 2) and connect with view llledExported to get your info with your filters
check indexes on all tables again
Finnaly you don't need variables and cursors. Try think in joins. If you will add some somple data from all tables and add description of your indexes I can make it more detailed.

How to write this procedure to get auto-increment alphanumeric id in SQL Server?

Table structure is as follows:
CREATE TABLE tblContact
(
SrNo DECIMAL IDENTITY(1,1) NOT NULL,
InquiryId VARCHAR(10) PRIMARY KEY,
SenderName VARCHAR(50),
SenderEmail VARCHAR(200),
SenderSubject VARCHAR(50),
SenderMessage VARCHAR(MAX),
IsActive BIT DEFAULT(1),
IsDelete BIT DEFAULT(0),
CreatedOn DATETIME DEFAULT(GETDATE()),
CreatedBy VARCHAR(10),
UpdatedOn DATETIME,
UpdatedBy VARCHAR(10)
)
Procedure listing is as follows:
CREATE PROC Usp_GetNewInquiryId
AS
BEGIN
IF NOT EXISTS(SELECT InquiryId FROM JobPortal.dbo.tblContact)
DECLARE #PrefixValue VARCHAR(10) = 'INQ'
DECLARE #InitialValue DECIMAL(10) = 1001
SELECT InquiryId = #PrefixValue + CAST(ISNULL(MAX(InquiryId), #InitialValue) AS VARCHAR(10))
FROM JobPortal.dbo.tblContact
ELSE
/* here I want to eliminate the word 'INQ' from 'INQ1001' towards left side and do increment 1002 from 1001, lastly want to select INQ1002 and so on... */
SELECT TOP 1
InquiryId = #PrefixValue + CONVERT(VARCHAR(10), SUBSTRING(InquiryId, 4, 4)) + 1
FROM JobPortal.dbo.tblContact
ORDER BY InquiryId DESC
END
Desired Output:
If table is empty then InquiryId=INQ1001
Otherwise InquiryId=INQ1002
If you want to return the "next available" InquiryId for tblContact, I would do this:
CREATE PROCEDURE Usp_GetNewInquiryId
AS
BEGIN
IF NOT EXISTS (SELECT InquiryId FROM tblContact)
SELECT 'INQ1001'
ELSE
SELECT TOP 1 'INQ' + CONVERT(VARCHAR,CONVERT(INT,REPLACE(InquiryId,'INQ','')) + 1) FROM tblContact ORDER BY InquiryId DESC
END

Stored procedure not accepting any value

I have written a stored procedure based on a set of process, I'm just passing a single parameter as input to the procedure but it seems it is not taking the value. But when I give input value instead of parameter in the procedure it is working.
There is no mistake in the flow of process, but seems something missing in the procedure syntax end.
below is the stored procedure I used.
ALTER PROCEDURE [TransferIn]
#ponumber NVARCHAR = NULL
AS
BEGIN
--step 1 Delete Temp Pur_ID
IF EXISTS (
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Pur_ID_IN')
DROP TABLE Pur_ID_IN;
-- =============================================
--step 2 select PO Number
--IF #ponumber IS NOT NULL
SELECT
ponumber, id
INTO Pur_ID_IN
FROM purchaseorder
WHERE potype IN (2, 4)
AND status = 0
AND ponumber = #ponumber;
-- =============================================
--step 3
--delete Temp. Tabel P_Test20_12_IN
IF EXISTS (
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'P_Test20_12_IN')
DROP TABLE P_Test20_12_IN;
-- =============================================
-- step 4 (Insert Data For Invoice To Temp Tabel After Group )
SELECT
ItemDescription, PurchaseOrderID,
SUM(QuantityOrdered) AS QuantityOrdered,
itemid, Price
INTO P_Test20_12_IN
FROM PurchaseOrderEntry
WHERE PurchaseOrderID IN (SELECT id FROM Pur_ID_IN)
GROUP BY
ItemDescription, StoreID, PurchaseOrderID,
itemid, Price;
--order by 3
-- =============================================
-- step 5 Delete Record From PurchaseOrderEntry
DELETE PurchaseOrderEntry
FROM PurchaseOrderEntry
WHERE PurchaseOrderID IN (SELECT id FROM Pur_ID_IN);
-- =============================================
INSERT INTO [W07].[dbo].[PurchaseOrderEntry] ([ItemDescription], [LastUpdated], [PurchaseOrderID], [QuantityOrdered], [ItemID], [Price])
SELECT
[ItemDescription],
GETDATE() AS [LastUpdated],
[PurchaseOrderID], [QuantityOrdered],
[ItemID], [Price]
FROM
P_Test20_12_IN;
END
Problem is
#ponumber nvarchar = null
change it to
#ponumber nvarchar(max) = null
Note:If you do NOT specify the size(char, nchar, varchar, nvarchar),
sql server will default to 1 character.
ALTER PROCEDURE [TransferIn]
(
#ponumber NVARCHAR(100)
)
AS BEGIN
SET NOCOUNT ON
IF OBJECT_ID('tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (id INT PRIMARY KEY)
INSERT INTO #temp (id)
SELECT /*DISTINCT*/ id
FROM dbo.purchaseorder
WHERE potype IN (2, 4)
AND [status] = 0
AND ponumber = #ponumber
IF OBJECT_ID('tempdb.dbo.#temp2') IS NOT NULL
DROP TABLE #temp2
SELECT ItemDescription,
PurchaseOrderID,
SUM(QuantityOrdered) AS QuantityOrdered,
itemid,
Price
INTO #temp2
FROM PurchaseOrderEntry
WHERE PurchaseOrderID IN (SELECT * FROM #temp)
GROUP BY ItemDescription,
StoreID, --?
PurchaseOrderID,
itemid,
Price;
DELETE PurchaseOrderEntry
FROM PurchaseOrderEntry
WHERE PurchaseOrderID IN (SELECT * FROM #temp)
INSERT INTO [W07].[dbo].[PurchaseOrderEntry] ([ItemDescription], [LastUpdated], [PurchaseOrderID], [QuantityOrdered], [ItemID], [Price])
SELECT [ItemDescription],
GETDATE() AS [LastUpdated],
[PurchaseOrderID],
[QuantityOrdered],
[ItemID],
[Price]
FROM #temp2
END

Use a table-value function to return data in columns instead of rows

I'm trying to write a query which will take a limited number of historical records and display the results in one row.
For example, I have a table of people:
|PersonID|Forename|Surname
|--------|--------|----------
|00000001|Andy |Cairns
|00000002|John |Smith
And a table of all their historical addresses:
|PersonID|Date |Street |Town
-------------------------------------------
|00000001|2011-01-01|Main Street |MyTown
|00000001|2010-01-01|Old Street |OldTown
|00000002|2010-01-01|Diagon Alley |London
|00000001|2009-01-01|First Street |OtherTown
etc..
I'd like to return the following:
|PersonID|Name |MoveDate1 |Town1 |MoveDate2 |Town2 |MoveDate3 |Town3
------------------------------------------------------------------------
|00000001|Andy |2011-01-01|MyTown|2010-01-01|OldTown|2009-01-01|OtherTown
|00000002|John |2010-01-01|London| | | |
At the moment, I'm using the following query:
select PersonID, Name, s.mdate, s.town
from dbo.people
cross apply dbo.getAddressList as s
And the following table-value function:
alter function [dbo].[getAddressList]
(
#personID
)
returns
#addresslisttable
(
mdate smalldatetime
town char
)
as
begin
insert into #addresslist (
mdate
town
)
select top 3 mdate, town
from dbo.addresses
where PersonID = #personID
order by mdate desc
return
end
Unfortunately, this is returning a new row for each address, like this:
|PersonID|Name|MDate |Town
|00000001|Andy|2011-01-01|MyTown
|00000001|Andy|2010-01-01|OldTown
|00000001|Andy|2009-01-01|OtherTown
How can I return each returned row in a field instead?
Thanks in advance.
Where possible you should always use inline TVFs in preference to multistatement ones.
ALTER FUNCTION [dbo].[getAddressList]
(
#personID INT
)
RETURNS TABLE
AS
RETURN
(
WITH cte AS
(SELECT TOP 3 mdate, town, ROW_NUMBER() OVER (ORDER BY mdate DESC) rn
FROM dbo.addresses
WHERE PersonID = #personID
ORDER BY mdate DESC
)
SELECT
MAX(CASE WHEN rn=1 THEN mdate END) AS MoveDate1,
MAX(CASE WHEN rn=1 THEN town END) AS Town1,
MAX(CASE WHEN rn=2 THEN mdate END) AS MoveDate2,
MAX(CASE WHEN rn=2 THEN town END) AS Town2,
MAX(CASE WHEN rn=3 THEN mdate END) AS MoveDate3,
MAX(CASE WHEN rn=3 THEN town END) AS Town3
FROM cte
)
I'd also investigate the relative performance of not using the TVF at all. And doing a JOIN, ROW_NUMBER() OVER (PARTITION BY PersonID) and the PIVOT technique above.
Here, check it out:
-- Create People (not like that... jeez...)
CREATE TABLE #People (PersonID INT, Forename VARCHAR(25), Surname VARCHAR(25))
INSERT INTO #People VALUES (1, 'Andy', 'Cairns')
INSERT INTO #People VALUES (2, 'John', 'Smith')
-- Create historical addresses
CREATE TABLE #Addy (PersonID INT, AddyDate DATETIME, Street VARCHAR(50), Town VARCHAR(50))
INSERT INTO #Addy VALUES (1, '2011-01-01', 'Main Street', 'MyTown')
INSERT INTO #Addy VALUES (1, '2010-01-01', 'Old Street', 'OldTown')
INSERT INTO #Addy VALUES (2, '2010-01-01', 'Diagon Alley', 'London')
INSERT INTO #Addy VALUES (1, '2009-01-01', 'First Street', 'OtherTown')
-- Create ranked addresses mapped to people
SELECT p.Forename, p.Surname, a.*,
ROW_NUMBER() OVER (PARTITION BY p.PersonID ORDER BY p.PersonID) As Ordinal
INTO #Ranked
FROM #People p INNER JOIN #Addy a ON p.PersonID = a.PersonID
-- Make sure everything is kosher
SELECT * FROM #People
SELECT * FROM #Addy
SELECT * FROM #Ranked
-- Create a container for "final" results
DECLARE #Results TABLE (PersonID INT, Forename VARCHAR(25)
, MoveDate1 DATETIME, Street1 VARCHAR(50), Town1 VARCHAR(50)
, MoveDate2 DATETIME, Street2 VARCHAR(50), Town2 VARCHAR(50)
, MoveDate3 DATETIME, Street3 VARCHAR(50), Town3 VARCHAR(50))
-- Get our people primed in the results table
INSERT INTO #Results (PersonID, Forename) SELECT PersonID, Forename FROM #People
-- Fill it up
UPDATE #Results SET MoveDate1 = AddyDate, Street1 = Street, Town1 = Town FROM #Ranked INNER JOIN #Results r ON #RAnked.PersonID = r.PersonID WHERE Ordinal = 1
UPDATE #Results SET MoveDate2 = AddyDate, Street2 = Street, Town2 = Town FROM #Ranked INNER JOIN #Results r ON #RAnked.PersonID = r.PersonID WHERE Ordinal = 2
UPDATE #Results SET MoveDate3 = AddyDate, Street3 = Street, Town3 = Town FROM #Ranked INNER JOIN #Results r ON #RAnked.PersonID = r.PersonID WHERE Ordinal = 3
-- Winsauce?
SELECT * FROM #Results
-- Cleanup
DROP TABLE #People
DROP TABLE #Addy
DROP TABLE #Ranked

Resources