使用SQL对具有预定义覆盖值的列表进行排序

使用SQL对具有预定义覆盖值的列表进行排序,sql,postgresql,sorting,Sql,Postgresql,Sorting,业务上的问题有点迟钝,所以我不想谈细节 我必须为一组键提出一个排序索引,但是其中一些键在索引中有一个预先确定的位置,必须遵守这个位置。其余的钥匙必须按正常顺序排列,但“围绕”预先确定的钥匙 简单的例子是对字母A到E进行排序,但A必须是位置3,D必须是位置1。我想要达到的结果是: A: 3 B: 2 C: 4 D: 1 E: 5 要设置样本的DDL: CREATE TABLE test.element (element_key TEXT, override_sort_idx INTEGER);

业务上的问题有点迟钝,所以我不想谈细节

我必须为一组键提出一个排序索引,但是其中一些键在索引中有一个预先确定的位置,必须遵守这个位置。其余的钥匙必须按正常顺序排列,但“围绕”预先确定的钥匙

简单的例子是对字母A到E进行排序,但A必须是位置3,D必须是位置1。我想要达到的结果是:

A: 3 B: 2 C: 4 D: 1 E: 5
要设置样本的DDL:

CREATE TABLE test.element (element_key TEXT, override_sort_idx INTEGER);

insert into test.element VALUES ('A', 3), ('B', Null), ('C', NULL), ('D', 1), ('E', NULL);
我能想到的最好的解决方案是这个,但尽管它似乎适用于这个简单的示例,但在一般情况下它会出错-如果您添加一些预定义的值,它就会崩溃[编辑-在这个示例中它甚至不起作用,因为A显示为4-道歉]:

WITH inner_sort AS (SELECT element_key, override_sort_idx, row_number()
OVER (ORDER BY element_key) AS natural_sort_idx
              FROM test.element)
SELECT element_key, row_number()
    OVER
     (ORDER BY
      CASE
      WHEN override_sort_idx IS NULL
      THEN natural_sort_idx
      ELSE override_sort_idx END) AS hybrid_sort
FROM inner_sort;

对于一个在一般情况下有效的解决方案,有什么想法吗?

事实证明,这是一个我最初预期的挑战

但此SQL将返回预期结果:

您可以在SQLFiddle上测试它

使用的技巧?
获取在删除重写后仍然可用的索引号列表。(使用除外)
然后为这些数字以及未被覆盖的数字获取行号。 加入排号上的人。

然后将覆盖的粘贴到它。

发布此内容,因为它可以工作,我认为这是我能做的最好的,但它非常可怕

WITH grouped AS (
   SELECT element_key, override_sort_idx, 
          row_number() OVER (
                             PARTITION BY override_sort_idx IS NULL
                             ORDER BY override_sort_idx, element_key) 
                                                        AS group_idx,
          row_number() OVER (ORDER BY element_key)      AS natural_sort_idx
    FROM test.element),
 remaining_idx AS (
    SELECT row_number() OVER () AS remain_idx FROM test.element
    EXCEPT
    SELECT override_sort_idx FROM test.element),
 indexed_remaining AS (
    SELECT row_number() OVER (ORDER BY remain_idx) AS r_sort_idx, 
           remain_idx 
    FROM remaining_idx)
SELECT g.element_key, 
       coalesce(g.override_sort_idx, r.remain_idx) AS hybrid_index
FROM grouped g
LEFT JOIN indexed_remaining r ON 
     (CASE WHEN g.override_sort_idx IS NULL 
           THEN g.group_idx END = r.r_sort_idx)
ORDER BY hybrid_index
这涉及到首先创建“剩余”索引值,作为简单行数()和预定义索引值之间的差值,然后将其连接到没有预定义索引值的键的排序列表

考虑到合并的顺序,JOIN中的
CASE
语句在功能上是不必要的,但它似乎是“更纯粹”的方法


我有一种感觉,比我聪明的人,能够正确理解窗口函数,可以使用带过滤器的窗口函数,或者操纵窗口函数的范围来编写它,如果没有疯狂的嵌套子查询/CTE和联接。

这将使元素_key'A'具有覆盖_sort _idx=3和混合_sort=4?是的,我认为我的解决方案是垃圾……我非常希望它能工作!但是我向表中添加了更多的值,这些值跳过了元素,还添加了超出自然范围的索引值(这可能会扭曲前面提到的原始问题)。我的全集现在是('A'3),('B',NULL),('C',NULL),('D',1),('E',NULL),('G',12),('H',4),('I',NULL),('J',NULL),('K',NULL),('L',NULL)。您的查询给出C为4,H为5,G为11。我写了一些我很不好意思发布的东西,有五个嵌套的CTE、一个并集和一个例外,它接受了非预定键、“剩余”索引值、缝合、并集返回..@enjayaitch我晚饭后会看一看。到那时,也许其他人找到了更好的解决办法。这是一个特殊的难题。顺便说一句,您应该标记您正在使用的数据库类型。DBMS通常有一些特定的技巧不是ANSI SQL。@enjayaitch更新。现在它也适用于较大的样本。比我想象的要难。用累积金额和抽签的方式尝试。但最后,结果与你的方法相似。也就是说,获取剩余的索引,然后用它做一些事情。肯定比我的更好(更容易阅读,更少冗余,更好解释)。真的觉得这应该是一个三线,一个窗口功能的工作-我很高兴我不是唯一一个没有找到解决方案!非常感谢。顺便说一句,就像你的网站一样。我认为不需要在
OPENNUMBERS
查询中的
row\u number()
函数中使用
ORDER by override\u sort\u idx
。看似微不足道,但当我现在将其应用于我的实际数据和业务问题时,解释中的差异非常明显。
WITH grouped AS (
   SELECT element_key, override_sort_idx, 
          row_number() OVER (
                             PARTITION BY override_sort_idx IS NULL
                             ORDER BY override_sort_idx, element_key) 
                                                        AS group_idx,
          row_number() OVER (ORDER BY element_key)      AS natural_sort_idx
    FROM test.element),
 remaining_idx AS (
    SELECT row_number() OVER () AS remain_idx FROM test.element
    EXCEPT
    SELECT override_sort_idx FROM test.element),
 indexed_remaining AS (
    SELECT row_number() OVER (ORDER BY remain_idx) AS r_sort_idx, 
           remain_idx 
    FROM remaining_idx)
SELECT g.element_key, 
       coalesce(g.override_sort_idx, r.remain_idx) AS hybrid_index
FROM grouped g
LEFT JOIN indexed_remaining r ON 
     (CASE WHEN g.override_sort_idx IS NULL 
           THEN g.group_idx END = r.r_sort_idx)
ORDER BY hybrid_index