如何在sql中对链表进行排序?

Nun*_*o G 17 sql sql-server linked-list

我已将链表实现为自引用数据库表:

CREATE TABLE LinkedList(
    Id bigint NOT NULL,
    ParentId bigint NULL,
    SomeData nvarchar(50) NOT NULL) 
Run Code Online (Sandbox Code Playgroud)

其中Id是主键,ParentId是列表中上一个节点的Id.第一个节点有ParentId = NULL.

我现在想要从表中进行SELECT,按照它们应该出现的顺序对行进行排序,作为列表中的节点.

例如:如果表包含行

Id      ParentId  SomeData
24971   NULL      0
38324   24971     1
60088   60089     3
60089   38324     2
61039   61497     5
61497   60088     4
109397  109831    7
109831  61039     6
Run Code Online (Sandbox Code Playgroud)

然后使用标准对其进行排序,结果应该是:

Id      ParentId  SomeData
24971   NULL      0
38324   24971     1
60089   38324     2
60088   60089     3
61497   60088     4
61039   61497     5
109831  61039     6
109397  109831    7
Run Code Online (Sandbox Code Playgroud)

你应该使用SomeData colum作为控件,所以请不要作为SomeData的ORDER作弊 :-)

Nun*_*o G 11

我找到了一个SQLServer的解决方案,但看起来比Quassnoi更大,更不优雅

WITH SortedList (Id, ParentId, SomeData, Level)
AS
(
  SELECT Id, ParentId, SomeData, 0 as Level
    FROM LinkedList
   WHERE ParentId IS NULL
  UNION ALL
  SELECT ll.Id, ll.ParentId, ll.SomeData, Level+1 as Level
    FROM LinkedList ll
   INNER JOIN SortedList as s
      ON ll.ParentId = s.Id
)

SELECT Id, ParentId, SomeData
  FROM SortedList
 ORDER BY Level
Run Code Online (Sandbox Code Playgroud)

  • 实际上,SortedList 这个名称有点令人困惑 - 实际上并没有排序 - 只是递归......你需要按最终的 SELECT 进行排序(请参阅我的回复) (2认同)
  • 使用 CTE 存在一个问题,即递归限制。默认情况下,此限制为 100,但您可以通过在查询末尾添加“选项 (maxrecursion 32767)”将其增加到 32767 (2认同)

Qua*_*noi 10

在Oracle中:

SELECT Id, ParentId, SomeData
FROM (
  SELECT ll.*, level AS lvl
  FROM LinkedList ll
  START WITH
    ParentID IS NULL
  CONNECT BY
    ParentId = PRIOR Id
)
ORDER BY
  lvl
Run Code Online (Sandbox Code Playgroud)

PS使用NULLas 是一种不好的做法ParentID,因为索引无法搜索.插入id为0-1代替的代理根,然后使用START WITH ParentID = 0.


Mar*_*ell 5

(编辑:d哦!我正在调试你发现它!)

在SQL Server中:

;WITH cte (Id, ParentId, SomeData, [Level]) AS (
    SELECT Id, ParentId, SomeData, 0
    FROM LinkedList
    WHERE ParentId IS NULL
    UNION ALL
    SELECT ll.Id, ll.ParentId, ll.SomeData, cte.[Level] + 1
    FROM LinkedList ll
    INNER JOIN cte ON ll.ParentID = cte.ID
)
SELECT * FROM cte
ORDER BY [Level]
Run Code Online (Sandbox Code Playgroud)

  • @Greg因为CTE很挑剔; 你几乎总是需要领导; 所以我不妨添加它.在示例上方添加*任何*,它会变得繁荣 (3认同)

sir*_*rex 5

PostgreSQL 版本。

创建表、索引和数据:

DROP TABLE IF EXISTS LinkedList;

CREATE TABLE LinkedList (
    Id BIGINT NOT NULL,
    ParentId BIGINT NULL,
    SomeData VARCHAR(50)
);

CREATE INDEX LinkedList_Id_idx on LinkedList (Id);
CREATE index LinkedList_ParentId_idx on LinkedList (ParentId);

INSERT INTO LinkedList
    (Id, ParentId, SomeData)
