Deleting Tree-Nodes in a SQL table - sql-server

I am trying to write a recursive procedure that would delete the node and all it's children if they are such in the table. I tried doing the following
CREATE PROCEDURE DeleteFile
#FileID INTEGER,
#UserID VARCHAR(MAX)
AS
DELETE FROM [FileTree] WHERE [ID] = #FileID AND [UserID]=#UserID;
IF EXISTS(SELECT * FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID)
BEGIN
DECLARE FileCursor CURSOR LOCAL FOR
SELECT [ID],[UserID] FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID;
OPEN FileCursor
FETCH NEXT FROM FileCursor INTO #FileID , #UserID
WHILE ##FETCH_STATUS =0
BEGIN
EXEC DeleteFile #FileID,#UserID;
FETCH NEXT FROM FileCursor INTO #FileID , #UserID ;
END
END
ELSE
return
But for some reason this is not working. It deletes the node but the kids remain.
Table ,,design" .
CREATE TABLE [FileTree] (
[ID] INT IDENTITY NOT NULL,
[Name] VARCHAR (MAX) NOT NULL,
[ParentID] INT NULL,
[UserID] VARCHAR (MAX) NOT NULL
);
Can you please indicate the errors I made and suggest a working procedure ?
UPD: I made the cursor LOCAL and I am fetching one time before going into the while loop, it still does not delete all the children.

I think you have syntax problem.i fixed it like this
CREATE PROCEDURE DeleteFile
#FileID INTEGER,
#UserID VARCHAR(MAX)
AS
BEGAIN
DELETE FROM [FileTree] WHERE [ID] = #FileID AND [UserID]=#UserID;
IF EXISTS(SELECT * FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID)
BEGIN
DECLARE FileCursor CURSOR LOCAL FOR
SELECT [ID],[UserID] FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID;
OPEN FileCursor
FETCH NEXT FROM FileCursor INTO #FileID , #UserID
WHILE ##FETCH_STATUS =0
BEGIN
EXEC DeleteFile #FileID,#UserID;
FETCH NEXT FROM FileCursor INTO #FileID , #UserID ;
END
END
END

