2016-08-02 69 views
1

我需要编写一个存储过程来返回从儿童到父母的订单上的一些数据。描述我想要做什么很复杂,但让我试试它: 假设我们有这样的层次结构称为分类:家长> Child1> CHILD2> Child3,所有存储在使用SQL HIERARCHYID:使用SQL HierarchyID分别从儿童到父母选择

Category Table 
-------------- 
Cat_ID    | Cat_Name 
.............................. 
/1/    | News 
/1/1/    | NewsOfUSA 
/1/1/1/   | NewsOfWestUSA 
/1/1/1/1/   | NewsOfWashington 

我们已经保存的新闻与这些分类如下:

News Table 
------------- 
News_ID  | FK_Cat_ID | News_Content 
......................................... 
0001  | /1/   | one 
0002  | /1/1/  | two 
0003  | /1/1/1/  | three 
0004  | /1/1/1/1/ | four1 
0005  | /1/1/1/1/ | four2 
0006  | /1/1/1/1/ | four3 
0007  | /1/1/1/1/ | four4 

最后我想选择“样本” p十大新闻与此条件:

如果NewsOfWashington有10个消息,然后选择它, 别的选择NewsOfWestUSA, 别的选择NewsOfUSA, 别的选择新闻, 直到您达到十个

而且要选择的顺序是这样的

four4,four3,four2,four1,three,two,one 

我试过使用递归CTE,但找不到实现它的正确方法。

+0

通过'UNION'使用多个查询? – Malk

+0

@Malk,不,不,不要,只是这两个表我想先检查,如果新闻标记与NewsOfWashington达到10或不,如果不从他的父母NewsOfWestUSA选择等等... – Yasin

回答

0

完全窃取数据脚本米奇”的回答,这是不是太糟糕了:

CREATE TABLE Categories 
    (
     CatID hierarchyid not null 
         primary key , 
     Name nvarchar(255) not null 
    ); 
CREATE TABLE News 
    (
     NewsID int not null 
       primary key , 
     CatID hierarchyid not null , 
     NewsContent nvarchar(max) not null 
    ); 

INSERT INTO Categories 
VALUES ('/1/', 'News'), 
    ('/1/1/', 'NewsOfUSA'), 
    ('/1/1/1/', 'NewsOfIndiana'), 
    ('/1/2/', 'NewsOfUK'); 

INSERT INTO News 
VALUES (1, '/1/', 'Aliens invaded'), 
    (2, '/1/1/', 'Aliens invaded the US'), 
    (3, '/1/1/1/', 'Aliens invaded the midwest'), 
    (4, '/1/2/', 'Aliens invaded the UK'); 

-- actual answer begins here 
select TOP(10) News.[NewsContent] 
from dbo.Categories as parent 
join dbo.Categories as child 
    on child.CatID.IsDescendantOf(parent.CatID) = 1 
join News 
    on News.CatID = parent.CatID 
WHERE child.Name = 'NewsOfIndiana' 
order by News.CatID.GetLevel() DESC 

从本质上讲,我使用的IsDescendentOf()方法获取给定类别属于哪个类别,并基于类别的和新的名单上加入对新闻项目最后在GetLevel()方法上进行排序(这会返回给定值的层次结构深度)。

+0

干得好!正如你所说的,我们也应该将米奇的回答也标记为答案,因为他也回答了这个问题,但是你的方法在某种程度上简单易行。多谢兄弟。 – Yasin

+0

你*可以*击中他的回答旁边的小箭头,以迅速击中他的声望。 :)我做了(感谢提醒!)。 –

+1

我是这么做的,但是我的名声还不够高,无法反映出来 – Yasin

0

尝试之后,它会在顺序返回TOP 10,你想:

SELECT TOP 10 CASE n.FK_Cat_ID 
WHEN '/1/1/1/1/' THEN 0 
WHEN '/1/1/1/' THEN 1 
WHEN '/1/1/' THEN 2 
WHEN '/1/' THEN 3 
Else 4 END,* 
FROM News as n 
INNER JOIN Category as c 
ON n.FK_Cat_ID = c.Cat_ID 
ORDER BY 1; 
+0

我感谢您的帮助,但问题是,这些类别,'NewsOfWashington'等是动态的,我只是有cat_id/1/1/1/1/ 这怎么能这样做呢? – Yasin

+0

这很容易修复 –

1

要识别的距离,寻找所有的后代,然后排序的深度差:

USE tempdb; 

CREATE TABLE Categories (CatID hierarchyid not null primary key, Name nvarchar(255) not null); 
CREATE TABLE News (NewsID int not null primary key, CatID hierarchyid not null, NewsContent nvarchar(max) not null); 

INSERT INTO Categories 
VALUES ('/1/', 'News'), 
    ('/1/1/', 'NewsOfUSA'), 
    ('/1/1/1/', 'NewsOfIndiana'), 
    ('/1/2/', 'NewsOfUK'); 

INSERT INTO News 
VALUES (1, '/1/', 'Aliens invaded'), 
    (2, '/1/1/', 'Aliens invaded the US'), 
    (3, '/1/1/1/', 'Aliens invaded the midwest'), 
    (4, '/1/2/', 'Aliens invaded the UK'); 

DECLARE @VisitorLocation hierarchyid = '/1/1/1/'; 

WITH 
relevantCategories AS (
    SELECT c.*, ABS(@VisitorLocation.GetLevel() - c.CatID.GetLevel()) as RelevanceDistance 
    FROM Categories c 
    WHERE @VisitorLocation.IsDescendantOf(c.CatID) = 1 
) 
SELECT TOP(10) n.*, c.RelevanceDistance 
FROM relevantCategories c 
INNER JOIN News n on n.CatID = c.CatID 
ORDER BY RelevanceDistance ASC, n.NewsID DESC; 

DROP TABLE Categories; 
DROP TABLE News; 

生产:

NewsID CatID   NewsContent   RelevanceDistance 
-------- -------- ---------------------------- ------------------- 
     3 0x5AD6 Aliens invaded the midwest     0 
     2 0x5AC0 Aliens invaded the US      1 
     1 0x58  Aliens invaded        2 
+0

我也希望你的答案也是答案,因为这也很好,但不幸的是它不可能标记多个! 感谢您的回答 – Yasin