How to Filter Window Function (Lag/Lead) for FloatField

207 views
Skip to first unread message

Gabriel Lima de Oliveira

unread,
Jul 12, 2018, 7:22:35 AM7/12/18
to Django users
Hi all. Carlton suggested I should ask this here, as stated at https://code.djangoproject.com/ticket/29561#ticket

Please, I'd like to know how to filter using new django 2.0 window functions. Something similar to this beautiful and simple example

Given a table

db=> SELECT * FROM weather ORDER BY day DESC;

daytemperaturerainy
2014-04-0820.0f
2014-04-0720.0f
2014-04-0616.0t
2014-04-0516.0t
2014-04-0416.0t
2014-04-0322.0f
2014-04-0222.0f
2014-04-0122.0t

I'd like to show dates only if the temperature changed:

daytemperature
2014-04-0820.0
2014-04-0616.0
2014-04-0322.0

On pure PostgreSQL, this translates to:

SELECT
    w1.day, w1.temperature
FROM
    (SELECT
        w2.day,
        w2.temperature,
        lead(w2.temperature) OVER (ORDER BY w2.day DESC) as prev_temp
     FROM
        weather w2
     ORDER BY
        w2.day DESC) as w1
WHERE
    w1.temp IS DISTINCT FROM w1.prev_temp
ORDER BY
    w1.day DESC;


I could accomplish the inner query by using the new Window Functions:

Weather.objects.annotate(prev_temp=Window(expression=Lead('temperature'), order_by=F('day').desc()))


Now my problem is use this annotation to filter only when temperature differs from prev_temp
(in order to accomplish something similar to the "temperature IS DISTINCT FROM prev_temp")

When I try to use the available filters, the following errors occurs:

Weather.objects.annotate(prev_temp=Window(expression=Lead('temperature'), order_by=F('day').desc())).order_by('-day').filter(temperature__ne=F('prev_temp'))


Gives the error: 

FieldError: Unsupported lookup 'ne' for FloatField or join on the field not permitted.


Another try:

Weather.objects.annotate(prev_temp=Window(expression=Lead('temperature'), order_by=F('day').desc())).order_by('-day').filter(~Q(temperature=F('prev_temp'))))


Gives the error: 

ProgrammingError: window functions are not allowed in WHERE


It's in fact a PostgreSQL error, because the generated SQL query tries to pass the LAG function inside the where clause.

How may I accomplish that, even if I have to use the extra fields, or even RawSQL class ?


Right now I'm doing:

with connection.cursor() as c:
    c.execute('SELECT q.day, q.temperature FROM (%s) AS q WHERE q.temperature IS DISTINCT FROM q.prev_temp' % str(Weather.objects.annotate(prev_temp=Window(expression=Lead('temperature'), order_by=F('day').desc())).order_by('-day').values('day','temperature', 'prev_temp').query))
    result = c.fetchall()


Important to note that I couldn't use the str(queryset.query) as parameter to the c.execute. I had to do a python replace.


I.e, had to use ('...%s...' % parameter) instead of c.execute ('...%s...', [parameter])


Thank you all in advance. 

Reply all
Reply to author
Forward
0 new messages