【问题标题】:Using SQL HierarchyID to select respectively from Children to Parents使用 SQL HierarchyID 分别从 Children 到 Parent 进行选择
【发布时间】:2016-08-02 19:47:28
【问题描述】:

我需要编写一个存储过程来将一些订单数据从孩子返回给父母。描述我想要做什么有点复杂,但让我试试吧: 假设我们有一个名为 Categories: Parent > Child1 > Child2 > Child3 的 Hierarchy,全部使用 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

我曾尝试使用 Recursive CTE,但找不到合适的实现方式。

【问题讨论】:

  • 使用UNION? 的多个查询?
  • @Malk,不,不,只是这两个表,我想首先检查标记为 NewsOfWashington 的新闻是否达到 10,如果不是从他的父 NewsOfWestUSA 中选择等等...

标签: sql sql-server recursive-query hierarchyid


【解决方案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  

【讨论】:

  • 我也想标记你的答案,因为这也很好,但不幸的是不能标记多个!谢谢你的回答
【解决方案2】:

完全从米奇的回答中窃取数据脚本,这还不错:

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() 方法进行排序(返回如何在层次结构的深处,给定的值是)。

【讨论】:

  • 干得好!正如你所说,我们也应该将 Mitch 的答案标记为答案,因为他也回答了它,但你的答案更简单,更容易实现。谢谢大哥。
  • 可以点击他的答案旁边的小向上箭头,让他快速获得声望。 :) 我做到了(感谢提醒!)。
  • 我这样做了,但我的声誉还不够高,无法立即反映
【解决方案3】:

尝试以下,它将按照您想要的顺序返回前 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;

【讨论】:

  • 感谢您的帮助,但问题是这些类别,'NewsOfWashington' 等是动态的,我只有 cat_id /1/1/1/1/ 如何在此完成方式?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-29
  • 2011-11-10
  • 1970-01-01
  • 2014-09-09
相关资源
最近更新 更多