我有以下表格(简称):
CREATE TABLE Category(
CategoryId int NOT NULL PRIMARY KEY,
ParentCategoryId int NULL,
Name nvarchar(255) NOT NULL,
FOREIGN KEY (ParentCategoryId) REFERENCES Category(CategoryId) ON UPDATE NO ACTION ON DELETE NO ACTION);
CREATE TABLE TimeSlot(
TimeSlotId int NOT NULL PRIMARY KEY,
CategoryId int NOT NULL,
FOREIGN KEY (CategoryId) REFERENCES Category(CategoryId) ON UPDATE NO ACTION ON DELETE NO ACTION);
CREATE TABLE PersonTimeSlotAssignment(
PersonId int NOT NULL,
TimeSlotId int NOT NULL,
PRIMARY KEY (PersonId, TimeSlotId),
FOREIGN KEY (TimeSlotId) REFERENCES TimeSlot(TimeSlotId) ON UPDATE NO ACTION ON DELETE NO ACTION);
以下是一些测试数据:
INSERT INTO Category(CategoryId, ParentCategoryId, Name) VALUES (100, NULL, 'cat 1');
INSERT INTO Category(CategoryId, ParentCategoryId, Name) VALUES (110, 100, 'cat 1.1');
INSERT INTO Category(CategoryId, ParentCategoryId, Name) VALUES (111, 110, 'cat 1.1.1');
INSERT INTO Category(CategoryId, ParentCategoryId, Name) VALUES (120, 100, 'cat 1.2');
INSERT INTO Category(CategoryId, ParentCategoryId, Name) VALUES (200, NULL, 'cat 2');
INSERT INTO TimeSlot(TimeSlotId, CategoryId) VALUES (301, 111);
INSERT INTO TimeSlot(TimeSlotId, CategoryId) VALUES (302, 120);
INSERT INTO TimeSlot(TimeSlotId, CategoryId) VALUES (303, 200);
INSERT INTO PersonTimeSlotAssignment(PersonId, TimeSlotId) VALUES (401, 301);
INSERT INTO PersonTimeSlotAssignment(PersonId, TimeSlotId) VALUES (401, 302);
INSERT INTO PersonTimeSlotAssignment(PersonId, TimeSlotId) VALUES (402, 302);
INSERT INTO PersonTimeSlotAssignment(PersonId, TimeSlotId) VALUES (402, 303);
SELECT ts.TimeSlotId, pc.Name
FROM PersonTimeSlotAssignment
JOIN TimeSlot AS ts ON PersonTimeSlotAssignment.TimeSlotId = ts.TimeSlotId
JOIN Category AS pc ON ts.CategoryId = pc.CategoryId
WHERE PersonTimeSlotAssignment.PersonId = @PERSON_ID;
这为我提供了一个人,列出了这个人被分配到的所有TimeSlots以及TimeSlot所属的叶子类别的名称。例如,对于ID为401的人,它给出了:
TimeSlotId Name
---------------------
301 cat 1.1.1
302 cat 1.2
使用以下递归查询,我也可以从某个类别获得所有祖先直到根类别:
;WITH Parents AS (
SELECT * FROM Category
WHERE CategoryId=@CATEGORY_ID
UNION ALL SELECT c.* FROM Category c JOIN Parents p ON p.ParentCategoryId=c.CategoryId
)
SELECT Name FROM Parents;
例如,对于ID为111的类别,我得到:
Name
---------
cat 1.1.1
cat 1.1
cat 1
我需要的是一个人被分配的TimeSlots列表,并与该TimeSlot的类别名称一起连接到根类别。因此,对于ID为401的人,结果应如下所示:
TimeSlotId Name
---------------------
301 cat 1.1.1
301 cat 1.1
301 cat 1
302 cat 1.2
302 cat 1
我无法弄清楚如何组合上述两个查询,以便获得预期的结果。
我希望沿着这些方向发挥作用:
;WITH Parents AS (
SELECT * FROM Category
WHERE CategoryId=<<'How to get CategoryId for each assigned TimeSlot here?'>>
UNION ALL SELECT c.* FROM Category c JOIN Parents p ON p.ParentCategoryId=c.CategoryId
)
SELECT ts.TimeSlotId, pc.Name
FROM PersonTimeSlotAssignment
JOIN TimeSlot AS ts ON PersonTimeSlotAssignment.TimeSlotId = ts.TimeSlotId
JOIN Parents AS pc ON <<'How should this look like?'>>
WHERE PersonTimeSlotAssignment.PersonId = @PERSON_ID;
答案 0 :(得分:1)
这将处理递归类别并提供基于PersonId或TimeSlotId的所有数据:
WITH Categories (PersonId, CategoryId, ParentCategoryId, TimeSlotId, Name, BASE)
AS
(
SELECT PersonId, c.CategoryId, c.ParentCategoryId, pts.TimeSlotId, c.Name, 0 AS BASE
FROM Category c
INNER JOIN TimeSlot ts ON c.CategoryId = ts.CategoryId
INNER JOIN PersonTimeSlotAssignment pts ON ts.TimeSlotId = pts.TimeSlotId
UNION ALL
SELECT PersonId, pc.CategoryId, pc.ParentCategoryId, TimeSlotId, pc.Name, BASE + 1
FROM Category pc
INNER JOIN Categories cs ON cs.ParentCategoryId = pc.CategoryId
)
SELECT * FROM Categories
WHERE PersonId = 401
--WHERE TimeSlotId = 301
可能有更好的方式来写这个,但做了你所问的,应该把你带到你需要去的地方。 &#39; BASE&#39;不符合其原始目的,但仍显示您的人员和类别之间的相关性,例如BASE 0表示该记录中的类别直接分配给该人。所以,我为此留下了它。感谢。
答案 1 :(得分:1)
用户定义的函数和cross apply
在这种情况下非常有用。
--1. Create function
create function fn_Category(@id int)
returns table
as
return
with tbl as (
--anckor query
select CategoryId, ParentCategoryId,Name, 1 lvl
from Category where CategoryId = @id
union all
--recursive query
select c.CategoryId, c.ParentCategoryId,c.Name, lvl+1
from Category c
inner join tbl on tbl.ParentCategoryId=c.CategoryId--go up the tree
)
select * from tbl
go
--end of function
--2. and now we can use it
declare @PERSON_ID int = 401
SELECT ts.TimeSlotId, pc.Name
FROM PersonTimeSlotAssignment
JOIN TimeSlot AS ts ON PersonTimeSlotAssignment.TimeSlotId = ts.TimeSlotId
--JOIN Category AS pc ON ts.CategoryId = pc.CategoryId
--use cross apply instead
cross apply fn_Category(ts.CategoryId) pc
WHERE PersonTimeSlotAssignment.PersonId = @PERSON_ID;
答案 2 :(得分:0)
希望这可以帮助你:)
List
答案 3 :(得分:0)
DECLARE @PersonId int = 401;
如果OBJECT_ID(&#39; tempdb.dbo。#TimeSlot&#39;)不为空 DROP TABLE #TimeSlot
SELECT DISTINCT DENSE_RANK()OVER(ORDER BY t.TimeSlotId)ID,t.TimeSlotId,c.CategoryId
INTO #TimeSlot
来自PersonTimeSlotAssignment p
INNER JOIN TimeSlot t
ON p.TimeSlotId = t.TimeSlotId
INNER JOIN类别c
ON c.CategoryId = t.CategoryId
WHERE PersonId = @ PersonId
如果OBJECT_ID(&#39; tempdb.dbo。#Output&#39;)不为空
DROP TABLE #Output
CREATE TABLE #Output(timeSlotId INT,CategoryId INT)
DECLARE @id INT = 1 DECLARE @timeSlotId AS INT DECLARE @CategoryId INT
WHILE(SELECT id FROM #TimeSlot WHERE id = @id)IS NOT NULL
BEGIN
SELECT @ timeSlotId = TimeSlotId,@ CategoryId = CategoryId FROM #TimeSlot WHERE ID = @ id
INSERT INTO #Output SELECT @ timeSlotId,@ CategoryId
WHILE(从类别WHERE CategoryId = @ CategoryId中选择ParentCategoryId)不为空
BEGIN
SELECT @ CategoryId = ParentCategoryId FROM Category WHERE CategoryId = @ CategoryId
INSERT INTO #Output SELECT @ timeSlotId,@ CategoryId
END
SET @ id = @ id + 1
结束
SELECT a.timeSlotId,c.Name FROM #Output a INNER JOIN Category c ON a.CategoryId = c.CategoryId