Выполнение необработанного SQL Django (с использованием синтаксиса WHERE col IN) или преобразование необработанного SQL в .raw () или .extra () - PullRequest
1 голос
/ 23 ноября 2010

Django 1.3-dev предоставляет несколько способов запроса базы данных с использованием необработанного SQL. Они покрыты здесь и здесь . Рекомендуемые способы - использовать методы .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)

Документация объясняет, как можно выполнить запрос, используя WHERE col = %s, но не синтаксис IN. Одна часть этого вопроса: Как мне выполнить необработанные SQL-запросы, используя Django и оператор IN?

Другая часть, учитывая следующие модели:

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?

Ответы [ 2 ]

1 голос
/ 23 ноября 2010

Для такой проблемы, как эта, я бы разбил запрос на небольшое количество более простых, я думаю, что это вполне возможно. Кроме того, я обнаружил, что MySQL на самом деле может возвращать результаты быстрее при таком подходе.

edit ... На самом деле, немного подумав, я вижу, что вам нужно «комментировать подзапросы», что невозможно в 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 нет способа сделать GROUP_CONCAT в обычном ORM.

(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
0 голосов
/ 24 ноября 2010

Не могли бы вы объяснить, что именно вы пытаетесь извлечь с помощью подзапроса snp? Я вижу, что вы объединяете две таблицы, но похоже, что вы действительно хотите - это Snp объекты, которые имеют связанный Clin с заданным идентификатором. Если это так, это становится почти таким же простым, как и отдельный запрос 2:

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()

если @ Евгений читает правильно (так я и читал).

Зачастую с ORM в Django я нахожу, что получаю лучшие результаты, если пытаюсь напрямую думать о том, что я хочу с точки зрения ORM, вместо того, чтобы пытаться переводить в или из SQL, который я мог бы использовать, если бы не использовал ОРМ.

...