使用最大列值过滤记录的Django查询

时间:2011-08-11 02:52:27

标签: python sql django postgresql django-orm

是否有一种简单的方法可以根据哪个记录在列中具有最大/最小值来过滤Django查询?我基本上是在问these questions,而是在Django的ORM的特定背景下。

e.g。

假设我有一个模型,用于存储每个人电话号码的历史值。

class Person(models.Model):
    name = models.CharField(max_length=100)
    phone = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)

记录:

Person(name='Jim',phone='123-456-9870', created=datetime(2005,1,2,4,2))
Person(name='Jim',phone='329-802-9870', created=datetime(2006,9,2,7,8))
Person(name='Sue',phone='324-345-3450', created=datetime(2008,7,4,6,1))

现在说我想找到每个人最近的电话号码。

在SQL中,我通常必须使用子查询来计算最大值:

SELECT p1.name, p1.phone, p1.created
FROM person_person p1, (
    SELECT name, MAX(created) AS max_created
    FROM person_person
    GROUP BY name
) AS p2
WHERE p1.name = p2.name AND p1.created = p2.max_created

Django中是否有可以简化此操作的机制?

我在我的后端使用PostgreSQL,所以任何依赖PostgreSQL特定功能的想法或解决方案都会有所帮助。

3 个答案:

答案 0 :(得分:4)

您可能只想在这里使用原始SQL,raw()管理器方法可以促进这一点,允许您从查询中返回模型实例。唯一的技巧是原始查询需要包含主键。这可能适合您(除非您将主键设置为id以外的其他内容):

latest_phone_numbers = Person.objects.raw('''
SELECT p1.id, p1.name, p1.phone, p1.created
FROM person_person p1, (
    SELECT name, MAX(created) AS max_created
    FROM person_person
    GROUP BY name
) AS p2
WHERE p1.name = p2.name AND p1.created = p2.max_created
''')

答案 1 :(得分:1)

如果你的后端是PostgreSQL,Roman Pekar给了this问题一个很好的答案。

答案 2 :(得分:1)

更新:如果您使用的是 PostgreSQL,则可以使用带有 .distinct() 的 ORM

来自PostgreSQL documentation

<块引用>

SELECT DISTINCT ON ( expression [, ...] ) 只保留第一行 给定表达式计算结果相等的每一组行。这 DISTINCT ON 表达式使用与 for 相同的规则进行解释 ORDER BY(见上文)。请注意,每组的“第一行”是 不可预测,除非使用 ORDER BY 来确保所需的行 最先出现。

使用 Django ORM:

setlocal enabledelayedexpansion
echo:
set "td=%~dp0"
set "pattern= id="
set "id=id"
call:getPatternLength "!id!" plen
set /a "slen=!plen!-1"
set "opt=tokens=1,2,* delims=<"
for /f "tokens=* delims=" %%z in (' type "!td!usedata\id.txt"^| findstr /c:"!pattern!" ') do (
   for /f "%opt%" %%b in ("%%z") do (
      for /f "tokens=* delims=" %%y in ('echo/%%b^|findstr /v [0-9][a-z]') do (
         set "bempty=true"
         call:findit "%%c" "!id!" strfound
      )
      if not defined bempty call:findit "%%b" "!id!" strfound
      set "strfound=!strfound:"=!"
      if defined strfound (
         call:handlefound "!strfound!" result
         if defined result (echo Finally: !result! & exit /b 0)
      )
   )
)

:end
endlocal
exit /b 0



:getPatternLength
set "str=%~1"
set /a "count=0"
:next
for /f "tokens=* delims=" %%z in ('echo/!str!') do (
   set /a "count+=1"
   set "str=!str:~1!"
   if not "!str!"=="" goto:next
)
set "%2=!count!"
if /i "%0"==":getPatternLength" (exit /b 0)


:handlefound
set "str=%~1"
for /f "tokens=* delims=" %%a in ('echo/!str!^| findstr [a-z]') do (
   :notyet
   for /f "tokens=1,2,* delims=-" %%d in ("!str!") do (
      for /f "tokens=* delims=" %%g in ('echo/%%e^|findstr "_"') do (
         set "res=!res!-%%d"
         set "%2=!res!"
         exit /b 0
      )
      if not defined res ( set "res=%%d" ) else ( set "res=!res!-%%d")
      set "str=%%e-%%f" & goto:notyet
   )
)
echo Not the right line.. next.
exit /b 1


:findit
set "str=%~1"
set "find=%~2"
set "opts=tokens=1,* delims= "
set "opts2=tokens=1,* delims=="

:nexttoken
for /f "%opts%" %%m in ("!str!") do (
   for /f "%opts2%" %%o in ("%%m") do (
      if "%%o"=="!find!" (
         set "%3=%%p"
         if "%0"==":findit" exit /b 0
      )
      set "str=%%n" & goto:nexttoken
   )
)
exit /b 1

生成的 SQL:


Person.objects.order_by('name', '-created').distinct('name')