使用Django查询降低在SQL数据库中搜索的成本

时间:2018-01-17 11:51:48

标签: python django django-models django-rest-framework django-queryset

我正在制作一个应用程序,将使用逻辑和模型搜索类似的药物,如果某种药物属于某一类特定类别,那么其他药物只有那些类别才会返回这些药物。 这是相关的代码和虚拟数据 -

views.py

class GetSimilarDrugs(APIView):

  def get(self, request, format=None):
    #import pdb
    #pdb.set_trace()
    get_req = request.GET.get('drugid', '')
    simi_list = []
    comp_class = DrugBankDrugEPClass.objects.filter(drug_bank_id = get_req).values_list('epc_id', flat=True).distinct()
    for drg_id in DrugBankDrugEPClass.objects.values_list('drug_bank_id', flat = True).distinct():
      classtocomp = DrugBankDrugEPClass.objects.filter(drug_bank_id = str(drg_id)).values_list('epc_id', flat=True).distinct()

      complist = list(comp_class)
      tolist = list(classtocomp)
      if complist == tolist:                
        simi_list.append(drg_id)
  return Response({'result':simi_list})

models.py

class DrugBankDrugEPClass(models.Model):
  drug_bank = models.ForeignKey(DrugBankDrugs, on_delete=models.CASCADE)
  epc = models.ForeignKey(DrugBankEPClass, on_delete=models.CASCADE)

虚拟SQL数据

 id   | drug_bank_id | epc_id |
+------+--------------+--------+
|    1 | DB12789      |      1 |
|    2 | DB12788      |      2 |
|    3 | DB00596      |      3 |
|    4 | DB09161      |      4 |
|    5 | DB01178      |      5 |
|    6 | DB01177      |      6 |
|    7 | DB01177      |      6 |
|    8 | DB01174      |      7 |
|    9 | DB01175      |      8 |
|   10 | DB01172      |      9 |
|   11 | DB01173      |     10 |
|   12 | DB12257      |     11 |
|   13 | DB08167      |     12 |
|   14 | DB01551      |     13 |
|   15 | DB01006      |     14 |
|   16 | DB01007      |     15 |
|   17 | DB01007      |     16 |
|   18 | DB01004      |     17 |
|   19 | DB01004      |     18 |
|   20 | DB01004      |     17 |
|   21 | DB01004      |     18 |
|   22 | DB01004      |     19 |
|   23 | DB00570      |     20 |
|   24 | DB01008      |     21 |
|   25 | DB00572      |     22 |
|   26 | DB00575      |      7 |
|   27 | DB00577      |     23 |
|   28 | DB00577      |     24 |
|   29 | DB00577      |     25 |
|   30 | DB00576      |     26 |
|   31 | DB00751      |     27 |
|   32 | DB00751      |     28 |
|   33 | DB00750      |     29 |
|   34 | DB00753      |     30 |
|   35 | DB00752      |     31 |
|   36 | DB00755      |     32 |
|   37 | DB00755      |     32 |
|   38 | DB00757      |     33 |
|   39 | DB00756      |     34 |
|   40 | DB00759      |     35 |
|   41 | DB00759      |     36 |
|   42 | DB00759      |     36 |

我得到了结果,但问题是它每次都在列表中进行迭代,因此花费了很多时间并且对于大量数据来说它确实很慢。还有其他方法可以更快地运作吗?

2 个答案:

答案 0 :(得分:1)

根据您的需要,我认为您可以这样做:

get_req = request.GET.get('drugid', '')
# Fetching all the epc_ids that belongs to requisted drug_bank_ids
comp_class = DrugBankDrugEPClass.objects.filter(drug_bank_id = get_req).values_list('epc_id', flat=True).distinct()
# filters all drug_bank_ids thats matcth with the epc_ids in requisted
classtocomp = DrugBankDrugEPClass.objects.filter(epc_id__in = comp_class).values_list('drug_bank_id', flat=True).distinct()

UPD:

get_req = request.GET.get('drugid', '')

comp_class = DrugBankDrugEPClass.objects.filter(
     drug_bank_id=get_req).values_list('epc_id', flat=True).distinct()

class_to_comp = DrugBankDrugEPClass.objects.filter(
     epc_id__in=comp_class).values_list('drug_bank_id', 'epc_id')

d = {}
for k, v in class_to_comp:
     d.setdefault(k, []).append(v)

simi_list = [k for k, v in d.items() if v == list(comp_class)]
print(simi_list)

我认为它会比你的代码快一点,因为如果我也在循环 就像你做的那样,它并没有在每个循环中击中数据库。它还可以循环过滤数据。

答案 1 :(得分:0)

您可以从

更改循环
for drg_id in DrugBankDrugEPClass.objects.values_list('drug_bank_id', flat = True).distinct():
  classtocomp = DrugBankDrugEPClass.objects.filter(drug_bank_id = str(drg_id)).values_list('epc_id', flat=True).distinct()

drug_ids = DrugBankDrugEPClass.objects.values_list('drug_bank_id', flat = True).distinct()
comps = DrugBankDrugEPClass.objects.filter(drug_bank_id__in = drug_ids).values_list('epc_id', flat=True).distinct()

然后遍历comps结果集。

您应该执行的其他优化是将db_index = True添加到您要查询的必要字段中。

如果您正在使用Postgres,则可以向distinct添加字段参数:

  

仅在PostgreSQL上,您可以传递位置参数(* fields)以指定DISTINCT应应用的字段的名称。这转换为SELECT DISTINCT ON SQL查询。这是区别。对于正常的distinct()调用,数据库在确定哪些行是不同的时比较每行中的每个字段。对于具有指定字段名称的distinct()调用,数据库将仅比较指定的字段名称。

您可以在其中执行以下操作:

comps = DrugBankDrugEPClass.objects.values_list('drug_bank_id', flat = True).distinct('drug_bank_id', 'epc_id')

编辑添加:

此外,您可以使用django-silkdjango-debug-toolbar等插件来分析您的查询和应用