41

I want to write a stored procedure that gets an array as input parameter and sort that array and return the sorted array.

Matt Hampel
  • 5,088
  • 12
  • 52
  • 78
giri
  • 411
  • 1
  • 4
  • 3

7 Answers7

74

The best way to sort an array of integers is without a doubt to use the intarray extension, which will do it much, much, much faster than any SQL formulation:

CREATE EXTENSION intarray;

SELECT sort( ARRAY[4,3,2,1] );

A function that works for any array type is:

CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)
RETURNS ANYARRAY LANGUAGE SQL
AS $$
SELECT ARRAY(SELECT unnest($1) ORDER BY 1)
$$;

(I've replaced my version with Pavel's slightly faster one after discussion elsewhere).

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • Craig I voted to delete my answer but for some reason I can't self terminate ;) . Thus I made a note to see your answer. – Adam Gent Sep 17 '13 at 01:59
  • If I'm not wrong, the `array_sort()` function above should be defined with the `STABLE` (or perhaps `IMMUTABLE`) tag after the second `$$`. Other programmers may look at such volatility-labels for hints about how a given function works. `LANGUAGE SQL`-functions are (typically) inlined by Postgres, but there's no harm in adding the label either way. – Seldom 'Where's Monica' Needy Dec 18 '15 at 07:10
  • 2
    Keep in mind that `unnest` will unnest all levels of an array: so if you are trying to sort an array of arrays, then you will have unexpected results: the shape of the arrays are maintained, but the values are sorted individually, not as a sub-array. – Matthew Schinckel Apr 27 '17 at 07:09
  • 5
    Hi, there are no similar extension for **bigint**? – Peter Krauss Aug 13 '18 at 08:37
20

In PostrgreSQL 8.4 and up you can use:

select array_agg(x) from (select unnest(ARRAY[1,5,3,7,2]) AS x order by x) as _;

But it will not be very fast.


In older Postgres you can implement unnest like this

CREATE OR REPLACE FUNCTION unnest(anyarray)
  RETURNS SETOF anyelement AS
$BODY$
SELECT $1[i] FROM
    generate_series(array_lower($1,1),
                    array_upper($1,1)) i;
$BODY$
  LANGUAGE 'sql' IMMUTABLE

And array_agg like this:

CREATE AGGREGATE array_agg (
        sfunc = array_append,
        basetype = anyelement,
        stype = anyarray,
        initcond = '{}'
);

But it will be even slower.


You can also implement any sorting algorithm in pl/pgsql or any other language you can plug in to postgres.

Tometzky
  • 22,573
  • 5
  • 59
  • 73
14

Just use the function unnest():

SELECT 
    unnest(ARRAY[1,2]) AS x
ORDER BY 
    x DESC;

See array functions in the Pg docs.

Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
Frank Heikens
  • 117,544
  • 24
  • 142
  • 135
8

This worked for me from http://www.pgsql.cz/index.php/PostgreSQL_SQL_Tricks_I#General_array_sort

CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)
RETURNS ANYARRAY LANGUAGE SQL
AS $$
SELECT ARRAY(
    SELECT $1[s.i] AS "foo"
    FROM
        generate_series(array_lower($1,1), array_upper($1,1)) AS s(i)
    ORDER BY foo
);
$$;

Please see Craig's answer since he is far more more knowledgable on Postgres and has a better answer. Also if possible vote to delete my answer.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • 1
    I got that wiki article updated with some more modern techniques, so please use the code in the article (or see my answer), not the older one shown here. – Craig Ringer Oct 14 '12 at 03:37
3

Very nice exhibition of PostgreSQL's features is general procedure for sorting by David Fetter.

CREATE OR REPLACE FUNCTION array_sort (ANYARRAY)
RETURNS ANYARRAY LANGUAGE SQL
AS $$
SELECT ARRAY(
    SELECT $1[s.i] AS "foo"
    FROM
        generate_series(array_lower($1,1), array_upper($1,1)) AS s(i)
    ORDER BY foo
);
$$;
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Shankar
  • 846
  • 8
  • 24
  • @MartijnPieters Notice how its the same answer as mine but I answered a year ago and referenced where I got it from. – Adam Gent Sep 26 '12 at 20:19
  • 1
    @AdamGent: I think I found this in the first answer queue and only reformatted the SQL to make it readable. In other words, I didn't have any context other than unreadable formatting. – Martijn Pieters Sep 26 '12 at 20:23
