【问题标题】:django performance - is sql view good enought?django 性能 - sql 视图是否足够好?
【发布时间】:2015-12-20 09:40:14
【问题描述】:

作为我目前项目的一部分,我重新启动了交付一组在线报告的任务。

根据我雷达上报告的复杂性和最佳性能,我决定在 Posgres 上编写 sql 视图,然后将它们附加到只读 Django 模型。

我想问你对这种方法的看法,因为我的一些团队成员说我应该只使用我认为在这种情况下不是最好的 ORM。

让我知道你的想法 b/c 我真的需要争论,但不喜欢:“这是错误的方法 b/c 我认为它就是这样。 " 谢谢,

J

示例 sql 视图:

DROP VIEW IF EXISTS riss_all_alerts;
DROP VIEW IF EXISTS riss_state_alerts;
DROP VIEW IF EXISTS riss_zone_alerts;
DROP VIEW IF EXISTS riss_lga_alerts;

-- ZONE PART
CREATE VIEW riss_zone_alerts AS
  WITH rissAlertPart AS (
      SELECT
        zone_name      AS alert_zone,
        min(rissAlert) AS alert,
        month
      FROM (
             SELECT
               date_part('month', odk_submission_date) AS month,
               zone_name,
               CASE
               WHEN (reason_no = 'N/A' AND reason_yes = 'N/A' AND ss_to_del = 1)
                 THEN '1'
               WHEN (reason_yes <> 'N/A' OR ss_to_del = 0)
                 THEN '3'
               WHEN (reason_no <> 'N/A' OR ss_to_del = 2)
                 THEN '2'
               ELSE 'ghj'
               END                                     AS rissAlert
             FROM riss_rissdetail
             WHERE alert_id = 'zn-ssn'
             GROUP BY zone_name, reason_no, reason_yes, ss_to_del, date_part('month', odk_submission_date)
           ) AS a
      GROUP BY a.zone_name, month
      ORDER BY 1, 2
  )

  SELECT
    'zn-ssn'::TEXT                                                                AS alert_id,
    zone_name                                                               AS level_name,
    CASE WHEN greenCount :: FLOAT > 0
      THEN greenCount :: FLOAT
    ELSE 0 END / count(*)                                                   AS value,
    to_char(CASE WHEN greenCount :: FLOAT > 0
      THEN greenCount :: FLOAT
            ELSE 0 END, 'FM999MI') || ' / ' || to_char(count(*), 'FM999MI') AS extra_desc,
    alert                                                                   AS alert_level,
    date_part('month', odk_submission_date) :: INT                          AS alert_date
  FROM riss_rissdetail
    LEFT JOIN (
                SELECT
                  date_part('month', odk_submission_date) AS green_month,
                  zone_name                               AS green_zone,
                  count(*)                                AS greenCount
                FROM riss_rissdetail
                WHERE alert_id = 'zn-ssn' AND ss_to_del = 0
                GROUP BY zone_name, date_part('month', odk_submission_date)
              ) AS g ON g.green_zone = zone_name AND g.green_month = date_part('month', odk_submission_date)
    LEFT JOIN rissAlertPart AS r
      ON r.alert_zone = zone_name AND r.month = date_part('month', odk_submission_date)
  WHERE alert_id = 'zn-ssn'
  GROUP BY zone_name, g.green_zone, greenCount, alert, date_part('month', odk_submission_date);


--LGA PART
CREATE VIEW riss_lga_alerts AS
  WITH rissAlertPart AS (
      SELECT
        lga_name       AS alert_lga,
        min(rissAlert) AS alert,
        month
      FROM (
             SELECT
               date_part('month', odk_submission_date) AS month,
               lga_name,
               CASE
               WHEN (reason_no = 'N/A' AND reason_yes = 'N/A' AND ss_to_del = 1)
                 THEN '1'
               WHEN (reason_yes <> 'N/A' OR ss_to_del = 0)
                 THEN '3'
               WHEN (reason_no <> 'N/A' OR ss_to_del = 2)
                 THEN '2'
               ELSE 'ghj'
               END                                     AS rissAlert
             FROM riss_rissdetail
             WHERE alert_id = 'lg-ssn'
             GROUP BY lga_name, reason_no, reason_yes, ss_to_del, date_part('month', odk_submission_date)
           ) AS a
      GROUP BY a.lga_name, month
      ORDER BY 1, 2
  )

  SELECT
    'lg-ssn'::TEXT                                                                AS alert_id,
    lga_name                                                                AS level_name,
    CASE WHEN greenCount :: FLOAT > 0
      THEN greenCount :: FLOAT
    ELSE 0 END / count(*)                                                   AS value,
    to_char(CASE WHEN greenCount :: FLOAT > 0
      THEN greenCount :: FLOAT
            ELSE 0 END, 'FM999MI') || ' / ' || to_char(count(*), 'FM999MI') AS extra_desc,
    alert                                                                   AS alert_level,
    date_part('month', odk_submission_date) :: INT                          AS alert_date
  FROM riss_rissdetail
    LEFT JOIN (
                SELECT
                  date_part('month', odk_submission_date) AS green_month,
                  lga_name                                AS green_lga,
                  count(*)                                AS greenCount
                FROM riss_rissdetail
                WHERE alert_id = 'lg-ssn' AND ss_to_del = 0
                GROUP BY lga_name, date_part('month', odk_submission_date)
              ) AS g ON g.green_lga = lga_name AND g.green_month = date_part('month', odk_submission_date)
    LEFT JOIN rissAlertPart AS r
      ON r.alert_lga = lga_name AND r.month = date_part('month', odk_submission_date)
  WHERE alert_id = 'lg-ssn'
  GROUP BY lga_name, g.green_lga, greenCount, alert, date_part('month', odk_submission_date);


