ORDER BY the IN value list
You can do it quite easily with (introduced in PostgreSQL 8.2) VALUES (), ().
Syntax will be like this:
select c.*
from comments c
join (
values
(1,1),
(3,2),
(2,3),
(4,4)
) as x (id, ordering) on c.id = x.id
order by x.ordering
How to respect the order of an array in a PostgreSQL select sentence
For long arrays you typically get (much!) more efficient query plans with unnesting the array and joining to the main table. In simple cases, this even preserves the original order of the array without adding ORDER BY
. Rows are processed in order. But there are no guarantees and the order may be broken with more joins or with parallel execution etc. To be sure, add WITH ORDINALITY
:
CREATE OR REPLACE FUNCTION list_products (tid int[]) -- VARIADIC?
RETURNS TABLE (
id integer,
reference varchar,
price decimal(13,4)
)
LANGUAGE sql STABLE AS
$func$
SELECT product_id, p.reference, p.price
FROM unnest(tid) WITH ORDINALITY AS t(product_id, ord)
JOIN product p USING (product_id) -- LEFT JOIN ?
ORDER BY t.ord
$func$;
Fast, simple, safe. See:
- PostgreSQL unnest() with element number
- Join against the output of an array unnest without creating a temp table
You might want to throw in the modifier VARIADIC
, so you can call the function with an array or a list of IDs (max 100 items by default). See:
- Return rows matching elements of input array in plpgsql function
- Call a function with composite type as argument from native query in jpa
- Function to select column values by comparing against comma separated list
I would declare STABLE
function volatility.
You might use LEFT JOIN
instead of JOIN
to make sure that all given IDs are returned - with NULL values if a row with given ID has gone missing.
db<>fiddle here
Note a subtle logic difference with duplicates in the array. While product_id
is UNIQUE
...
- unnest + left join returns exactly one row for every given ID - preserving duplicates in the given IDs if any.
product_id = any (tid)
folds duplicates. (One of the reasons it typically results in more expensive query plans.)
If there are no dupes in the given array, there is no difference. If there can be duplicates and you want to fold them, your task is ambiguous, as it's undefined which position to keep.
PostgreSQL - order by an array
CREATE OR REPLACE FUNCTION search_by_tags(tags varchar[])
RETURNS TABLE (id_course integer, name text, tag_ct integer)
LANGUAGE sql AS
$func$
SELECT id_course, c.name, ct.tag_ct
FROM (
SELECT tc.id_course, count(*)::int AS tag_ct
FROM unnest($1) x(tag)
JOIN tagcourse tc USING (tag)
GROUP BY 1 -- first aggregate ..
) AS ct
JOIN course c USING (id_course) -- .. then join
ORDER BY ct.tag_ct DESC -- more columns to break ties?
$func$;
Use unnest()
to produce a table from your input array, like already demonstrated by @Clodoaldo.
You don't need plpgsql for this. Simpler with a plain SQL function.
I use unnest($1)
(with positional parameter) instead of unnest(tags)
, since the later is only valid for PostgreSQL 9.2+ in SQL functions (unlike plpgsql). The manual:
In the older numeric approach, arguments are referenced using the
syntax$n
:$1
refers to the first input argument,$2
to the second,
and so on. This will work whether or not the particular argument was
declared with a name.
count()
returns bigint
. You need to cast it to int
to match the declared return type or declare the the returned column as bigint
to begin with.
Perfect occasion to simplify the syntax a bit with USING
(equi-joins): USING (tag)
instead of ON tc.tag = c.tag
.
It's regularly faster to first aggregate, then join to another table. Reduces the needed join operations.
To address @Clodoaldo's comments, here is a fiddle demonstrating the difference:
db<>fiddle here
Old sqlfiddle
OTOH, if you aggregate after the join, you don't need a subquery. Shorter, but probably slower:
SELECT c.id_course, c.name, count(*)::int AS tag_ct
FROM unnest($1) x(tag)
JOIN tagcourse tc USING (tag)
JOIN course c USING (id_course)
GROUP BY 1
ORDER BY 3 DESC; -- more columns to break ties?
Postgresql 11 - Order by integer array index
You can use array_position()
:
ORDER BY array_position('{3,2,1}'::int[], id)
If you didn't want to repeat the array twice:
select p.id
from product p join
(values ('{3,2,1}'::int[])) v(ar)
on p.id = any(v.ar)
order by array_position(v.ar, p.id);
Sort array elements alphabetically PostgreSQL
To do this you will need to use the ORDER BY
clause in the ARRAY_AGG
function.
Example:
SELECT ARRAY_AGG(fullname ORDER BY lastname)
FROM ...
This will return an array with names sorted by last name.
Order by number of matches in array
Unnest tags, filter unnested elements and aggregate remaining ones:
select id, count(distinct u) as matches
from (
select id, u
from testing_items,
lateral unnest(tags) u
where u in ('222', '555', '666')
) s
group by 1
order by 2 desc
id | matches
----+---------
5 | 3
3 | 2
2 | 1
4 | 1
(4 rows)
Considering all the answers, it seems that this query combines good sides of each of them:
select id, count(*)
from testing_items,
unnest(array['11','5','8']) u
where tags @> array[u]
group by id
order by 2 desc, 1;
It has the best performance in Eduardo's test.
PostgreSQL - ORDER BY a date in nested array
You have all the necessary information in your images
table. (Since you did not show the definition of that table, what follows is assuming a couple of things.) A window function should do the trick:
WITH image_tags AS (
SELECT images."post id",
json_build_object (
'imageID', images."image id",
'uploaded', images.uploaded,
'tags', json_agg("tag map".tag) ) AS image,
first_value(images.uploaded) OVER (
PARTITION BY images."post id" ORDER BY images.uploaded DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS newest,
FROM images
JOIN "tag map" ON images."image id" = "tag map"."image id"
GROUP BY images."post id", images."image id"
ORDER BY images."pos" DESC
)
SELECT posts."post id" AS "postID",
json_agg(image_tags.image) AS images
FROM posts
JOIN image_tags USING ("post id")
GROUP BY posts."post id"
ORDER BY image_tags.newest -- ASC by default, use DESC for reverse order
The window function takes all the rows in the images
table that have the same "post id"
and makes them available for analysis. You can then find the first and the last row of the window frame and store the value in new columns in the image_tags
row source. After that the ordering in the main query is straightforward.
Related Topics
SQL Query Where Date = Today Minus 7 Days
Spark:Failure: ''Union'' Expected But '(' Found
Select Columns with Particular Column Names in Postgresql
Finding Continuous Ranges in a Set of Numbers
Date Calculation with Parameter in Ssis Is Not Giving the Correct Result
How to Sum Multiple Lines in SQL
Change the Size of Datatype in SQL
Setting Variables in SQL Functions/Probs
Creating Nondeterministic Functions in SQL Server Using Rand()
SQL Server 2008 - Conditional Query
Sql: Syntax Error with Intersect
Is Order by and Row_Number() Deterministic
How to Write Blob from Oracle Column to the File System