43

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

8 Answers 8

81

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).

Sign up to request clarification or add additional context in comments.

4 Comments

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.
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.
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.
Hi, there are no similar extension for bigint?
23

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.

2 Comments

The fastest way is the sort function in the intarray contrib module.
The first solution could be written equivalently, and much more concisely, as SELECT array_agg(x ORDER BY x) FROM unnest(ARRAY[1,5,3,7,2]) AS x;
14

Just use the function unnest():

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

See array functions in the Pg docs.

Comments

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.

1 Comment

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.
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
);
$$;

2 Comments

@MartijnPieters Notice how its the same answer as mine but I answered a year ago and referenced where I got it from.
@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.
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

5 Comments

It's longer, but is it better than Craig's solution ?
I'm not sure I understand the indent of your question.. Whether it's "better" for your implementation is up for you to decide.
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. :)
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.
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. ;)
1

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)

2 Comments

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.
BTW, when elements are unique, instead of checking "contains" both ways, you can also do a length check plus a "contains" check.
0

My one-liner to order an array:

select array_agg(a order by a) from unnest(array[null, 'tt']) as a -- asc
select array_agg(a order by a desc) from unnest(array[null, 'tt']) as a -- desc

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.