3

Django 1.8 アプリケーションがあり、db バックエンドとして pyodbc を使用して MsSQL データベースを使用しています (「django-pyodbc-azure」モジュールを使用)。

私は次のモデルを持っています:

class Branch(models.Model):
    name = models.CharField(max_length=30)
    startTime = models.DateTimeField()

class Device(models.Model):
    uid = models.CharField(max_length=100, primary_key=True)
    type = models.CharField(max_length=20)
    firstSeen = models.DateTimeField()
    lastSeen = models.DateTimeField()

class Session(models.Model):
    device = models.ForeignKey(Device)
    branch = models.ForeignKey(Branch)
    start = models.DateTimeField()
    end = models.DateTimeField(null=True, blank=True)

セッション モデルを照会する必要があり、特定のデバイス値を持つレコードを除外したいと考えています。そこで、次のクエリを発行します。

sessionCount = Session.objects.filter(branch=branch)
                          .exclude(device__in=badDevices)                                             
                          .filter(end__gte=F('start')+timedelta(minutes=30)).count()

badDevices は、約 60 項目のデバイス ID が事前に入力されたリストです。

badDevices = ['id-1', 'id-2', ...]

このクエリは、完了するまでに約 1.5 秒かかります。クエリから除外を削除すると、約 250 ミリ秒かかります。

このクエリセットに対して生成された sql を出力し、データベース クライアントで試してみました。そこでは、両方のバージョンが約 250 ミリ秒で実行されました。

生成された SQL は次のとおりです。

SELECT [session].[id], [session].[device_id], [session].[branch_id], [session].[start], [session].[end] 
FROM [session] 
WHERE ([session].[branch_id] = my-branch-id AND 
NOT ([session].[device_id] IN ('id-1', 'id-2', 'id-3',...)) AND 
DATEPART(dw, [session].[start]) = 1 
AND [session].[end] IS NOT NULL AND 
[session].[end] >= ((DATEADD(second, 600, CAST([session].[start] AS datetime)))))

したがって、データベースレベルで除外を使用してもクエリのパフォーマンスに影響はないようですが、django では、除外部分を追加するとクエリの実行が 6 倍遅くなります。何が原因でしょうか?

4

2 に答える 2

6

exclude一般的な問題は、ジャンゴが句を準備するために余分な作業を行っていることです。そのステップの後、SQL が生成されてデータベースに送信されるまでには、django 側でこのような重大な遅延を引き起こす可能性のある興味深いことは何も起きていません。

あなたの場合、これを引き起こしている可能性のあるものの1つは、ある種の前処理ですbadDevices。たとえば、badDevicesが の場合、実際のクエリの SQL を準備するためだけにQuerySetdjango がクエリを実行している可能性があります。デフォルト以外の主キーがbadDevicesある場合、おそらく同様のことが起こっている可能性があります。device

もう 1 つのことは、もちろん SQL の準備を遅らせる可能性がありますdjango-pyodbc-azure。クエリのコンパイル中に何かおかしなことをしている可能性があり、それがボトルネックになっています。

ただし、これはすべて突飛な憶測であるため、この問題がまだ解決しない場合は、モデルDeviceとモデル、およびクエリから生成された SQLBranchの正確な内容も投稿してください。badDevicesそうすれば、いくつかのシナリオを少なくとも排除できるかもしれません。

編集:それはフィールドでなければならないと思いDevice.uidます。おそらく、django または pyodbc がデフォルト以外の主キーによって混乱し、クエリの生成中にすべてのデバイスを取得しています。次の 2 つのことを試してください。

  • 、 、に置き換えdevice__indevice_id__in、それぞれをもう一度確認します。おそらく、より明示的なクエリの方が、django が SQL に変換しやすいでしょう。念のため、に置き換えてみることもできます。device__pk__indevice__uid__inbranchbranch_id

  • 上記が機能しない場合は、exclude 式を生の SQL where 句に置き換えてみてください。

    # add quotes (because of the hyphens) & join
    badDevicesIdString = ", ".join(["'%s'" % id for id in badDevices])
    
    # Replaces .exclude()
    ... .extra(where=['device_id NOT IN (%s)' % badDevicesIdString])
    

どちらも機能しない場合は、クエリだけでなく、クエリ全体に問題がある可能性がありますexclude。その場合、さらにいくつかのオプションがありますが、最初に上記を試してください。必要に応じて後で回答を更新します。

于 2015-10-21T18:52:24.547 に答える