3

If you're looking for a solution which will work across any data-type, I'd recommend taking the approach laid out at YouLikeProgramming.com.

Essentially, you can create a stored procedure (code below) which performs the sorting for you, and all you need to do is pass your array to that procedure for it to be sorted appropriately.

I have also included an implementation which does not require the use of a stored procedure, if you're looking for your query to be a little more transportable.

Creating the stored procedure

DROP FUNCTION IF EXISTS array_sort(anyarray);
CREATE FUNCTION
  array_sort(
    array_vals_to_sort anyarray
  )
  RETURNS TABLE (
    sorted_array anyarray
  )
  AS $BODY$
    BEGIN
      RETURN QUERY SELECT
        ARRAY_AGG(val) AS sorted_array
      FROM
        (
          SELECT
            UNNEST(array_vals_to_sort) AS val
          ORDER BY
            val
        ) AS sorted_vals
      ;
    END;
  $BODY$
LANGUAGE plpgsql;

Sorting array values (works with any array data-type)

-- The following will return: {1,2,3,4}
SELECT ARRAY_SORT(ARRAY[4,3,2,1]);

-- The following will return: {in,is,it,on,up}
SELECT ARRAY_SORT(ARRAY['up','on','it','is','in']);

Sorting array values without a stored procedure

In the following query, simply replace ARRAY[4,3,2,1] with your array or query which returns an array:

WITH
  sorted_vals AS (
    SELECT
      UNNEST(ARRAY[4,3,2,1]) AS val
    ORDER BY
      val
  )
SELECT
  ARRAY_AGG(val) AS sorted_array
FROM
  sorted_vals

... or ...

SELECT
  ARRAY_AGG(vals.val) AS sorted_arr
FROM (
  SELECT
    UNNEST(ARRAY[4,3,2,1]) AS val
  ORDER BY
    val
) AS vals
Joshua Burns
  • 8,268
  • 4
  • 48
  • 61
  • It's longer, but is it better than Craig's solution ? – Skippy le Grand Gourou Oct 01 '14 at 16:54
  • I'm not sure I understand the indent of your question.. Whether it's "better" for your implementation is up for you to decide. – Joshua Burns Oct 02 '14 at 19:55
  • Well, Craig provides a 5 lines working solution. Almost one year later, you add an answer with a much longer solution. So I guess your solution must be somehow better, or work in some cases where the other doesn't. I'm a (Postgre)SQL newbie, so I can't judge, therefore I ask. :) – Skippy le Grand Gourou Oct 02 '14 at 20:06
  • I haven't ran a stress test to compare the method I've listed vs. Craig's.. If I get a chance, I might just do that though. :) I did add that sorting can also be performed without the need of creating a stored procedure which may be useful to some. – Joshua Burns Oct 06 '14 at 02:06
  • Right, of course I was just referring to the stored procedure. Not necessarily in terms of a stress test, but possibly in terms of "design" or whatever. But if you don't know, no problem, it's just another solution, maybe someone else will come later to comment on one or the other. ;) – Skippy le Grand Gourou Oct 06 '14 at 09:03
0

I'm surprised no-one has mentioned the containment operators:

select array[1,2,3] <@ array[2,1,3] and array[1,2,3] @> array[2,1,3];
 ?column?
══════════
 t
(1 row)

Notice that this requires that all elements of the arrays must be unique.

(If a contains b and b contains a, they must be the same if all elements are unique)

mollerhoj
  • 1,298
  • 1
  • 10
  • 18
  • Not true - select array[1, 1, 1, 1, 1, 2, 3, 3 ,2,3] <@ array[2,1,3] and array[1,2,3] @> array[2,1,3]; Result t - see fiddle [here](https://stackoverflow.com/questions/2913368/sorting-array-elements). – Vérace Jan 29 '23 at 03:04