简体   繁体   中英

django performance - is sql view good enought?

As part of my present project I revived task to deliver group of on-line reports.

As per reports complexity and best performance on my radar I decided to write sql view on Posgres and next attached them to read-only Django models.

I want to ask you what you think about this approach as some of my team members saying that I should use ORM only which I don't think is best in this case.

Let me know your thoughts b/c I really need arguments but not like: "This is wrong approach b/c I think it is what it is. " Thanks,

J

example sql view:

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;

My django model which is read-only here:

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)

If you don't want to get "This is wrong approach b/c I think it is what it is." , then there's only one way to get ojective impartial answer on what is best in terms of performance - measure.

When performance is critical, it's worth to spend some time on benchmarking different approches.

In general Django and it's ORM is widely used over many years and people behind it try to optimise it as much as possible, so using its parts shouldn't be that bad. Consider checking this part of the docs . Also, note that Django is modular, so if you don't like the Django ORM you can swap it for something else, ie SQLALchemy .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM