Django 1.3-dev提供了几种使用原始SQL查询数据库的方法。它们涵盖here和here。建议的方法是使用.raw()
或.extra()
方法。优点是,如果检索到的数据符合 Model ,您仍然可以直接使用它的一些功能。
我试图显示的页面有点复杂,因为它使用了大量信息,这些信息分布在具有不同关系的多个表中(one2one,one2many)。使用当前方法,服务器必须执行每页4K查询。由于数据库与网络服务器的通信,这显然很慢。
一种可能的解决方案是使用原始SQL来检索相关数据,但由于查询的复杂性,我无法将其转换为Django中的等效数据。
查询是:
SELECT clin.iso as iso,
(SELECT COUNT(*)
FROM clin AS a
LEFT JOIN clin AS b
ON a.pat_id = b.pat_id
WHERE a.iso = clin.iso
) AS multiple_iso,
(SELECT COUNT(*)
FROM samptopat
WHERE samptopat.iso_id = clin.iso
) AS multiple_samp,
(SELECT GROUP_CONCAT(value ORDER BY snp_id ASC)
FROM samptopat
RIGHT JOIN samptosnp
USING(samp_id)
WHERE iso_id = clin.iso
GROUP BY samp_id
LIMIT 1 -- Return 1st samp only
) AS snp
FROM clin
WHERE iso IN (...)
或WHERE iso = ...
。
示例输出如下:
+-------+--------------+---------------+-------------+
| iso | multiple_iso | multiple_samp | snp |
+-------+--------------+---------------+-------------+
| 7 | 19883 | 0 | NULL |
| 8 | 19883 | 0 | NULL |
| 21092 | 1 | 2 | G,T,C,G,T,G |
| 31548 | 1 | 0 | NULL |
+-------+--------------+---------------+-------------+
4 rows in set (0.00 sec)
documentation解释了如何使用WHERE col = %s
而不是IN
语法进行查询。
这个问题的一部分是如何使用Django和IN
语句执行原始SQL查询?
另一部分是,考虑以下模型:
class Clin(models.Model):
iso = models.IntegerField(primary_key=True)
pat = models.IntegerField(db_column='pat_id')
class Meta:
db_table = u'clin'
class SampToPat(models.Model):
samptopat_id = models.IntegerField(primary_key=True)
samp = models.OneToOneField(Samp, db_column='samp_id')
pat = models.IntegerField(db_column='pat_id')
iso = models.ForeignKey(Clin, db_column='iso_id')
class Meta:
db_table = u'samptopat'
class Samp(models.Model):
samp_id = models.IntegerField(primary_key=True)
samp = models.CharField(max_length=8)
class Meta:
db_table = u'samp'
class SampToSnp(models.Model):
samptosnp_id = models.IntegerField(primary_key=True)
samp = models.ForeignKey(Samp, db_column='samp_id')
snp = models.IntegerField(db_column='snp_id')
value = models.CharField(max_length=2)
class Meta:
db_table = u'samptosnp'
是否可以将上述查询重写为更面向ORM的内容?
答案 0 :(得分:1)
对于像这样的问题,我将查询分成少数更简单的问题,我认为这很有可能。此外,我发现MySQL实际上可以通过这种方法更快地返回结果。
编辑 ...实际上在稍微思考后我发现你需要“在子查询上注释”,这在Django ORM中是不可能的(至少在1.2中不是这样)。也许你必须在这里做简单的SQL或使用其他工具来构建查询。
尝试用更默认的django模式重写模型,也许有助于更好地理解问题。虽然模型Pat和Snp缺失......
class Clin(models.Model):
pat = models.ForeignKey(Pat)
class Meta:
db_table = u'clin'
class SampToPat(models.Model):
samp = models.ForeignKey(Samp)
pat = models.ForeignKey(Pat)
iso = models.ForeignKey(Clin)
class Meta:
db_table = u'samptopat'
unique_together = ['samp', 'pat']
class Samp(models.Model):
samp = models.CharField(max_length=8)
snp_set = models.ManyToManyField(Snp, through='SampToSnp')
pat_set = models.ManyToManyField(Pat, through='SaptToPat')
class Meta:
db_table = u'samp'
class SampToSnp(models.Model):
samp = models.ForeignKey(Samp)
snp = models.ForeignKey(Snp)
value = models.CharField(max_length=2)
class Meta:
db_table = u'samptosnp'
以下似乎意味着 - 每个诊所的独特患者数量......
(SELECT COUNT(*)
FROM clin AS a
LEFT JOIN clin AS b
ON a.pat_id = b.pat_id
WHERE a.iso = clin.iso
) AS multiple_iso,
每个诊所的样本数量:
(SELECT COUNT(*)
FROM samptopat
WHERE samptopat.iso_id = clin.iso
) AS multiple_samp,
这一部分难以理解,但在Django中,没有办法在普通的ORM中进行GROUP_CONCAT。
(SELECT GROUP_CONCAT(value ORDER BY snp_id ASC)
FROM samptopat
RIGHT JOIN samptosnp
USING(samp_id)
WHERE iso_id = clin.iso
GROUP BY samp_id
LIMIT 1 -- Return 1st samp only
) AS snp
答案 1 :(得分:0)
你能解释一下你正在尝试用snp子查询提取的内容吗?我看到你正在加入这两个表,但看起来你真正想要的是Snp
对象,它们具有一个具有给定id的关联Clin
。如果是这样,那么几乎就像另一个单独的查询一样简单:
Snp.objects.filter(samp__pat__clin__pk=given_clin)
或某些此类事情应该做的伎俩。不幸的是,由于你违反惯例的所有方式,你可能不得不重写一点。
其他类似的东西:
Pat.objects.filter(clin__pk=given_clin).count()
和
Samp.objects.filter(clin__pk=given_clin).count()
如果@Evgeny的阅读是正确的(这也是我读它的方式)。
通常,使用Django的ORM,如果我尝试直接考虑ORM方面的内容,而不是尝试转换为我可能使用的SQL(如果我不使用),我会发现效果更好ORM。