I'm trying to handle some active requests using Cursor:
DECLARE #ID bigint --id attachments
DECLARE #personID BIGINT
DECLARE #territoryServiceID BIGINT
DECLARE #isAtClosed BIT
DECLARE #currentServerDate DATETIME = '2016-01-01 01:10:00.000' --this change GETDATE()
DECLARE #BeginDate DATETIME SET #BeginDate = #currentServerDate
DECLARE #periodYear INT SET #periodYear = DATEPART(YEAR,#currentServerDate) - 1
DECLARE cur cursor LOCAL STATIC
FOR
SELECT at.id, at.personID, at.territoryServiceID, ts.isClosing
FROM Attachments at
INNER JOIN Person p ON p.id = at.personID AND p.parentID IS NULL
INNER JOIN TerritoryServices ts ON ts.id = at.territoryServiceID
LEFT JOIN Attachments at2 ON at2.personID = at.personID AND at2.parentID = at.id AND at2.attachmentStatusID IN (2,11,12)
WHERE at.attachmentStatusID = 1 AND at.causeOfAttachID = 8 AND at.endDate IS NOT NULL
AND at2.id IS NULL
AND p.id IN (15300000019296419,15300000018501113,15300000014988209,414674754,420940229,409531785)
OPEN cur
FETCH NEXT FROM cur INTO #ID, #personID, #territoryServiceID, #isAtClosed
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #personID_NVARCHAR NVARCHAR(MAX) SET #personID_NVARCHAR = CONVERT(NVARCHAR(MAX),#personID)
PRINT '1 ('+#personID_NVARCHAR+')'
IF (#isAtClosed = 1) -- if ter of CA is closing
BEGIN
-- Insert error into ErrorHandlingCampainOfAttach
DECLARE #ErrorDescr NVARCHAR(MAX) SET #ErrorDescr = 'TerId: ' + CONVERT(NVARCHAR(MAX),#territoryServiceID)
INSERT INTO [dbo].[ErrorHandlingCampainOfAttach] ([AttachmentsID],[personID],[territoryServiceID],[periodYear],[reasonError],[addDate],[description])
VALUES (#ID, #personID, #territoryServiceID, #periodYear, 1, GETDATE(), #ErrorDescr)
END
ELSE
BEGIN
DECLARE #terAt2ID BIGINT
DECLARE #isAt2Close BIT = 0
SELECT #isAt2Close = ts.isClosing, #terAt2ID = ts.id FROM Attachments at
INNER JOIN TerritoryServices ts ON ts.id = at.territoryServiceID
WHERE at.personID = #personID AND at.attachmentStatusID = 2 AND at.endDate IS NULL
IF (#isAt2Close = 1) -- if ter of attach is closing
BEGIN
-- Insert error into ErrorHandlingCampainOfAttach
DECLARE #ErrorDescr2 NVARCHAR(MAX) SET #ErrorDescr2 = 'TerAttachId: ' + CONVERT(NVARCHAR(MAX),#terAt2ID)
INSERT INTO [dbo].[ErrorHandlingCampainOfAttach] ([AttachmentsID],[personID],[territoryServiceID],[periodYear],[reasonError],[addDate],[description])
VALUES (#ID, #personID, #territoryServiceID, #periodYear, 2, GETDATE(), #ErrorDescr2)
END
ELSE
BEGIN
BEGIN TRY
BEGIN TRANSACTION TranName
-- Search active request
DECLARE #ID_zapros BIGINT
SELECT #ID_zapros = id FROM Attachments WHERE personID = #personID AND endDate IS NULL AND attachmentStatusID != 2 AND id != #ID
IF (#ID_zapros IS NOT NULL)
BEGIN
-- Canseled request
-- Block #1
-- Create cancel for active request
INSERT INTO Attachments (personID,orgHealthCareID,personAddressesID,territoryServiceID,attachmentProfileID,doctorID,
causeOfAttachID,careAtHome,senderRequestID,senderSystemID,attachmentStatusID,beginDate,endDate,parentID,userID,registratorID,
actualAttachmentID,ConflictAttachment,Node,regDate,isMigrated,isDuplicate,oldPersonID,servApplicationID,Num)
SELECT at.personID,at.orgHealthCareID,at.personAddressesID,at.territoryServiceID,at.attachmentProfileID, at.doctorID,
8,at.careAtHome,NULL,NULL, 11, #BeginDate, #BeginDate, at.id,
at.userID, at.registratorID, at.actualAttachmentID, NULL,NULL,at.regDate,NULL,0,at.oldPersonID,NULL,at.Num
FROM Attachments at
WHERE at.id = #ID_zapros
-- Set endDate for active request
UPDATE Attachments SET endDate = #BeginDate WHERE id = #ID_zapros
END
--Search active attach
DECLARE #ID_prikrep BIGINT
SELECT #ID_prikrep = id FROM Attachments WHERE personID = #personID AND endDate IS NULL AND attachmentStatusID = 2
IF (#ID_prikrep IS NOT NULL)
BEGIN
-- Block #2
-- Insert detach
INSERT INTO Attachments (personID,orgHealthCareID,personAddressesID,territoryServiceID,attachmentProfileID,doctorID,
causeOfAttachID,careAtHome,senderRequestID,senderSystemID,attachmentStatusID,beginDate,endDate,parentID,userID,registratorID,
actualAttachmentID,ConflictAttachment,Node,regDate,isMigrated,isDuplicate,oldPersonID,servApplicationID,Num)
SELECT at.personID,at.orgHealthCareID,at.personAddressesID,at.territoryServiceID,at.attachmentProfileID, at.doctorID,
8,at.careAtHome,NULL,NULL, 8, #BeginDate, #BeginDate, at.id,
at.userID, at.registratorID, at.actualAttachmentID, NULL,NULL,at.regDate,NULL,0,at.oldPersonID,NULL,at.Num
FROM Attachments at
WHERE at.id = #ID_prikrep
--Set endDate for active attach
UPDATE Attachments SET endDate = #BeginDate WHERE id = #ID_prikrep
END
-- Attach CA
INSERT INTO Attachments (personID,orgHealthCareID,personAddressesID,territoryServiceID,attachmentProfileID,doctorID,
causeOfAttachID,careAtHome,senderRequestID,senderSystemID,attachmentStatusID,beginDate,endDate,parentID,userID,registratorID,
actualAttachmentID,ConflictAttachment,Node,regDate,isMigrated,isDuplicate,oldPersonID,servApplicationID,Num)
SELECT at.personID,at.orgHealthCareID,at.personAddressesID,at.territoryServiceID,at.attachmentProfileID, at.doctorID,
8,at.careAtHome,NULL,NULL, 2, #BeginDate, NULL, at.id,
at.userID, at.registratorID, at.actualAttachmentID, NULL,NULL,at.regDate,NULL,0,at.oldPersonID,NULL,at.Num
FROM Attachments at
WHERE at.id = #ID
COMMIT TRANSACTION TranName
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION TranName
-- Insert error into ErrorHandlingCampainOfAttach
INSERT INTO [dbo].[ErrorHandlingCampainOfAttach] ([AttachmentsID],[personID],[territoryServiceID],[periodYear],[reasonError],[addDate],[description])
VALUES (#ID, #personID, #territoryServiceID, #periodYear, 3, GETDATE(),ERROR_MESSAGE())
END CATCH
END
END
FETCH NEXT FROM cur INTO #ID, #personID, #territoryServiceID, #isAtClosed
END
CLOSE cur
DEALLOCATE cur
But sometimes the result is inserted into two rows instead of one, in block statement "Create cancel for active request" (Block #1) or "Insert detach" (Block #2). Each run on the same recording is done in different ways, sometimes OK, sometimes duplicated entry in the block #1, block #2 times. For some reason, one of the inserts is performed twice, but not always.
Please tell me what is the reason for this behavior
It turned out pretty simple:
DECLARE #ID_zapros BIGINT
SELECT #ID_zapros = id FROM Attachments WHERE personID = #personID AND endDate IS NULL AND attachmentStatusID != 2 AND id != #ID
When this query returns NULL is written into the variable the previous value and not NULL. The solution to this problem reset the variable after insert statement or before set:
DECLARE #ID_zapros BIGINT
SELECT #ID_zapros = id FROM Attachments WHERE personID = #personID AND endDate IS NULL AND attachmentStatusID != 2 AND id != #ID
IF (#ID_zapros IS NOT NULL)
BEGIN
INSERT INTO Attachments (personID,orgHealthCareID,personAddressesID,territoryServiceID,attachmentProfileID,doctorID,
causeOfAttachID,careAtHome,senderRequestID,senderSystemID,attachmentStatusID,beginDate,endDate,parentID,userID,registratorID,
actualAttachmentID,ConflictAttachment,Node,regDate,isMigrated,isDuplicate,oldPersonID,servApplicationID,Num)
SELECT at.personID,at.orgHealthCareID,at.personAddressesID,at.territoryServiceID,at.attachmentProfileID, at.doctorID,
8,at.careAtHome,NULL,NULL, 11, #BeginDate, #BeginDate, at.id,
at.userID, at.registratorID, at.actualAttachmentID, NULL,#nvar_ID,at.regDate,NULL,0,at.oldPersonID,NULL,at.Num
FROM Attachments at
WHERE at.id = #ID_zapros
UPDATE Attachments SET endDate = #BeginDate WHERE id = #ID_zapros
SET #ID_zapros = NULL
END
... or
DECLARE #ID_zapros BIGINT
SET #ID_zapros = NULL
SELECT #ID_zapros = id FROM Attachments WHERE personID = #personID AND endDate IS NULL AND attachmentStatusID != 2 AND id != #ID
IF (#ID_zapros IS NOT NULL)
BEGIN
INSERT INTO Attachments (personID,orgHealthCareID,personAddressesID,territoryServiceID,attachmentProfileID,doctorID,
causeOfAttachID,careAtHome,senderRequestID,senderSystemID,attachmentStatusID,beginDate,endDate,parentID,userID,registratorID,
actualAttachmentID,ConflictAttachment,Node,regDate,isMigrated,isDuplicate,oldPersonID,servApplicationID,Num)
SELECT at.personID,at.orgHealthCareID,at.personAddressesID,at.territoryServiceID,at.attachmentProfileID, at.doctorID,
8,at.careAtHome,NULL,NULL, 11, #BeginDate, #BeginDate, at.id,
at.userID, at.registratorID, at.actualAttachmentID, NULL,#nvar_ID,at.regDate,NULL,0,at.oldPersonID,NULL,at.Num
FROM Attachments at
WHERE at.id = #ID_zapros
UPDATE Attachments SET endDate = #BeginDate WHERE id = #ID_zapros
END
I'm trying to anonymize all the data in my database, so I'm renaming all the people in it. I asked a similar question earlier, and was told to use NewID to force the creation of a new value per updated row, but in this situation it doesn't seem to be working.
What am I doing wrong?
-- Create Table Customer
CREATE TABLE #FirstName
(
ID int,
FirstName nvarchar(255) NULL,
Gender nvarchar(255) NULL
)
CREATE TABLE #LastName (
ID int,
LastName nvarchar(255)
)
-- BULK INSERT to import data from Text or CSV File
BULK INSERT #FirstName
FROM 'C:\Users\jhollon\Desktop\tmp\names\firstnames.lined.txt'
WITH
(
FIRSTROW = 1,
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
BULK INSERT #LastName
FROM 'C:\Users\jhollon\Desktop\tmp\names\lastnames.lined.txt'
WITH
(
FIRSTROW = 1,
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
/*SELECT FirstName FROM #FirstName WHERE ID = (
SELECT RandomNumber FROM (
SELECT ABS(CHECKSUM(NewID())) % 1500 AS RandomNumber FROM tblTenant WHERE Sex = '1'
) AS A
);*/
UPDATE tblTenant SET TenantName = (
SELECT LastName + ', ' + FirstName FROM
(SELECT UPPER(FirstName) as FirstName FROM #FirstName WHERE ID = (SELECT ABS(CHECKSUM(NewID())) % 500 + 1501)) AS A,
(SELECT LastName FROM #LastName WHERE ID = (SELECT ABS(CHECKSUM(NewID())) % 200 + 1)) as B
) WHERE Sex = '2';
UPDATE tblTenant SET TenantName = (
SELECT LastName + ', ' + FirstName FROM
(SELECT UPPER(FirstName) as FirstName FROM #FirstName WHERE ID = (SELECT ABS(CHECKSUM(NewID())) % 500 + 1)) AS A,
(SELECT LastName FROM #LastName WHERE ID = (SELECT ABS(CHECKSUM(NewID())) % 200 + 1)) as B
) WHERE Sex = '1';
DROP TABLE #FirstName;
DROP TABLE #LastName;
Correct. The subquery is evaluated once which is as advertised ("cachable scalar subquery")
Try this which uses NEWID as a derived table
UPDATE T
SET
TenantName = L.LastName + ', ' + F.FirstName
FROM
tblTenant T
CROSS APPLY
(SELECT TOP 1 UPPER(FirstName) as FirstName FROM #FirstName
WHERE CHECKSUM(NEWID()) <> T.ID
ORDER BY NEWID()) F
CROSS APPLY
(SELECT TOP 1 LastName FROM #LastName
WHERE CHECKSUM(NEWID()) <> T.ID
ORDER BY NEWID()) L
I'm not sure I understand your question, but if you want the ID to be unique values, you can make it an identity column.
Ex:
[ID] [int] IDENTITY(1,1) NOT NULL
The code below demonstrates that without an inner to outer correlation, that the old name is not guaranteed to differ from the new name when using the CROSS APPLY answer above.
WHERE F.Id <> T.Id ORDER BY NEWID() would be better within the FirstName CROSS APPLY
USE tempdb
GO
IF OBJECT_ID('tblTenant') IS NOT NULL
DROP TABLE tblTenant
GO
CREATE TABLE tblTenant
(
Id int,
FirstName nvarchar(20),
LastName nvarchar(20),
Gender bit
)
INSERT INTO tblTenant
VALUES (1, 'Bob' , 'Marley', 1),
(2, 'Boz' , 'Skaggs', 1)
SELECT DISTINCT FirstName
INTO #FirstNames
FROM tblTenant
SELECT DISTINCT LastName
INTO #LastNames
FROM tblTenant
-- There is a probability > 0 that a tenant's new name = tenants old name
SELECT
OldFirst = T.FirstName,
OldLast = T.LastName,
NewFirst = F.FirstName,
NewLast = L.LastName
FROM
tblTenant T
CROSS APPLY
(
SELECT TOP 1 UPPER(FirstName) AS FirstName
FROM #FirstNames
WHERE CHECKSUM(NEWID()) <> T.ID
ORDER BY NEWID()
) F
CROSS APPLY
(
SELECT TOP 1 LastName
FROM #LastNames
WHERE CHECKSUM(NEWID()) <> T.ID
ORDER BY NEWID()
) L
I have this statement in T-SQL.
SELECT Bay From TABLE where uid in (
select B_Numbers from Info_Step WHERE uid = 'number'
)
I am selecting "multiple" BAYs from TABLE where their uid is equal to a string of numbers like this:
B_Numbers = 1:45:34:98
Therefore, I should be selecting 4 different BAYs from TABLE. I basically need to split the string 1:45:34:98 up into 4 different numbers.
I'm thinking that Split() would work, but it doesn't and I get a syntax error.
Any thoughts from the T-SQL gods would be awesome!
Here is an implementation of a split function that returns the list of numbers as a table:
http://rbgupta.blogspot.com/2007/03/split-function-tsql.html
Looks like this would set you on your way...
Here is a method that uses an auxiliary numbers table to parse the input string. The logic can easily be added to a function that returns a table. That table can then be joined to lookup the correct rows.
Step 1: Create the Numbers table
SET NOCOUNT ON
GO
IF EXISTS
(
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Numbers'
AND TABLE_SCHEMA = 'dbo'
AND TABLE_TYPE = 'BASE TABLE'
)
BEGIN
DROP TABLE dbo.Numbers
END
GO
CREATE TABLE dbo.Numbers
(
Number smallint IDENTITY(1, 1) PRIMARY KEY
)
GO
WHILE 1 = 1
BEGIN
INSERT INTO dbo.Numbers DEFAULT VALUES
IF SCOPE_IDENTITY() = 32767
BEGIN
BREAK
END
END
GO
Step 2: Parse the Input String
CREATE FUNCTION dbo.ParseString(#input_string varchar(8000), #delim varchar(8000) = " ")
RETURNS TABLE
AS RETURN
(
SELECT Number
FROM dbo.Numbers
WHERE CHARINDEX
(
#delim + CONVERT(VARCHAR(12),Number) + #delim,
#delim + #input_string + #delim
) > 0
)
GO
**EXAMPLE**
SELECT * FROM dbo.ParseString('1:45:34:98',':')
Step 3: Use the results however you want/need
Number
------
1
34
45
98
End-To-End Example
Create function that returns the appropriate BNumber (of course change it to use the commented out SQL)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION dbo.GetBNumber (#uid int)
RETURNS VARCHAR(8000)
AS
BEGIN
RETURN '1:45:34:98'
--select B_Numbers from Info_Step WHERE uid = #uid
END
GO
Use the use functions to return the desired results
-- Using Test Data
SELECT N.Number FROM Numbers N
JOIN dbo.ParseString(dbo.GetBNumber(12345),':') Q ON Q.Number = N.Number
-- Using Your Data (Untested but should work.)
SELECT N.Bay
FROM TABLE N
JOIN dbo.ParseString(dbo.GetBNumber(ENTER YOU NUMBER HERE),':') Q ON Q.Number = N.uid
Results
Number
------
1
34
45
98
You should keep your arrays as rows but if I understand your question I think this will work.
SELECT
Bay
From
TABLE
join Info_Step
on B_Numbers like '%'+ uid +'%'
where
Info_Step.uid = 'number'
This query will do a full table scan because of the like operator.
What you can do is loop through the B_Numbers entries and do your own split on : Insert those entries into a temp table and then perform your query.
DECLARE #i int
DECLARE #start int
DECLARE #B_Numbers nvarchar(20)
DECLARE #temp table (
number nvarchar(10)
)
-- SELECT B_Numbers FROM Info_Step WHERE uid = 'number'
SELECT #B_Numbers = '1:45:34:98'
SET #i = 0
SET #start = 0
-- Parse out characters delimited by ":";
-- Would make a nice user defined function.
WHILE #i < len(#B_Numbers)
BEGIN
IF substring(#B_Numbers, #i, 1) = ':'
BEGIN
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start))
SET #start = #i + 1
END
SET #i = #i + 1
END
-- Insert last item
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start + 1))
-- Do query with parsed values
SELECT Bay FROM TABLE WHERE uid in (SELECT * FROM #temp)
You can even try this
declare #str varchar(50)
set #str = '1:45:34:98'
;with numcte as(
select 1 as rn union all select rn+1 from numcte where rn<LEN(#str)),
getchars as(select
ROW_NUMBER() over(order by rn) slno,
rn,chars from numcte
cross apply(select SUBSTRING(#str,rn,1) chars)X where chars = ':')
select top 1
Bay1 = SUBSTRING(#str,0,(select rn from getchars where slno = 1))
,Bay2 = SUBSTRING(#str,
(select rn from getchars where slno = 1) + 1,
(((select rn from getchars where slno = 2)-
(select rn from getchars where slno = 1)
)-1))
,Bay3 = SUBSTRING(#str,
(select rn from getchars where slno = 2) + 1,
(((select rn from getchars where slno = 3)-
(select rn from getchars where slno = 2)
)-1))
,Bay4 = SUBSTRING(#str,
(select rn from getchars where slno = 3)+1,
LEN(#str))
from getchars
Output:
Bay1 Bay2 Bay3 Bay4
1 45 34 98