在考虑条件的同时对表进行排序

时间:2016-06-01 12:56:22

标签: sql postgresql sorting

我有一个PostgreSQL表,并希望按案例分层次。这是一个例子:

> Name    |    Port
> ----------------------------  
> Switch1 | GigabitEthernet2/2 
> Switch1 | GigabitEthernet1/2
> Switch2 | 1.23
> Switch2 | 1.21
> Switch1 | GigabitEthernet2/1
> Switch1 | GigabitEthernet1/1/3
> Switch1 | FastEthernet1/1/14 
> Switch1 | FastEthernet3/0/19
> Switch1 | FastEthernet3/0/20
> Switch2 | Port-Channel3 
> Switch1 | GigabitEthernet3/0/4
> Switch1 | GigabitEthernet3/1/3
> Switch1 | FastEthernet3/0/2
> Switch2 | 1.14
> Switch2 | Port-Channel6

订购/分拣后应该是这样的:

> Name    |    Port
> ----------------------------  
> Switch1 | FastEthernet1/1/14 
> Switch1 | FastEthernet3/0/2
> Switch1 | FastEthernet3/0/19
> Switch1 | FastEthernet3/0/20
> Switch1 | GigabitEthernet1/2
> Switch1 | GigabitEthernet2/1
> Switch1 | GigabitEthernet2/2 
> Switch1 | GigabitEthernet1/1/3
> Switch1 | GigabitEthernet3/0/4
> Switch1 | GigabitEthernet3/1/3
> Switch2 | 1.14
> Switch2 | 1.21
> Switch2 | 1.23
> Switch2 | Port-Channel3 
> Switch2 | Port-Channel6

我试图用ORDER BY CASE做点什么。这是一个例子:

SELECT device.name, device_port.port
   FROM device
   JOIN device_port ON device.ip = device_port.ip
   ORDER BY CASE WHEN device_port.port like 'Fast%' THEN string_to_array(substr(device_port.port, position('/' in device_port.port)-1 ),'/')::float[] 
         WHEN device_port.port like 'Gigabit%' THEN string_to_array(substr(device_port.port,16),'/')::float[] 
         WHEN device_port.port like 'Port-channel%' THEN string_to_array(substr(device_port.port,13),'/')::float[] END;

但是通过这种方式,不可能首先通过device.name和端口名称对其进行排序。结果将是例如:

> Name    |    Port
> ----------------------------  
> Switch1 | FastEthernet1/1/14
> Switch1 | GigabitEthernet1/2
> Switch1 | FastEthernet3/0/2
> Switch1 | GigabitEthernet3/0/4

等等......

2 个答案:

答案 0 :(得分:2)

使用regexp_replace()

在示例中,为简单起见,我使用一个表格,并选择port_nameport_number以便更好地理解。

select 
    *, 
    regexp_replace(port, '[0-9/\.]', '', 'g') port_name,
    string_to_array(regexp_replace(port, '[A-Za-z\-]', '', 'g'), '/')::float[] port_number
from 
    device
order by 
    name, 
    regexp_replace(port, '[0-9/\.]', '', 'g'),
    string_to_array(regexp_replace(port, '[A-Za-z\-]', '', 'g'), '/')::float[];

  name   |         port         |    port_name    | port_number 
---------+----------------------+-----------------+-------------
 Switch1 | FastEthernet1/1/14   | FastEthernet    | {1,1,14}
 Switch1 | FastEthernet3/0/2    | FastEthernet    | {3,0,2}
 Switch1 | FastEthernet3/0/19   | FastEthernet    | {3,0,19}
 Switch1 | FastEthernet3/0/20   | FastEthernet    | {3,0,20}
 Switch1 | GigabitEthernet1/1/3 | GigabitEthernet | {1,1,3}
 Switch1 | GigabitEthernet1/2   | GigabitEthernet | {1,2}
 Switch1 | GigabitEthernet2/1   | GigabitEthernet | {2,1}
 Switch1 | GigabitEthernet2/2   | GigabitEthernet | {2,2}
 Switch1 | GigabitEthernet3/0/4 | GigabitEthernet | {3,0,4}
 Switch1 | GigabitEthernet3/1/3 | GigabitEthernet | {3,1,3}
 Switch2 | 1.14                 |                 | {1.14}
 Switch2 | 1.21                 |                 | {1.21}
 Switch2 | 1.23                 |                 | {1.23}
 Switch2 | Port-Channel3        | Port-Channel    | {3}
 Switch2 | Port-Channel6        | Port-Channel    | {6}
(15 rows)

选项int[]

我以为你故意使用过类型float[]。如果您想比较int[]值(并将浮点数转换为int数组),您可以动态使用translate()

select 
    *, 
    regexp_replace(port, '[0-9/\.]', '', 'g') port_name,
    string_to_array(
        regexp_replace(
            translate(port, '.', '/'),
            '[A-Za-z\-]', '', 'g'),
        '/')::int[] port_number
from 
    device
order by 
    name, 
    regexp_replace(port, '[0-9/\.]', '', 'g'),
    string_to_array(
        regexp_replace(
            translate(port, '.', '/'),
            '[A-Za-z\-]', '', 'g'),
        '/')::int[];

  name   | port | port_name | port_number 
---------+------+-----------+-------------
 Switch2 | 1.1  |           | {1,1}
 Switch2 | 1.2  |           | {1,2}
 Switch2 | 1.14 |           | {1,14}
 Switch2 | 1.21 |           | {1,21}
 Switch2 | 1.23 |           | {1,23}
(5 rows)

答案 1 :(得分:0)

@klin:哇这么简单的代码,几乎按预期工作!非常感谢!不知道这样的正则表达式函数是否存在。

唯一缺少的是将{1.14}分离为像{1,14}这样的数组。

目前,在这种情况下,排序失败了:

> Name    |    Port
> ------------------
> Switch2 | 1.1
> Switch2 | 1.18
> Switch2 | 1.19
> Switch2 | 1.2
> Switch2 | 1.20
> Switch2 | 1.21

但应该像

> Name    |    Port
> ------------------
> Switch2 | 1.1
> Switch2 | 1.2
> Switch2 | 1.18
> Switch2 | 1.19
> Switch2 | 1.20
> Switch2 | 1.21

我认为解决方案应该是这样的:

order by 
    name, 
    regexp_replace(port, '[0-9/\.]', '', 'g'),
    string_to_array(regexp_replace(port, '[A-Za-z\-]', '', 'g'), '/')::float[],
    string_to_array(regexp_matches(port, '**finding a . in the string**', **give back full string matching pattern** , 'g'), '.')::float[];

问题:

1)你能帮我构建一个regexp_matches函数,以便在找到一个点时总是返回完整的字符串吗?

2)正则表达式替换表达式中的''是什么意思?它通常应该提供替换模式。

再次感谢您的帮助!