2012-03-23 96 views
0

我正在創建一份報告,列出病人死亡日期和醫生。在整個研究過程中會有成千上萬的人死亡,並有數百名醫生,我希望我的報告能夠快速運行(頁面負載在1秒以內)。3表內部加入django

我想創建一個可用於創建表的查詢集對象。我會爲表做這樣的事 -

for doc in doctors: 
    html += "<tr><td>" + str(doc) + "<td>" 
    for period in time_periods: 
     count = my_new_queryset.filter(gp = doc) 
        .filter(date__gte=period['start_date']) 
        .filter(date__lte=period['end_date']) 
     html += "<td>" + str(count) + "</td>" 
    html += "</tr>" 

在SQL查詢看起來是這樣的 -

SELECT patient.name, 
     death.date, 
     patient_gp_link.gp 
FROM patient 
INNER JOIN death 
ON  patient.id = death.patient 
INNER JOIN patient_gp_link 
ON  patient.id = patient_gp_link.patient 
WHERE patient_gp_link.is_main = true; 

(簡化)模型的樣子 -

class GP(models.Model): 
    #.... 

class Patient(models.Model): 
    #.... 

class Death(models.Model): 
    patient = models.ForeignKey(Patient) 
    date = models.DateField() 

class PatientGPLink(models.Model): 
    gp = models.ForeignKey(GP) 
    patient = models.ForeignKey(Patient) 
    is_main = models.BooleanField(default = False) 

我只是看不到如何創建與此sql對應的queryset對象。可以Django做到這一點,或者我應該訴諸使用原始的SQL?

+0

你可以發佈醫生queryset的代碼嗎?和my_new_queryset?最終模型?謝謝 ! – jpic 2012-03-23 11:53:45

+0

醫生是一個集合或一個集合(我還沒有做過)。我的新查詢集是我遇到的問題之一。 – 2012-03-23 11:59:34

回答

2

擴大我給你的答案評論,我試着寫一個查詢集你想要什麼,會做,但它似乎可以根據你的數據模型不。

您的數據模型實際上並未描述特定醫生治療特定患者的日期範圍。無論您如何編寫查詢,您正在編寫的查詢都會返回錯誤的結果。您目前所說的內容如下:

「如果患者死亡,那麼任何對該患者進行治療的醫生將被標記爲」負責「。 (責任可能不是這裏正確的詞,但你應該明白)。

現在,如果您只是將一個GP分配給is_main給特定的患者,那麼以上方法都是正確的..但是您的數據模型並未強制執行此操作,並且可能容易出錯。特別是如果is_main在患者死亡後改變。我將構建數據模型爲以下之一:

class GP(models.Model): 
    name = models.CharField(max_length=64) 

class Patient(models.Model): 
    name = models.CharField(max_length=64) 

class Death(models.Model): 
    current_gp = models.ForeignKey(GP) 
    patient = models.ForeignKey(Patient) 
    date = models.DateField() 

class Consultation(models.Model): 
    gp = models.ForeignKey(GP) 
    patient = models.ForeignKey(Patient) 
    start_date = models.DateField() 
    end_date = models.DateField(blank=True, null=True) 

或..

class GP(models.Model): 
    name = models.CharField(max_length=64) 

class Patient(models.Model): 
    name = models.CharField(max_length=64) 

class Death(models.Model): 
    patient = models.ForeignKey(Patient) 
    date = models.DateField() 

class Consultation(models.Model): 
    gp = models.ForeignKey(GP) 
    patient = models.ForeignKey(Patient) 
    start_date = models.DateField() 
    end_date = models.DateField(blank=True, null=True) 

第一結構具有允許非常好的疑問,這將是非常高性能的利益,在成本要求在患者死亡時輸入額外的信息。但是,Consultation(以前的PatientGPLink)模型將具有推導此信息所需的所有信息。你也可以使Death.current_gp成爲一個ManyToManyField來支持多個GP負責病人。

第二個結構可以收集相同的信息,但需要日期時間過濾,這將加入另一個表,並使查詢更慢,更復雜。

如果您非常清楚維護is_main字段,所有這些都是無關緊要的,因爲數據是正確的。但讓我告訴你如何查詢您的(可能)要更有效的方式從您的視圖的信息:

def my_view(request): 
    doctors = GP.objects.all() 
    periods = get_time_periods() # however it is you do this... 
    smallest_date = get_smallest_date(time_periods) 
    largest_date = get_largest_date(time_periods) 
    deaths = Death.objects.select_related(depth=1).filter(date__range=(smallest_date, largest_date)) 
    # build the results table with initial count of 0 to account for all doctors 
    # {period: {doctor: count}} 
    results = dict((period,{doctor: 0}) for doctor in doctors for period in periods) 
    for death in deaths: 
     for period in time_periods: # I'm assuming this is a small range of values 
      if death.date > period['start_date'] and death.date < period['end_date']: 
       results[period][death.current_gp] += 1 # add a death to the count 

然後,在你的模板,你有results表的所有預先計算的信息:

<table>  
{% for period, lookup in results.items %} 
    {% for doctor, deaths in lookup.items %} 
     <tr> 
      <td>{{ period }}</td> 
      <td>{{ doctor }}</td> 
      <td>{{ deaths }}</td> 
     </tr> 
    {% endfor %} 
{% endfor %} 
</table> 

對於總共2個SQL查詢。這種方式有更多的手動處理,但計算結果應該比查詢數據庫num_doctors * num_timeperiods + 1倍更快,這是您當前正在執行的操作。

編輯:

,使其與當前的模型結構工作(如果你真的不能更改機型...),您合併與我你的答案,並與這是非常相似的視圖,結束了到我寫的原文。我將所有評論都刪掉,因爲他們會和上面一樣。我已將評論放在我改變原始視圖的地方。

    def my_view(request): 
        doctors = GP.objects.all() 
        periods = get_time_periods() 
        smallest_date = get_smallest_date(time_periods) 
        largest_date = get_largest_date(time_periods) 
     # we make depth=3 so it spans via the PatientGPLink over to GP 
        deaths = Death.objects.select_related(depth=3).filter(date__range=(smallest_date, largest_date)).filter(patient__patientgplink__ismain=True) 
        results = dict((period,{doctor: 0}) for doctor in doctors for period in periods)  
        for death in deaths: 
            for period in time_periods: 
                if death.date > period['start_date'] and death.date < period['end_date']: 
        # and we change how we access the GP 
                    results[period][death.patient.patientgplink.gp] += 1 

仍然只有兩個查詢,儘管這些查詢更大(跨越多個表)。

+0

愛分離邏輯和演示(從我+1)。我很堅持'is_main'字段(這是強制模型保存方法 - 但不是在數據庫級)。死亡模型(甚至病人模型)中也沒有current_gp。我使用函數get_main_gp(death.patient_id)進行數據庫查找。所以我仍然在查看錶格中每個值的db查找。 – 2012-03-27 10:55:42

+0

@AidanEwen編輯使用您當前的模型結構 – 2012-03-27 11:57:15

+0

感謝所有輸入Josh。我認爲最後一行仍然失敗。死亡對象上沒有patientgplink屬性(我用我的get_main_gp函數卡住了,並且碰到了數據庫) – 2012-03-27 13:44:48

1

QuerySet可以與.select_related()進行連接。您還需要閱讀有關lookups that span relationships。 my_new_queryset會是什麼樣子:

Patient.objects.filter(patientgplink__is_main=True).select_related('death') 

你也可以嘗試讓醫生.annotate()每位醫生計數。

+0

添加了一個跨越關係的查找示例 – jpic 2012-03-23 12:59:27

+0

在上述4個模型中,沒有一個模型包含與所有其他三個關係(直接或間接)的關係。不要select_related()和annotate()需要預定義的關係? SQL允許我在查詢過程中定義關係(''上的'內部連接')。 django嗎? – 2012-03-23 13:03:56

+0

是的,你讀過select_related()和跨越關係的查找嗎? – jpic 2012-03-23 13:07:42

0

感謝jpic獲取跨越關係(特別是「向後」功能)+1的查找信息。我已經包含了我的更新代碼,以防其他人查找。

for doc in GP.objects.all(): 
    html += "<tr><td>" + str(doc) + "<td>" 
    patients = (Patient.objects.filter(patientgplink__gp=doc) 
          .filter(patientgplink__is_main=True) 
          .select_related(depth=1)) 
    for period in time_periods: 
     count = (patients.filter(death__date__gte=period['start_date']) 
          .filter(death__date__lte=period['end_date']) 
          .count()) 
     html += "<td>" + str(count) + "</td>" 
    html += "</tr>" 
+0

這是錯誤的。你正在爲每一個日期做一個計數(一個SQL查詢)。您應該能夠通過視圖中的單個查詢集查找來實現您想要的結果,並將結果集傳遞給模板進行呈現。 – 2012-03-24 09:32:39

+0

這聽起來很理想。任何建議我如何做到這一點?我當然想要減少數據庫的命中率。 – 2012-03-26 11:57:48

+0

請看我的答案。評論太長了。 – 2012-03-26 23:11:09