You can use a CTE and a temporary table:
WITH CTE (ID)
AS
(
SELECT ID FROM FileTree WHERE Id=#ID
UNION ALL
SELECT t.ID FROM FileTree t
INNER JOIN CTE c ON t.ParentId=c.Id
)
SELECT ID INTO #temp FROM CTE;
DELETE FROM FileTree WHERE ID IN(SELECT ID FROM #temp)
DROP TABLE #temp

Related

T-SQL 2008- Exit when Value is NULL

I am looking for a way to exit an T-SQL script when #Value is null. This is what I have so far but it does not work as expected:
SELECT
#Value,
CASE
WHEN #Value IS NULL
RAISERROR('EXIT', 16, 1)
FROM
table
WHERE
name LIKE 'test'
Perhaps this will work for you:
DECLARE #Value INT = 1
IF( #Value IS NULL)
BEGIN
RAISERROR('Exit',16,1)
END
ELSE
BEGIN
SELECT #Value
END
IF #Value IS NULL RAISERROR('EXIT', 16,1);
Using a cursor and a temp table you can get the output you want. don't know if that's the goal,
USE AdventureWorksLT2012
DECLARE #CustomerID AS INT
DECLARE #CompanyName AS VARCHAR(MAX)
DECLARE #EmailAddress AS VARCHAR(MAX)
CREATE TABLE #output (CustomerID INT,CompanyName VARCHAR(MAX),EmailAddress VARCHAR(MAX))
DECLARE testCursor CURSOR
FOR
SELECT TOP (100)
CustomerID
,CompanyName
,EmailAddress
FROM SalesLT.Customer
ORDER BY customerID DESC;
OPEN testCursor;
FETCH NEXT FROM testCursor
INTO #CustomerID, #CompanyName, #emailAddress;
if #EmailAddress is not null
BEGIN
INSERT INTO #output values( #CustomerID, #CompanyName, #emailAddress);
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM testCursor
INTO #CustomerID, #CompanyName, #emailAddress;
if #EmailAddress is null
BEGIN
RAISERROR('Exit',16,1);
BREAK;
end
INSERT INTO #output values( #CustomerID, #CompanyName, #emailAddress);
END;
END
CLOSE testCursor;
DEALLOCATE testCursor;
SELECT * FROM #output;
DROP TABLE #output

SQL Server procedure error - duplicate entries inserted into the table

I am writing a procedure in SQL Server to insert or update records.
The update part of the code is working fine but when I am executing it for inserting, duplicate entries are inserted into the table.
I created the primary key to avoid this error but after creating that I am not able to insert any single record.
Here is the code :
Alter Procedure test_case
#id int,
#name nvarchar(20)
AS
If exists (Select t_id from testing2 where t_id = #id)
begin
update testing2
set t_id = #id, t_name = #name
where t_id = #id
end
else
begin
insert into testing2 (t_id, t_name, last_date, hard)
select
#id, #name, convert(date, getdate()), 'null'
from test
end
On executing it is showing 2 rows affected
You do not require test table in the select query
insert into testing2 (t_id, t_name, last_date, hard)
select
#id as t_id, #name as t_name, convert(date, getdate()) as last_date, 'null' as hard
is enough
I like to break functionality into smaller parts because it helps me to manage code better.
Maybe this is not a good example since it is pretty simple but I will write it anyway.
Create Procedure Testing2_InsertData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
Insert Into testing2
(t_id, t_name, last_date, hard)
Values
( #id, #name, GetDate(), null )
Go
Create Procedure Testing2_UpdateData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
Update testing2 Set
t_name = #name --, maybe last_date = GetDate()
Where ( t_id = #id )
Go
Create Procedure Testing2_SaveData (
#id int,
#name nvarchar(20)
) As
Set NoCount On
If ( Exists( Select t_id From testing2 Where ( t_id = #id ) ) )
Exec Testing2_UpdateData #id, #name
Else
Exec Testing2_InsertData #id, #name
Go

SQL Server: Nested cursor syntax error

I tried to define following SQL Server nested cursor statement: I loop calendars and migrate them from cut_calendar to sd_calendar. Each calendar also have calendar days. They are also moved by each calendar move.
DECLARE #id NUMERIC(20)
DECLARE #sdCalendarId NUMERIC(20)
DECLARE #calendarTypId NUMERIC(5)
DECLARE #name NVARCHAR(35)
DECLARE #description NVARCHAR(255)
DECLARE #ptyId NUMERIC(20)
DECLARE #lockCode NVARCHAR(20)
DECLARE #dataOwnerId NUMERIC(20)
DECLARE #cntId NUMERIC(20)
DECLARE #nonBusinessDaysMonday CHAR(1)
DECLARE #nonBusinessDaysTuesday CHAR(1)
DECLARE #nonBusinessDaysWednesday CHAR(1)
DECLARE #nonBusinessDaysThursday CHAR(1)
DECLARE #nonBusinessDaysFriday CHAR(1)
DECLARE #nonBusinessDaysSaturday CHAR(1)
DECLARE #nonBusinessDaysSunday CHAR(1)
DECLARE #ccyId NUMERIC(20)
DECLARE #code NVARCHAR(30)
DECLARE #version NUMERIC(10)
DECLARE #seal VARCHAR(255)
DECLARE #lstUpdTs DATETIME
DECLARE #day_id NUMERIC(20)
DECLARE #day_calDate DATETIME
DECLARE #day_lockCode NVARCHAR(20)
DECLARE #day_calComment NVARCHAR(255)
DECLARE #day_dataOwnerId NUMERIC(20)
DECLARE #day_calendarId NUMERIC(20)
DECLARE #day_calRecurring CHAR(1)
DECLARE #day_version NUMERIC(10)
DECLARE #day_seal VARCHAR(255)
DECLARE #day_lstUpdTs DATETIME
DECLARE #day_sdCalendarDaysId NUMERIC(20)
DECLARE #sdCodeId NUMERIC(20)
DECLARE cursorCutoffCalendar CURSOR FOR
SELECT ID, NAME, CALENDAR_TYP_ID,DESCRIPTION,PTY_ID,LOCK_CODE,DATA_OWNER_ID,CNT_ID,NON_BUSINESS_DAYS_MONDAY,NON_BUSINESS_DAYS_TUESDAY,NON_BUSINESS_DAYS_WEDNESDAY,NON_BUSINESS_DAYS_THURSDAY,NON_BUSINESS_DAYS_FRIDAY,NON_BUSINESS_DAYS_SATURDAY,NON_BUSINESS_DAYS_SUNDAY,CCY_ID,CODE,VERSION,SEAL,LST_UPD_TS
FROM CUT_CALENDAR
WHERE ID != 1
OPEN cursorCutoffCalendar
FETCH NEXT FROM cursorCutoffCalendar INTO #id, #name, #calendarTypId, #description, #ptyId, #lockCode, #dataOwnerId, #cntId, #nonBusinessDaysMonday, #nonBusinessDaysTuesday, #nonBusinessDaysWednesday, #nonBusinessDaysThursday, #nonBusinessDaysFriday, #nonBusinessDaysSaturday, #nonBusinessDaysSunday, #ccyId, #code, #version, #seal, #lstUpdTs
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #sdCalendarId = COALESCE(MAX(ID),1) FROM SD_CALENDAR
SET #sdCalendarId = #sdCalendarId + 1
UPDATE CUT_CALENDAR_DAY
SET CALENDAR_ID = #sdCalendarId
WHERE CALENDAR_ID = #id
SELECT #sdCodeId = ID FROM SD_CALENDAR WHERE CODE = #code
IF #sdCodeId > 0
SET #code = CONCAT(#code, '_co')
ELSE
INSERT INTO SD_CALENDAR (ID, NAME, CALENDAR_ROLE_ID,DESCRIPTION,USE_IN_CUTOFF,PTY_ID,LOCK_CODE,DATA_OWNER_ID,CNT_ID,NON_BUSINESS_DAYS_MONDAY,NON_BUSINESS_DAYS_TUESDAY,NON_BUSINESS_DAYS_WEDNESDAY,NON_BUSINESS_DAYS_THURSDAY,NON_BUSINESS_DAYS_FRIDAY,NON_BUSINESS_DAYS_SATURDAY,NON_BUSINESS_DAYS_SUNDAY,CCY_ID,CODE,VERSION,SEAL,LST_UPD_TS)
VALUES(#sdCalendarId, #name, #calendarTypId,#description,1,#ptyId,#lockCode,#dataOwnerId,#cntId,#nonBusinessDaysMonday,#nonBusinessDaysTuesday,#nonBusinessDaysWednesday,#nonBusinessDaysThursday,#nonBusinessDaysFriday,#nonBusinessDaysSaturday,#nonBusinessDaysSunday,#ccyId,#code,#version,#seal,#lstUpdTs)
DECLARE cursorCutoffCalendarDays CURSOR FOR
SELECT ID, CAL_DATE, LOCK_CODE,CAL_COMMENT,DATA_OWNER_ID,CALENDAR_ID,CAL_RECURRING,VERSION,SEAL,LST_UPD_TS
FROM CUT_CALENDAR_DAY
WHERE ID != 1
OPEN cursorCutoffCalendarDays
FETCH NEXT FROM cursorCutoffCalendarDays INTO #day_id, #day_calDate, #day_lockCode, #day_calComment, #day_dataOwnerId, #day_calendarId, #day_calRecurring, #day_version, #day_seal, #day_lstUpdTs
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #day_sdCalendarDaysId = COALESCE(MAX(ID),1) FROM SD_CALENDAR_DAY
SET #day_sdCalendarDaysId = #day_sdCalendarDaysId + 1
INSERT INTO SD_CALENDAR_DAY (ID, CAL_DATE, LOCK_CODE,CAL_COMMENT,DATA_OWNER_ID,CALENDAR_ID,CAL_RECURRING,VERSION,SEAL,LST_UPD_TS)
VALUES(#day_sdCalendarDaysId, #day_calDate, #day_lockCode, #day_calComment, #day_dataOwnerId, #day_calendarId, #day_calRecurring, #day_version, #day_seal, #day_lstUpdTs)
FETCH NEXT FROM cursorCutoffCalendarDays INTO #day_sdCalendarDaysId, #day_calDate, #day_lockCode, #day_calComment, #day_dataOwnerId, #day_calendarId, #day_calRecurring, #day_version, #day_seal, #day_lstUpdTs
END
CLOSE cursorCutoffCalendarDays
DEALLOCATE cursorCutoffCalendarDays
GO
DELETE FROM CUT_CALENDAR
WHERE ID = #id
FETCH NEXT FROM cursorCutoffCalendar INTO #sdCalendarId, #name, #calendarTypId, #description, #ptyId, #lockCode, #dataOwnerId, #cntId, #nonBusinessDaysMonday, #nonBusinessDaysTuesday, #nonBusinessDaysWednesday, #nonBusinessDaysThursday, #nonBusinessDaysFriday, #nonBusinessDaysSaturday, #nonBusinessDaysSunday, #ccyId, #code, #version, #seal, #lstUpdTs
END
CLOSE cursorCutoffCalendar
DEALLOCATE cursorCutoffCalendar
GO
Now when I run this, I get following error:
SQLServerException: Incorrect syntax near 'cursorCutoffCalendarDays'.
cursorCutoffCalendarDays is the inner cursor of my statement.
Try removing GO from:
CLOSE cursorCutoffCalendarDays
DEALLOCATE cursorCutoffCalendarDays
GO
And I agree with the comment you got from GarethD, I used to write cursors like this but then I asked how to do it without the cursor and got a nice explanation
Blockquote
Remove Go from this line then try
CLOSE cursorCutoffCalendarDays
DEALLOCATE cursorCutoffCalendarDays

MSG 2714 There is an object already named

CREATE PROC PS_LOGIN(
#ID NVARCHAR(50),
#PSW NVARCHAR(50)
)
AS
BEGIN
SELECT * FROM Table_Users WHERE ID=#ID AND PSW=#PSW
END
Try something like this....
IF OBJECT_ID('PS_LOGIN' , 'P') IS NOT NULL
DROP PROC PS_LOGIN
GO
CREATE PROC PS_LOGIN(
#ID NVARCHAR(50),
#PSW NVARCHAR(50)
)
AS
BEGIN
SELECT * FROM Table_Users WHERE ID=#ID AND PSW=#PSW
END
GO

Deleting Tree-Nodes in a SQL table without a cursor

I have a tree of strings stored in a sql table, this is the definition for the table.
CREATE TABLE [FileTree] (
[ID] INT NOT NULL,
[Name] VARCHAR (MAX) NOT NULL,
[ParentID] INT NULL,
[UserID] VARCHAR (MAX) NOT NULL
);
And I have a recursive delete procedure for deleting a node and it's children, it uses a cursor and it works perfectly well after a user on SO in my previous question regarding this matter pointed out the problems with the syntax. (Deleting Tree-Nodes in a SQL table)
CREATE PROCEDURE DeleteFile
#FileID INTEGER,
#UserID VARCHAR(MAX)
AS
BEGIN
DELETE FROM [FileTree] WHERE [ID] = #FileID AND [UserID]=#UserID;
IF EXISTS(SELECT * FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID)
BEGIN
DECLARE FileCursor CURSOR LOCAL FOR
SELECT [ID],[UserID] FROM [FileTree] WHERE [ParentID] = #FileID AND [UserID]=#UserID;
OPEN FileCursor
FETCH NEXT FROM FileCursor INTO #FileID , #UserID
WHILE ##FETCH_STATUS =0
BEGIN
EXEC DeleteFile #FileID,#UserID;
FETCH NEXT FROM FileCursor INTO #FileID , #UserID ;
END
END
END
However , another reply to the question suggested using a Common Table Expression, I googled and I don't think I really understand how can a CTE replace the cursor in this procedure. Any suggestions ?
You can use a recursive CTE to get all children of the ID that should be deleted and then use that in a DELETE statement:
with all_ids as (
select id, ParentID
from FileTree
where id = 4 -- this is the root ID that should be deleted
union all
select c.id, c.ParentID
from FileTree c
join all_ids p on p.id = c.ParentID
)
delete from file_tree
where id in (select id from all_ids);
SQLFiddle example: http://sqlfiddle.com/#!3/ef474f/1

Resources