排序表为null"无效"

时间:2014-07-17 03:34:28

标签: sql postgresql

我有一个包含两列的表:col_order(int)和name(text)。我想检索有序行,这样当col_order不为null时,它确定顺序,但是当它为null时,name确定顺序。我想到了一个像

这样的订单条款
order by coalesce( col_order, name )

但是,这不起作用,因为两列具有不同的类型。我正在考虑将两者都转换为bytea,但是:1)转换整数是一种更好的方法,而不仅仅是将moding循环256,并在函数中堆叠单个字节,以及2)如何转换“name”以确保一些一种理智的整理顺序(假设名称有顺序......好citext会很好,但我没有费心去重建......目前UTF8)。

即使所有这一切都是可能的(欢迎细节的建议),似乎还有很多工作要做。还有更好的方法吗?

修改

戈登得到了一个很好的答案,但它表明我没有正确地说出这个问题。我希望按name排序,其中col_order表示此订单被覆盖的地方。这不是一个很好的问题,但这是一个可接受的解决方案:

col_order| name
----------------
    null | a
       1 | foo
    null | foo1
       2 | bar

即 - 如果col_order为空名,则应在按名称最接近字母顺序后插入名称,但小于该名称。否则,可以通过以下方式获得:

order by col_order nulls last, name

编辑2

好的......为了让你的创意充满活力,这似乎正朝着正确的方向发展:

with x as ( select *, 
    case when col_order is null then null else row_number() over (order by col_order) end as ord
  from temp )
select col_order, name, coalesce( ord, lag(ord,1) over (order by name) + .5) as ord from x; 

当没有col_order时,它会从上一行获取按名称排序的顺序。一般来说这是不对的...我想我必须回到非空col_order的第一行......似乎sql标准对于窗口函数有“忽略空值”可能会这样做,但没有在postgres中实现。有什么建议吗?

编辑3

以下内容似乎很接近 - 但不起作用。对于递归查询,窗口评估可能有点奇怪。

with recursive x(col_order, name, n) as ( 
  select col_order, name, case when col_order is null then null 
      else row_number() over (order by col_order) * t end from temp, tot
  union all
  select col_order, name, 
    case when row_number() over (order by name) = 1 then 0 
      else lag(n,1) over (order by name) + 1 end from x
  where x.n is null ),
tot as ( select count(*) as t from temp )
select * from x;

2 个答案:

答案 0 :(得分:3)

只需使用多个子句:

order by (case when col_order is not null then 1 else 2 end),
         col_order,
         name

col_ordernot null时,会为第一个排序键分配1。如果是null,则会分配2。因此,not-nulls将是第一个。

答案 1 :(得分:0)

好的..以下似乎有效 - 我将留下问题"未得到答复"虽然有待批评或更好的建议:

使用here中的last_agg聚合:

with
tot as ( select count(*) as t from temp ),
x as (
    select col_order, name,  
        case when col_order is null then null 
            else (row_number() over (order by col_order)) * t end as n,
        row_number() over (order by name) - 1 as i
    from temp, tot )
select x.col_order, x.name, coalesce(x.n,last_agg(y.n order by y.i)+x.i, 0 ) as n
from x
left join x as y on y.name < x.name
group by x.col_order, x.n, x.name, x.i
order by n;