使用SQL HierarchyID分别从Children到Parents选择

时间:2016-08-02 19:47:28

标签: sql sql-server recursive-query hierarchyid

我需要编写一个存储过程来返回从子项到父项的订单的一些数据。描述我想要做的事情有点复杂,但让我尝试一下: 想象一下,我们有这个层次结构叫做Categories:Parent> Child1> Child2> Child3,全部使用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

最后我想在这个条件下选择sxample十大新闻:

  

如果 NewsOfWashington 有10条新闻,请选择它,    否则从 NewsOfWestUSA 中选择,    否则请从 NewsOfUSA 中选择,    否则从新闻中选择,    直到你达到十岁

要选择的顺序是

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

我尝试过使用递归CTE,但找不到合适的方法来实现它。

3 个答案:

答案 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  

答案 1 :(得分: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;

答案 2 :(得分:0)

从Mitch的回答中完全窃取数据脚本,这不是太糟糕了:

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()方法获取给定类别所属的类别,然后根据新的类别列表加入新闻项目,最后在{{1}上排序} method(返回给定值在层次结构中的深度)。