1

作爲我現在項目的一部分,我恢復了提供一組在線報告的任務。Django的性能 - 是SQL視圖不夠好?

根據報告的複雜性和最佳性能,我決定在Posgres上編寫sql視圖,然後將它們附加到只讀的Django模型。

我想問你你對這種方法的看法,因爲我的一些團隊成員說我應該只使用ORM,在這種情況下我認爲這不是最好的。

讓我知道你的想法B/C我真正需要的論點,但不喜歡:「這是錯誤的做法B/C我認爲這是它是什麼 」 謝謝,

Ĵ

例如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) 

回答

1

如果你不想得到「這是錯誤的方法b/c我認爲這是它是什麼。」,那麼只有一種方法可以獲得關於績效最好的衡量標準的公正答案。

當表現很關鍵時,花點時間對不同的表示進行基準測試是值得的。

一般來說,Django和它的ORM被廣泛使用了很多年,它後面的人試圖儘可能地優化它,所以使用它的部分不應該那麼糟糕。考慮檢查這部分docs。另外,請注意Django是模塊化的,所以如果你不喜歡Django ORM,你可以將它換成別的東西,例如SQLALchemy