How to move hierarchyid subtree if the new parent has already got children? - sql

I guess the problem is described in the title pretty well. I've got a table like this:
CREATE TABLE [Employees] (
[Id] INT identity(1, 1) PRIMARY KEY
,[Hid] HIERARCHYID NOT NULL
,[Name] VARCHAR(50) NULL
,[Secondname] VARCHAR(50)
,[Surname] VARCHAR(50)
,[BossId] INT FOREIGN KEY REFERENCES [Employees]([Id])
,[PositionId] INT FOREIGN KEY REFERENCES [Positions]([Id])
,[DepartmentId] INT FOREIGN KEY REFERENCES [Departments]([Id])
,[RecruitDate] DATE NOT NULL
);
And I need to change employee's boss from one to another. Obviously, there is a nice solution - GetReparentedValue(), and I used this example that seemed to be exactly what I needed.
But this solution happens to be not working. Hid.GetAncestor(1) is equal to previous BossHid. Probably, the problem is that it can't work well if new parent already got children. That really upsets me. Does it mean I need to write recursive CTE on my own?
Here is the code that was supposed to be working but it didn't:
CREATE PROCEDURE UpdateEmployee #Id INT
,#Name VARCHAR(50)
,#Secondname VARCHAR(50)
,#Surname VARCHAR(50)
,#BossId INT
,#PosId INT
,#DepId INT
,#Rdate DATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #BossExist INT
,#OldBossHid HIERARCHYID
,#NewBossHid HIERARCHYID
,#LastHid HIERARCHYID;
SELECT #BossExist = count(*)
FROM dbo.Employees
WHERE [Hid] = HIERARCHYID::GetRoot();
IF #BossExist != 0
AND #BossId IS NULL
RETURN;
SELECT #OldBossHid = (
SELECT [Hid].GetAncestor(1)
FROM dbo.Employees
WHERE #Id = [Id]
);
SELECT #NewBossHid = (
SELECT [Hid].GetAncestor(1)
FROM dbo.Employees
WHERE #BossId = BossId
);
SELECT #LastHid = #NewBossHid.GetDescendant(MAX([Hid]), NULL)
FROM dbo.Employees
WHERE [Hid].GetAncestor(1) = #NewBossHid;
UPDATE dbo.Employees
SET [Hid] = [Hid].GetReparentedValue(#OldBossHid, #NewBossHid)
WHERE [Hid].IsDescendantOf(#OldBossHid) = 1;
UPDATE dbo.Employees
SET [Name] = #Name
,[Secondname] = #Secondname
,[Surname] = #Surname
,[BossId] = #BossId
,[PositionId] = #PosId
,[DepartmentId] = #DepId
,[RecruitDate] = #Rdate
WHERE #Id = Id;
END
GO
Thank you.

Related

SQL Server Trigger After Insert is Repeating old valus

Can someone please explain why this trigger would start failing and insert the same record repeatedly? It seems as though there is something wrong with the variables. The purpose of the trigger is to copy the inserted record from the Employee table to the EmployeeHistory table. I set the trigger and it runs fine. But then when my coworker runs some insert scripts, it starts repeating the same old value from my last execution of an insert, instead of the new values that they are trying to insert.
I have already recoded this to not use variables, but I would still like to know why this doesn't work as expected.
CREATE TRIGGER [dbo].[triggerEmployee_AfterInsert]
ON [dbo].[Employee]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #EmployeeID varchar(25)
DECLARE #FirstName varchar(25)
DECLARE #LastName varchar(50)
DECLARE #FullName varchar(75)
DECLARE #EmailAddress varchar(50)
DECLARE #ManagerID varchar(15)
DECLARE #JobTitle varchar(50)
DECLARE #EmployeeStatus varchar(10)
DECLARE #Office varchar(25)
SELECT #EmployeeID = [dbo].[Employee].[EmployeeID]
,#FirstName = [dbo].[Employee].[FirstName]
,#LastName = [dbo].[Employee].[LastName]
,#FullName = [dbo].[Employee].[FullName]
,#EmailAddress = [dbo].[Employee].[EmailAddress]
,#ManagerID = [dbo].[Employee].[ManagerID]
,#JobTitle = [dbo].[Employee].[JobTitle]
,#EmployeeStatus = [dbo].[Employee].[EmployeeStatus]
,#Office = [dbo].[Employee].[Office]
FROM [dbo].[Employee]
INSERT INTO [dbo].[EmployeeHistory] (
EmployeeID
,FirstName
,LastName
,FullName
,EmailAddress
,ManagerID
,JobTitle
,EmployeeStatus
,Office
)
VALUES (
#EmployeeID
,#FirstName
,#LastName
,#FullName
,#EmailAddress
,#ManagerID
,#JobTitle
,#EmployeeStatus
,#Office
)
END
GO
ALTER TABLE [dbo].[Employee] ENABLE TRIGGER [triggerEmployee_AfterInsert]
GO
Rewriting the SELECT statement to use the INSERTED table fixed the issue.
SELECT #EmployeeID = [EmployeeID]
,#FirstName = [FirstName]
,#LastName = [LastName]
,#FullName = [FullName]
,#EmailAddress = [EmailAddress]
,#ManagerID = [ManagerID]
,#JobTitle = [JobTitle]
,#EmployeeStatus = [EmployeeStatus]
,#Office = [Office]
FROM INSERTED

Microsoft SQL - Stored Procedure to transfer an employee

I want to create a stored procedure that transfers an employee from the current team to another team. So far I've done this:
CREATE OR ALTER PROC pr_EmployeeTeam #IDTeam1 int, #IDTeam2 int
AS
BEGIN
DECLARE #NIC int
SELECT #IDTeam1 = IDTeam from StuffTeams
SELECT #IDTeam2 = IDTeam from StuffTeams
UPDATE StuffTeams SET IDTeam = #IDTeam1 WHERE NIC = #NIC
UPDATE StuffTeams SET IDTeam = #IDTeam2 WHERE NIC = #NIC
END
EXEC pr_EmployeeTeam 2,5
And my StuffTeam table:
CREATE TABLE StuffTeams(
IDStuffTeam nvarchar(30) NOT NULL,
IDTeam int NOT NULL,
NIC int NOT NULL,
DateStart date NOT NULL,
DateFinish date NOT NULL,
CONSTRAINT PK_Stuff PRIMARY KEY (IDStuffTeam),
CONSTRAINT FK_sTeam FOREIGN KEY (IDTeam) REFERENCES Teams (IDTeam),
CONSTRAINT FK_sNIC FOREIGN KEY (NIC) REFERENCES Employees (NIC),
)
Inside of StuffTeams I have 30 values but when I exec the code says
0 rows affacted
Many thanks
UPDATE
CREATE PROC sp_UpdateTeam
#TeamID int,
#NIC int
AS
BEGIN
update StuffTeams SET IDTeam = #TeamID
WHERE NIC = #NIC
END
EXEC sp_UpdateTeam 5,54321
This solved my problem! Thanks
Based upon your requirement, I have updated your proc as shown below:
CREATE OR ALTER PROC pr_EmployeeTeam #IDTeam1 int, #IDTeam2 int
AS
BEGIN
DECLARE #NIC int
SELECT #NIC = IDTeam from StuffTeams WHERE IDTeam = #IDTeam1
UPDATE StuffTeams SET IDTeam = #IDTeam2 WHERE NIC = #NIC
END
EXEC pr_EmployeeTeam 2,5

Create SQL Function view with User ID and Date range

As I title says, I need to create a Function View of two tables.
Below are the SQL Tables
CREATE TABLE User_Specialist(
ID_User_Specialist INT NOT NULL,
Name_User_Specialist VARCHAR(50) NOT NULL,
CONSTRAINT PK_ID_User_Specialist PRIMARY KEY(ID_User_Specialist),
GO
CREATE TABLE Incident(
ID_Incident INT IDENTITY(1,1) NOT NULL,
Incident_Creation_Date DATETIME NULL,
Assigned_Specialist INT NULL,
CONSTRAINT FK_Assigned_Specialist FOREIGN KEY (Especialista_Asignado) REFERENCES Usuario_Especialista(ID_Usuario_Especialista),
GO
Based on the previous information, I need the function to display Assign Specialist and the dates that the Incident was created.
Right know this is what I got:
CREATE FUNCTION View_Date (#ID_User_Incident INT)
RETURNS INT
AS
BEGIN
DECLARE #Total_Incidents INT
SELECT #Total_Incidents = COUNT(ID_Incident)
FROM Incidents i, User_Specialist u
WHERE i.ID_Incident = u.ID_User_Specialist AND u.ID_User_Specialist =#ID_User_Incident
RETURN (#Total_Incidents)
END
GO
DECLARE #Specialist_ID int;
EXEC #Specialist_ID = [dbo].View_Date
#ID_Incidentes_Usuarios = 5;
SELECT #Specialist_ID AS 'Assigned Specialist Incidents'
GO
The only thing missing is the dates range.
I believe you changed your table and field names to English from Spanish. You have missed some of them and there were some missing parenthesis. As much as I understand I changed them to understand better.
CREATE TABLE User_Specialist
(
ID_User_Specialist INT NOT NULL,
Name_User_Specialist VARCHAR(50) NOT NULL
CONSTRAINT PK_ID_User_Specialist PRIMARY KEY(ID_User_Specialist)
)
GO
CREATE TABLE Incident
(
ID_Incident INT IDENTITY(1,1) NOT NULL,
Incident_Creation_Date DATETIME NULL,
Assigned_Specialist INT NULL
CONSTRAINT FK_Assigned_Specialist FOREIGN KEY (Assigned_Specialist) REFERENCES User_Specialist(ID_User_Specialist)
)
GO
If you want to see multiple columns or rows as result you need to use Table-valued Function. I have created sample query from your table and prepared function. you can change it with what you want.
CREATE FUNCTION dbo.FN_ViewDate
(
#ID_User_Incident INT
)
RETURNS #Result TABLE
(
TotalIncidents INT
,FirstIncidentDate DATETIME
,LastIncidentDate DATETIME
)
AS
BEGIN
INSERT INTO #Result
SELECT COUNT(ID_Incident)
,MIN(Incident_Creation_Date)
,MAX(Incident_Creation_Date)
FROM Incident I
LEFT JOIN User_Specialist U ON I.Assigned_Specialist = U.ID_User_Specialist
WHERE I.Assigned_Specialist = #ID_User_Incident
RETURN
END
GO
I think this query can help you.
declare #count int = 0 , #dates varchar(200) =''
SELECT #count+=1 , #dates +=' '+ i.Incident_Creation_Date
FROM Incidents i, User_Specialist u
WHERE i.ID_Incident = u.ID_User_Specialist AND u.ID_User_Specialist =#ID_User_Incident
select #count as [count] ,#dates [incidentDates]

Create a stored procedure for a table named X

CREATE TABLE Persons
(
ID int NOT NULL,
ModifiedDate datetime,
FirstName varchar(50),
LastName varchar(50),
EMail varchar(30),
PhoneNumber varchar(15),
PRIMARY KEY (ID)
);
GetX (int IDX)
if the parameter is null returns all the rows of the table ordered by ModifiedDate field in descending order
otherwise returns just the row that matches the ID
What you are wanting is a "catch all" query. For SQL Server, this can be done a couple of ways. Aaron Bertrand writes about it here.
create procedure GetX (#IDX int = null)
as
select
ID
,ModifiedDate
,FirstName
,LastName
,EMail
,PhoneNumber
from Persons
where #IDX is null or ID = #IDX
order by ModifiedDate desc
Then
exec GetX #IDX = 4;
exec GetX #IDX = null;

Cascade update sql trigger

I have two tables in two different databases Lab15DB_1 and Lab15DB_2.
The parent table is Author and the child table is Book.
Here they are:
CREATE TABLE Author
(
AuthorId INT NOT NULL PRIMARY KEY,
AuthorName NVARCHAR(50) NOT NULL,
AuthorSurname NVARCHAR(50) NOT NULL,
)
CREATE TABLE Book
(
BookId INT NOT NULL PRIMARY KEY,
BookName NVARCHAR(50) NOT NULL,
BookType NVARCHAR(50) NOT NULL CHECK(BookType IN ('Science', 'Hobby', 'Cooking', 'Fishing', 'Nature', 'Favorites')),
PublishYear INT NOT NULL,
AuthorId INT NOT NULL,
Price MONEY CHECK (PRICE > 0.0)
)
I want to create cascade update via triggers.
Here is an attempt to create trigger for cascade update on the Author table:
CREATE TRIGGER UpdateAuthorTrigger
ON Author
FOR UPDATE
AS
DECLARE #intRowCount int
SELECT #intRowCount = ##ROWCOUNT
IF #intRowCount > 1
BEGIN
IF UPDATE(AuthorId)
BEGIN
UPDATE Author
SET AuthorId = AuthorId
/* some code for update Book*/
END
END
ELSE
IF #intRowCount = 1
BEGIN
IF UPDATE(AuthorId)
BEGIN
UPDATE Author
SET Author.AuthorId = (SELECT AuthorId FROM INSERTED)
FROM Author
INNER JOIN DELETED ON Author.AuthorId = DELETED.AuthorId
UPDATE Lab15DB_1.dbo.Book
SET Lab15DB_1.dbo.Book.AuthorId = (SELECT AuthorId FROM INSERTED)
FROM Lab15DB_1.dbo.Book
INNER JOIN DELETED ON Lab15DB_1.dbo.Book.AuthorId = DELETED.AuthorId
END
END
GO
There is no problem with case #intRowCount = 1, but I have difficulties with case #intRowCount > 1.
Could you please give me any advice?