简体   繁体   中英

Priority select statement in Postgres

I have rails application. in this application I use module globalize3 for i18n. I have table news and news_translations with this rows locale , description and title . I using join statement get all news checking current locale. It's correct work for current locale. For example:

WHERE "news_tranlations"."locale" = 'it'

But I want make another logic. If user from 'de' for example, I show him all news with locale 'de' and all news which haven't 'de' translation but have 'en' . When I try do it with OR statement I get one news in two translation 'de' and 'en' . Have I can do correct using something like priorities.

It's an awkward query. In a word, you need to OR the potential rows, and then compute the top row of the sub-result sets ordered by case when locale = 'it' then 1 when ... end . There are many ways to end up with the correct result (window functions in particular) but, frankly, you really don't want to go there for performace reasons.

Methinks revisit your assumptions, or alter your schema.

One way to make it reasonably fast is to add an array column to news items, eg languages , that is maintained by a trigger. Filter the top news (which I assume is what you're fetching) using that instead of joins against translations. (Note: depending on your Postgres version, avoid adding a GIST index on that column, because the selectivity of the && operator was hard-coded and would beat that of an order by/limit/offset using a btree beyond a certain row.)

Another way to make it reasonably fast is to track a separate feed table per language -- basically a materialized view that yields the ids of the query you're trying to build.

Either option, as you see, amounts to partially or completely pre-computing the rows that will show for each language. The first approach with an array column works fine in my own experience -- I like to use it for tags, to speed up and/or filters.

Consider seriously Denis' comments about performance and altering your schema. However, the following does solve your problem and may be applicable if you don't let weighted_tables get too large. Note in particular that Common Table Expressions (the tables WITH generates,) have no indices on them.

SQL Fiddle

You can use DISTINCT and ORDER BY to trick it.

I do not know what are your primary and foreign keys for the news, but assuming it is 'news_id' you can do it that way:

SELECT
  DISTINCT ON (n.news_id),
  n.news_id,
  nt.title,
  nt.description
FROM
  news n INNER JOIN news_translation nt
ON
  n.news_id = nt.news_id
ORDER BY
  n.news_id,
  nt.locale != 'de',
  nt.locale != 'en',
  nt.locale;

That query will always return you the news articles once (no duplicates and multiple translations) and will preferably give you the 'de' translation (if it exists), if not it will fall into the 'en', and finally if both do not exist, it will give you the first translation found ordered by the 'locale'.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM