2012-03-23 112 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に対応するクエリーセットオブジェクトを作成する方法がわかりません。これを行うことができますdjangoまたは私は生のSQLを使用して頼りにする必要がありますか?

+0

医者のクエリーセットのコードを投稿できますか?とmy_new_queryset?そして最終的にモデル?ありがとう! – jpic

+0

医師は、私はまだそれをやっていないセットまたはコレクションです。私の新しいクエリーセットは、私が問題を抱えているものです。 –

答えて

2

、私はあなたがやりたいだろう、単一のクエリセットを書き込もうとしましたが、それはあなたのデータモデルに基づいて可能ていないようです。

あなたのデータモデルは、特定の患者が特定の患者を治療した日付範囲を実際には説明していません。あなたが書いているクエリは、あなたがどのようにクエリを書くかにかかわらず、間違った結果を返すでしょう。あなたが現在言っているのは、次のとおりです。

"患者が死亡した場合、この患者を治療した医師はis_main=Trueであり、責任を負うとマークされます。" (責任はここに正しい言葉ではないかもしれませんが、あなたはその考えを得るべきです)。

特定の患者に単一の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が患者の責任を担うようにすることもできます。

2番目の構造は同じ情報を収集できますが、別のテーブルを結合するdatetimeフィルタリングを必要とし、クエリをより遅く複雑にします。

これは、データが正しいという点で、その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 

まだ2つのクエリですが、これらのクエリはより大きい(複数のテーブルにまたがっています)。

+0

論理とプレゼンテーションの分離が大好きです(私の+1)。私は 'is_main'フィールド(これはモデル保存メソッドに強制されていますが、dbレベルではありません)にかなり悩まされています。死のモデル(または患者のモデル)にもcurrent_gpはありません。私は関数get_main_gp(death.patient_id)でdbルックアップを使ってこれを行います。だから私はまだ私のテーブルのすべての値のDBのルックアップを見ている。 –

+0

@AidanEwenあなたの現在のモデル構造を使用する編集 –

+0

すべての入力Joshに感謝します。私は最後の行がまだ失敗すると思います。死オブジェクトにはpatientgplink属性はありません(私はget_main_gp関数を使用していますが、データベースにヒットします) –

1

QuerySetは.select_related()で結合を実行できます。 lookups that span relationshipsについても読む必要があります。あなたはまた、医師が医師あたりのカウント数を.annotate()するために作ってみることができ

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

:my_new_querysetは次のようになります。

+0

関係をまたぐルックアップの例を追加 – jpic

+0

上記の4つのモデルのうち、他の3つのモデルとの関係(直接的または間接的)を含むモデルはありません。 select_related()とannotate()には事前定義された関係が必要ですか? SQLでは、クエリ中の関係を定義することができます( '内部結合' 'on')。 djangoはありますか? –

+0

はい、select_related()と関係をまたぐルックアップを読みましたか? – jpic

0

関係者間の関係(具体的には「後方」機能)+1のルックアップについては、jpicに感謝します。他の誰かが探しに来る場合に備えて、私は私の更新されたコードを含めました。あなたの答えに私のコメントに拡大

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クエリ)を実行しています。ビューで単一のクエリーセット参照を使用して必要なものを達成し、レンダリングのために結果セットをテンプレートに渡すことができるはずです。 –

+0

理想的ですね。どのように私はそれを行うすべての提案?私は確かにデータベースのヒットを減らしたい。 –

+0

私の答えを見てください。コメントは長すぎました。 –