2017-04-24 1 views
0

조인에있는 다른 관계 집계의 몇 가지 가능한 결과를 테스트하여 쿼리의 단일 값을 어떻게 나타낼 수 있습니까? 이것은 현재 장고 모델을 통해 액세스이 관계에 대한 CASE 절, 다른 관계에 대한 집계 함수 테스트

TABLE lorem (
    id INTEGER 
    name VARCHAR 
    created TIME STAMP 
    PRIMARY KEY (id) 
) 

TABLE ipsum (
    id INTEGER 
    lorem_id INTEGER 
     REFERENCES lorem(id) 
    state VARCHAR 
    PRIMARY KEY (id) 
) 

: 파이썬에서 제대로 작동

class Lorem(models.Model): 
    name = models.CharField() 
    created = models.DateTimeField() 

    def state(self): 
     ipsum_states = [ipsum.state for ipsum in self.ipsum_set] 

     if ipsum_states and all(
       ipsum_state == 'complete' for ipsum_state in ipsum_states): 
      state = 'done' 
     elif any(
       ipsum_state == 'error' for ipsum_state in ipsum_states): 
      state = 'failure' 
     elif any(
       ipsum_state == 'begin' for ipsum_state in ipsum_states): 
      state = 'sending' 
     else: 
      state = 'not started' 

     return state 

class Ipsum(models.Model): 
    lorem = models.ForeignKey(Lorem) 
    state = models.CharField() 

나는이 스키마와 데이터베이스를 가지고있다. 그러나 모든 인스턴스에 대해 계산하는 것은 너무 느립니다.

이제 위의 Lorem 인스턴스 조건과 집계를 SELECT 문의 전체 집합 집계로 변환해야합니다. , Lorem 인스턴스에서 집계 함수는 파이썬 코드에서 사용할 수 있도록 내가 그 CASE 절을 만들 수있는 방법

SELECT 
    lorem.name AS name, 
    CASE 
     WHEN [… all the ipsum.state values, 
      for the set of ipsum referring to this lorem, 
      are 'complete' …] THEN 'done' 
     WHEN [… any ipsum.state value, 
      for the set of ipsum referring to this lorem, 
      is 'error' …] THEN 'failure' 
     WHEN [… any ipsum.state value, 
      for the set of ipsum referring to this lorem, 
      is 'begin' …] THEN 'sending' 
     ELSE 'not started' 
    END AS state 
FROM 
    lorem 
    LEFT INNER JOIN ipsum 
     ON ipsum.lorem_id = lorem.id 
; 

:

그래서 나는 데이터베이스 뷰에서 결정된 state 값을 갖고 싶어 선택에 lorem 인스턴스 당 적절한 설정 작업 을 수행합니까?

표 lorem_name, lorem_id,

표 2 ipsum_state : 이

을 lorem_id 당신이 기본적으로 필요로하는 것은에서 작동하는 플래그입니다

답변

0
CASE ipsum.states 
    WHEN 'complete' THEN 'done' 
    WHEN 'error' THEN 'failure' 
    WHEN 'begin' THEN 'sending' 
    ELSE 'not started' 
END as state 
+1

은'ipsum' 관계는'states' 필드가없는 - 가 : 여기

내가이 개념에 대해 쓴 기사입니다. 요점은 관련된 모든'ipsum' 인스턴스에 대해'state' 필드의 여러 가지 * 집계를 수행하는 것입니다. – bignose

0

나는 다음과 같이 데이터베이스가 보이는 것을 가정하고 첫 번째 경우와 다른 경우가있는 외부 쿼리의 집계에서. SQL은 행 단위로 작동하므로 외부 쿼리가 필요합니다.

SELECT name, 
case 
when min(state)=1 then 'done' 
when min(state)=-1 then 'error' 
when min(state)=0 then 'sending' 
when min(state)=-2 then 'not started' 
from 
(
    SELECT 
    lorem.name AS name, 
    CASE 
    WHEN ipsum.state=='complete' then 1 
    WHEN ipsum.state=='error' then -1 
    WHEN ipsum.state='begin' then 0 
    ELSE '-2' 
    END AS state 
    FROM 
    lorem 
    LEFT INNER JOIN ipsum 
    ON ipsum.lorem_id = lorem.id 
)group by 1; 

이렇게하면 문제가 해결됩니다.

+1

중첩 할 필요가 없습니다. 단지 귀중한 사례를 사용할 수 있습니다 :'사례 분 (사례가있을 때 ipsum.state ...) 1 ... ' – dnoeth

+0

매우 좋습니다.계산적으로 느껴지지만 두 쿼리가 같은 속도로 실행됩니다. 코드 가독성을 높이기 때문에 중첩 쿼리에 대한 열렬한 팬이 아닙니다. – Satyadev

0
CASE 
    WHEN [… all the ipsum.states are 'complete' …] THEN 'done' 
    WHEN [… any of the ipsum.states are 'error' …] THEN 'failure' 
    WHEN [… any of the ipsum.states are 'begin' …] THEN 'sending' 
    ELSE 'not started' 
END AS state 

CASE 
    WHEN COUNT(CASE WHEN ipsum.states = 'complete' THEN 1 END) = COUNT(*) THEN 'done' 
    WHEN COUNT(CASE WHEN ipsum.states = 'error' THEN 1 END) > 1 THEN 'failure' 
    WHEN COUNT(CASE WHEN ipsum.states = 'begin' THEN 1 END) > 1 THEN 'sending' 
    ELSE 'not started' 
END AS state 

그것은 당신의 의사 꽤 글을 번역 한 것입니다 같이 쓸 수있다 (나쁜 들여 쓰기를 무시하십시오) : countnull 값을 계산하지 않습니다; case의 기본값 인 elseelse null입니다. 즉 count 내의 각 case은 기준과 일치하는 행만 계산합니다. 이 수는 총 수 ("모든"경우) 또는 "임의"의 경우에만 >1과 비교됩니다. http://modern-sql.com/feature/filter

+0

'ipsum.states' 필드는 없습니다. 그것은 쓰는 방법을 모르는 것에 대한 의사 자리 표시 자입니다. 필자는 필드라는 느낌을 피하기 위해 질문을 업데이트했습니다. – bignose