Sql server 我需要一个更可靠的CTE层次结构查询排序

Sql server 我需要一个更可靠的CTE层次结构查询排序,sql-server,tsql,Sql Server,Tsql,示例表: CREATE TABLE Fruit ( ID int identity(1,1) NOT NULL, ParentID int NULL, Name varchar(255) ); 我想按字母顺序(一级以上)对同一表中的父记录和子记录进行排序: 我试图这样做: ;WITH CTE(ID, ParentID, Name, Sort) AS ( SELECT ID ,ParentID ,Name ,

示例表:

CREATE TABLE Fruit (
  ID int identity(1,1) NOT NULL,
  ParentID int NULL,
  Name varchar(255)
);
我想按字母顺序(一级以上)对同一表中的父记录和子记录进行排序:

我试图这样做:

;WITH CTE(ID, ParentID, Name, Sort) AS
(
    SELECT 
         ID
        ,ParentID
        ,Name
        ,cast('\' + Name as nvarchar(255)) AS Sort          
    FROM Fruit
    WHERE ParentID IS NULL

    UNION ALL

    SELECT 
         a.ID
        ,a.ParentID
        ,a.Name
        ,cast(b.Sort + '\' + a.Name as nvarchar(255)) AS Sort           
    FROM Fruit a
    INNER JOIN CTE b ON a.ParentID = b.ID           
)
SELECT * FROM CTE Order by Sort
这将生成排序结果,如:

\Apples
\Apples\Green
\Apples\Green\Just Sour
\etc.
就在我认为事情很好的时候,它并不可靠。例如,如果一个项目有多个单词。比如:

\Apples
\Apples A <-- culprit
\Apples\Green
我快速做到这一点的一种简陋方法是在表中为所有记录添加一个前缀列,其值为
-
。然后我可以这样做:

;WITH CTE(ID, ParentID, Name, Sort, Prefix) AS
(
    SELECT 
         ID
        ,ParentID
        ,Name
        ,cast('\' + Name as nvarchar(255)) AS Sort  
        ,Prefix

    FROM Fruit
    WHERE ParentID IS NULL

    UNION ALL

    SELECT 
         a.ID
        ,a.ParentID
        ,a.Name
        ,cast(b.Sort + '\' + a.Name as nvarchar(255)) AS Sort
        ,cast(b.Prefix + a.Prefix as nvarchar(10)) AS Prefix


    FROM Fruit a
    INNER JOIN CTE b ON a.ParentID = b.ID           
)
SELECT * FROM CTE Order by Sort
但这似乎是错误的或不是最佳的


这些分层查询仍然让我头疼,所以也许我没有看到明显的问题

我猜你想要这个结果

\Apples
\Apples\Green
\Apples A
也许可以试试这样:

SELECT * 
FROM CTE 
ORDER BY replace(Sort, ' ', '~')
“~”
是ascii 126,您也可以使用excel排序进行检查

在这种情况下,我倾向于使用按名称排序的行数()

示例

Declare @YourTable table (id int,ParentId  int,Name varchar(50))
Insert into @YourTable values 
 ( 1, NULL,'Apples')
,( 2, 1   ,'Green')
,( 3, 2   ,'Just Sour')
,( 4, 2   ,'Really Sour')
,( 5, 1   ,'Red')
,( 6, 5   ,'Big')
,( 7, 5   ,'Small')
,( 8, NULL,'Bananas')

Declare @Top    int         = null      --<<  Sets top of Hier Try 5 
Declare @Nest   varchar(25) = '|-----'  --<<  Optional: Added for readability

;with cteP as (
      Select Seq  = cast(1000+Row_Number() over (Order by Name) as varchar(500))
            ,ID
            ,ParentId 
            ,Lvl=1
            ,Name 
      From   @YourTable 
      Where  IsNull(@Top,-1) = case when @Top is null then isnull(ParentId ,-1) else ID end
      Union  All
      Select Seq  = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.Name)) as varchar(500))
            ,r.ID
            ,r.ParentId 
            ,p.Lvl+1
            ,r.Name 
      From   @YourTable r
      Join   cteP p on r.ParentId  = p.ID)
Select A.ID
      ,A.ParentId 
      ,A.Lvl
      ,Name = Replicate(@Nest,A.Lvl-1) + A.Name
      ,Seq  --<< Can be removed
 From cteP A
 Order By Seq

问题是,空间ascii(32)比
'\'
ascii(92)更重要。您希望得到什么结果?在SQL中执行此操作看起来非常糟糕design@JoePhillips你的意思是在申请中更好?在我的例子中,我真的需要在查询中完成它。我并没有真正弄乱这个功能,但似乎您可以更改递归查询以创建FORXML()可读的内容,然后从这里分解层次结构以分别对它们进行排序。我没有代码来支持这一点。哦,这很聪明,应该是显而易见的。我问题的第二部分也在盯着我的脸看吗?如果是的话,这可能是我躲在新挖的洞里的一天。我没有看到第二部分:P只需要第一部分来解决它。第二个查询有什么问题?顺便说一句,
~
是127 ascII上最大的字符,但是如果你的字符串是扩展ascII上的字符,比如
=
,你可能需要使用
asci(254)
,事实上,我相信我可以根据你的建议来解决这个问题。不管怎样,我现在要挖洞了。哦,我明白了。是的,也许是同样的原则。但我不清楚你想要的确切结果。请记住,下次问题需要样本数据和预期输出。Nice。胡安的回答很有效。这在前面看起来更简单,并且消除了名称本身的任何问题。在不同的场景中进行测试。谢谢你的帮助。经过严格的测试后我会回来的。@user1447679胡安的答案我永远不会想到。我缺乏想象力:)好吧。。。现在,我要收回我的声明,我为自己挖了一个洞,因为我没有看到显而易见的东西。你缺乏想象力减轻了我的痛苦!我的意思当然是开玩笑的。@user1447679这就是为什么我如此喜欢它。我每天都学到一些东西。这是有趣的部分。虽然在你回答之前我已经计算了我问题的第二部分(用连字符命名),但我还是接受了这一部分,因为从技术上讲,它似乎更合理,并且回答了我的整个问题,但对Juan来说是一种荣誉,因为它促使我下次更具创造性地思考。
SELECT * 
FROM CTE 
ORDER BY replace(Sort, ' ', '~')
Declare @YourTable table (id int,ParentId  int,Name varchar(50))
Insert into @YourTable values 
 ( 1, NULL,'Apples')
,( 2, 1   ,'Green')
,( 3, 2   ,'Just Sour')
,( 4, 2   ,'Really Sour')
,( 5, 1   ,'Red')
,( 6, 5   ,'Big')
,( 7, 5   ,'Small')
,( 8, NULL,'Bananas')

Declare @Top    int         = null      --<<  Sets top of Hier Try 5 
Declare @Nest   varchar(25) = '|-----'  --<<  Optional: Added for readability

;with cteP as (
      Select Seq  = cast(1000+Row_Number() over (Order by Name) as varchar(500))
            ,ID
            ,ParentId 
            ,Lvl=1
            ,Name 
      From   @YourTable 
      Where  IsNull(@Top,-1) = case when @Top is null then isnull(ParentId ,-1) else ID end
      Union  All
      Select Seq  = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.Name)) as varchar(500))
            ,r.ID
            ,r.ParentId 
            ,p.Lvl+1
            ,r.Name 
      From   @YourTable r
      Join   cteP p on r.ParentId  = p.ID)
Select A.ID
      ,A.ParentId 
      ,A.Lvl
      ,Name = Replicate(@Nest,A.Lvl-1) + A.Name
      ,Seq  --<< Can be removed
 From cteP A
 Order By Seq
ID  ParentId    Lvl Name                      Seq
1   NULL        1   Apples                    1001
2   1           2   |-----Green               1001.1001
3   2           3   |-----|-----Just Sour     1001.1001.1001
4   2           3   |-----|-----Really Sour   1001.1001.1002
5   1           2   |-----Red                 1001.1002
6   5           3   |-----|-----Big           1001.1002.1001
7   5           3   |-----|-----Small         1001.1002.1002
8   NULL        1   Bananas                   1002