1

I have the following query that runs correctly on Postgres 9.3:

select distinct date_part('year', date_created) 
from "Topic";

The intention is to return only the distinct years on the column date_created which is created thus:

date_created  | timestamp with time zone | not null default now()

I need to turn it into a SQLAlchemy query but what I wrote does a select distinct on the date_created, not on the year, and returns the whole row, not just the distinct value:

topics = Topic.query.distinct(func.date_part('YEAR', Topic.date_created)).all()

How can I get only the distinct years from the table Topic?

Juan Carlos Oropeza
  • 47,252
  • 12
  • 78
  • 118
ruipacheco
  • 15,025
  • 19
  • 82
  • 138

3 Answers3

3

Here are two variants:

Using ORM:

from sqlalchemy import func, distinct

result = session.query(distinct(func.date_part('YEAR', Topic.date_created)))
for row in result:
    print(row[0])

SQL Expression:

from sqlalchemy import func, select, distinct

query = select([distinct(func.date_part('YEAR', Topic.date_created))])
for row in session.execute(query):
        print(row[0])
1

Use extract function:

session.query(func.extract(Topic.date_created, 'year'))

this is a concept code, not tested.

Tomasz Jakub Rup
  • 10,502
  • 7
  • 48
  • 49
  • You probably think about http://docs.sqlalchemy.org/en/latest/core/tutorial.html#functions. I think `extract` is better in this case. – Tomasz Jakub Rup Nov 26 '15 at 22:31
1

SQL Alchemy syntax aside, you have a potential problem in your query.

Your data type is timestamptz (timestamp with time zone), which is a good choice. However, you cannot tell the year reliably form a timestamptz alone, you need to specify the time zone additionally. If you don't, the current time zone setting of the session is applied silently, which may or may not work for you.

Think of New Year's Eve: timestamptz '2016-01-01 04:00:00+00' - what year is it?

It's 2016 in Europe, but still 2015 in the USA.
You should make that explicit with the AT TIME ZONE construct to avoid sneaky mistakes:

SELECT extract(year FROM timestamptz '2016-01-01 04:00:00+00'
                         AT TIME ZONE 'America/New_York') AS year;

Detailed explanation:

date_part() and extract() do the same in Postgres, extract() is the SQL standard, so rather use that.

SQL Fiddle.

BTW, you could also just:

SELECT extract(year FROM date_created) AS year
FROM   "Topic"
GROUP  BY 1;
Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228