VALUES 
    (24971,   NULL,      0),
    (38324,   24971,     1),
    (60088,   60089,     3),
    (60089,   38324,     2),
    (61039,   61497,     5),
    (61497,   60088,     4),
    (109397,  109831,    7),
    (109831,  61039,     6);
Run Code Online (Sandbox Code Playgroud)

实际查询:

WITH RECURSIVE SortedList AS (
    SELECT
        *,
        0 AS SortKey
    FROM LinkedList
    WHERE ParentId IS NULL
    UNION ALL (
        SELECT
            LinkedList.*,
            SortedList.SortKey + 1 AS SortKey
        FROM LinkedList
        INNER JOIN SortedList
            ON (LinkedList.ParentId = SortedList.Id)
    )
)
SELECT
    *
FROM SortedList
ORDER BY SortKey;
Run Code Online (Sandbox Code Playgroud)

结果:

   id   | parentid | somedata | sortkey
--------+----------+----------+---------
  24971 |          | 0        |       0
  38324 |    24971 | 1        |       1
  60089 |    38324 | 2        |       2
  60088 |    60089 | 3        |       3
  61497 |    60088 | 4        |       4
  61039 |    61497 | 5        |       5
 109831 |    61039 | 6        |       6
 109397 |   109831 | 7        |       7
Run Code Online (Sandbox Code Playgroud)

还做了一些基准测试:

\set N 10000

DELETE FROM LinkedList;
INSERT INTO LinkedList VALUES (1, NULL, 1);
INSERT INTO LinkedList (
    SELECT
        generate_series AS Id,
        (generate_series - 1) AS ParentId,
        generate_series AS SomeData
    FROM GENERATE_SERIES(2, :N)
);

EXPLAIN ANALYZE
WITH RECURSIVE SortedList AS (
    SELECT
        *,
        0 AS SortKey
    FROM LinkedList
    WHERE ParentId IS NULL
    UNION ALL (
        SELECT
            LinkedList.*,
            SortedList.SortKey + 1 AS SortKey
        FROM LinkedList
        INNER JOIN SortedList
            ON (LinkedList.ParentId = SortedList.Id)
    )
)
SELECT
    *
FROM SortedList
ORDER BY SortKey;
Run Code Online (Sandbox Code Playgroud)

结果:

Sort  (cost=6236.12..6300.16 rows=25616 width=138) (actual time=17857.640..17858.207 rows=10000 loops=1)
  Sort Key: sortedlist.sortkey
  Sort Method: quicksort  Memory: 1166kB
  CTE sortedlist
    ->  Recursive Union  (cost=4.40..2007.10 rows=25616 width=138) (actual time=0.032..17844.139 rows=10000 loops=1)
          ->  Bitmap Heap Scan on linkedlist  (cost=4.40..42.78 rows=16 width=138) (actual time=0.031..0.032 rows=1 loops=1)
                Recheck Cond: (parentid IS NULL)
                Heap Blocks: exact=1
                ->  Bitmap Index Scan on linkedlist_parentid_idx  (cost=0.00..4.40 rows=16 width=0) (actual time=0.006..0.006 rows=2 loops=1)
                      Index Cond: (parentid IS NULL)
          ->  Hash Join  (cost=5.20..145.20 rows=2560 width=138) (actual time=0.896..1.780 rows=1 loops=10000)
                Hash Cond: (linkedlist_1.parentid = sortedlist_1.id)
                ->  Seq Scan on linkedlist linkedlist_1  (cost=0.00..96.00 rows=3200 width=134) (actual time=0.002..0.784 rows=10000 loops=10000)
                ->  Hash  (cost=3.20..3.20 rows=160 width=12) (actual time=0.001..0.001 rows=1 loops=10000)
                      Buckets: 1024  Batches: 1  Memory Usage: 9kB
                      ->  WorkTable Scan on sortedlist sortedlist_1  (cost=0.00..3.20 rows=160 width=12) (actual time=0.000..0.001 rows=1 loops=10000)
  ->  CTE Scan on sortedlist  (cost=0.00..512.32 rows=25616 width=138) (actual time=0.034..17851.344 rows=10000 loops=1)
Planning Time: 0.163 ms
Execution Time: 17858.957 ms
Run Code Online (Sandbox Code Playgroud)

所以这个查询非常慢。