--STATE PART
CREATE VIEW riss_state_alerts AS
  WITH rissAlertPart AS (
      SELECT
        state_name     AS alert_state,
        min(rissAlert) AS alert,
        month
      FROM (
             SELECT
               date_part('month', odk_submission_date) AS month,
               state_name,
               CASE
               WHEN (reason_no = 'N/A' AND reason_yes = 'N/A' AND ss_to_del = 1)
                 THEN '1'
               WHEN (reason_yes <> 'N/A' OR ss_to_del = 0)
                 THEN '3'
               WHEN (reason_no <> 'N/A' OR ss_to_del = 2)
                 THEN '2'
               ELSE 'ghj'
               END                                     AS rissAlert
             FROM riss_rissdetail
             WHERE alert_id = 'st-ssn'
             GROUP BY state_name, reason_no, reason_yes, ss_to_del, date_part('month', odk_submission_date)
           ) AS a
      GROUP BY a.state_name, month
      ORDER BY 1, 2
  )

  SELECT
    'st-ssn'::TEXT                                                                AS alert_id,
    state_name                                                              AS level_name,
    CASE WHEN greenCount :: FLOAT > 0
      THEN greenCount :: FLOAT
    ELSE 0 END / count(*)                                                   AS value,
    to_char(CASE WHEN greenCount :: FLOAT > 0
      THEN greenCount :: FLOAT
            ELSE 0 END, 'FM999MI') || ' / ' || to_char(count(*), 'FM999MI') AS extra_desc,
    alert                                                                   AS alert_level,
    date_part('month', odk_submission_date) :: INT                          AS alert_date
  FROM riss_rissdetail
    LEFT JOIN (
                SELECT
                  date_part('month', odk_submission_date) AS green_month,
                  state_name                              AS green_state,
                  count(*)                                AS greenCount
                FROM riss_rissdetail
                WHERE alert_id = 'st-ssn' AND ss_to_del = 0
                GROUP BY state_name, date_part('month', odk_submission_date)
              ) AS g ON g.green_state = state_name AND g.green_month = date_part('month', odk_submission_date)
    LEFT JOIN rissAlertPart AS r
      ON r.alert_state = state_name AND r.month = date_part('month', odk_submission_date)
  WHERE alert_id = 'st-ssn'
  GROUP BY state_name, g.green_state, greenCount, alert, date_part('month', odk_submission_date);

CREATE VIEW riss_all_alerts AS
  SELECT *
  FROM riss_zone_alerts
  UNION ALL
  SELECT *
  FROM riss_lga_alerts
  UNION ALL
  SELECT *
  FROM riss_state_alerts;

我的 django 模型在这里是只读的:

class RissAlertView(models.Model):
    alert_id = models.CharField(max_length=255, primary_key=True)
    value = models.FloatField()
    alert_date = models.PositiveSmallIntegerField(null=True)
    alert_level = models.PositiveSmallIntegerField(null=True)
    level_name = models.CharField(max_length=255, null=True)
    extra_desc = models.CharField(max_length=255, null=True)

    class Meta:
        db_table = u'riss_all_alerts'
        managed = False
        verbose_name = "Riss Alerts View"
        verbose_name_plural = "Riss Alerts View"
        permissions = (("can_access_riss_alerts", "Can access RISS alerts"),)

    def __unicode__(self):
        return u'%s %s %s' % (self.alert_id, self.levelname, self.alert_date)

    def _percenatge_value(self):
        """Returns the percenateg value."""
        return self.value * 100

    percent = property(_percenatge_value)

【问题讨论】:

    标签: django postgresql django-models database-performance query-performance


    【解决方案1】:

    如果你不想得到 “这是错误的方法 b/c,我认为它就是这样。”,那么只有一种方法可以客观公正地回答什么是最好的在性能方面 - 衡量标准。

    当性能至关重要时,值得花一些时间对不同的方法进行基准测试。

    一般来说,Django 和它的 ORM 已经被广泛使用了很多年,它背后的人试图尽可能地优化它,所以使用它的部分应该不会那么糟糕。考虑检查docs 的这一部分。另外,请注意 Django 是模块化的,因此如果您不喜欢 Django ORM,可以将其换成其他东西,即SQLALchemy

    【讨论】:

      猜你喜欢
      • 2019-04-19
      • 2011-04-14
      • 2011-08-11
      • 1970-01-01
      • 2011-05-25
      • 2013-11-11
      • 2013-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多