Пользовательская переменная MySQL, основанная на вычислении, остается нулевой, пока столбец имеет результаты

#mysql #sql

#mysql #sql

Вопрос:

У меня есть таблица с показаниями расхода газа и всеми видами данных, необходимых для анализа потребления газа. Я пытаюсь использовать SQL для выполнения вычислений и предоставления ответа, который я могу отправить генератору графиков.

Прямо сейчас я пытаюсь сохранить результат вычисления в виде переменной для дальнейшего использования в запросе: @corrConsGas . Само вычисление работает нормально и возвращает столбец с правильными данными, но переменная остается нулевой.

corrConsGas также, похоже, пропускает первый день диапазона. Я подтвердил это, сгруппировав и упорядочив результаты по годам, месяцам и дням. corrConsGas было 0.000 для первой даты. Я понятия не имею, почему.

 SELECT
   DATE_FORMAT(date, "%Y-%m") as "Month",

   -- Calculate avg gas consumption on non-heating days (when degrDays = 0), i.e. avg gas used for showering etc
   -- Uses data from >= 2018-06-11, as no decimals were reported before that date...
   @avgNHConsGas := (SELECT AVG(`consGas`) FROM `energyTST` WHERE degrDays = 0 AND consGas > 0 AND DATE >= '2018-06-11') AS "avgNHConsGas",

   -- Uncorrected values
   sum(`degrDays`) as "degrDays",
   sum(`consGas`) as "consGas",
   IF( SUM(`consGas`) > 0, ROUND( SUM(`consGas`) / SUM(`degrDays`), 3), 0 ) AS "consDD",


   -- Corrected gas consumption and consumption per degree day. Produces 0 instead of negative consumption.
   -- Skips first day in range...why???
   @corrConsGas := CASE
       WHEN SUM(`consGas`) - SUM(@avgNHConsGas) > 0 THEN ROUND(SUM(`consGas` - @avgNHConsGas), 3)
       ELSE 0
   END AS "corrConsGas",

   -- calculate corrected gas consumption per degree day.
   -- @corrGasCons == NULL...why???
   SUM(@corrConsGas) / SUM(`degrDays`) AS corrConsDD,
   @corrConsGas  2 AS "TEST",


   -- Calculate corrConsGas again to calculate corrected gas consumption per degree day (corrConsGasDD).
   IF( SUM(`consGas` - @avgNHConsGas) > 0, ROUND(SUM(`consGas` - @avgNHConsGas) / SUM(`degrDays`), 3), 0 ) AS "corrConsGasDD"


   FROM `energyTST`
   WHERE date >= '2018-02-01' AND date <= '2018-04-30'
   GROUP BY YEAR(date), MONTH(date)
   ORDER BY YEAR(date), MONTH(date)
  

Все даты хранятся как дата и форматируются как ГГГГ-ММ-ДД без данных о времени, все остальные данные хранятся в виде десятичной дроби (32,3).

На случай, если у кого-нибудь возникнет подобный вопрос; Я, наконец, решил его, используя INNER JOIN для включения вычислений в столбец вместо использования переменных:

 SELECT
   e1.date,

   -- Weighted degree days
   SUM(e2.wDegrDays) AS "wDegrDays",

   -- Gas usage based on daily usage minus average daily usage for showering
   SUM(IF(e1.consGas - e3.avgSU > 0, e1.consGas - e3.avgSU, 0)) AS "corrConsGas",

   -- Gas usage per degree day
   SUM(IF(e1.consGas - e3.avgSU > 0, e1.consGas - e3.avgSU, 0)) / SUM(e2.wDegrDays) AS "corrConsGasDD"

FROM 
   energyTST AS e1

   INNER JOIN
      (SELECT
         date,

         -- Degree days is the difference between daily mean temperature indoor (dmtI) and daily mean temperature outdoor (dmtO).
         -- They are weighed based on the month to account for weather influences (Nov-Feb * 1.1, Apr-Sept * 0.8)
         -- and only calculated when the dmtO is below the heating threshold, otherwise there are 0 degree days.
         CASE
            WHEN MONTH(date) IN (04, 05, 06, 07, 08, 09) THEN ROUND(IF(dmtO < ht, dmtI - dmtO, 0) * 0.8, 3)
            WHEN MONTH(date) IN (11, 12, 01, 02) THEN ROUND(IF(dmtO < ht, dmtI - dmtO, 0) * 1.1, 3)
            ELSE ROUND(IF(dmtO < ht, dmtI - dmtO, 0), 3)
         END AS "wDegrDays"

      FROM energyTST
      ) AS e2 ON e1.date = e2.date

   INNER JOIN
      (SELECT

         -- Average usage on non-heating days = average daily shower usage
         ROUND(AVG(consGas), 3) AS "avgSU"

      FROM energyTST
      WHERE degrDays = 0 AND consGas > 0 AND date >= '2018-06-11'
      ) AS e3

WHERE e1.date >= '2018-02-01' AND e1.date <= '2018-02-28'
GROUP BY YEAR(e1.date), MONTH(e1.date), DAY(e1.date)
ORDER BY YEAR(e1.date), MONTH(e1.date), DAY(e1.date)
  

Комментарии:

1. Используйте подзапросы или CTE, если вы хотите повторно использовать вычисляемые столбцы. Что-то вроде @corrConsGas := SUM(..) as corrConsGas, SUM(@corrConsGas) не будет делать то, что вы ожидаете.

Ответ №1:

Как указано в комментарии Пола, вы должны просто иметь возможность использовать подзапрос следующим образом:

 ... energyTST 
CROSS JOIN (
   SELECT AVG(`consGas`) AS "avgNHConsGas"
   FROM `energyTST` 
   WHERE degrDays = 0 AND consGas > 0 AND DATE >= '2018-06-11'
) AS a
  

а затем замените все ссылки на @avgNHConsGas на a.avgNHConsGas

Порядок вычисления и присвоения выражений, включающих переменные @ / сеанса, не всегда ясен; запросы, включающие агрегирование, являются одним из таких случаев.

Комментарии:

1. ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ не сработало, или, по крайней мере, я не заставил его работать так, как нужно. В итоге я использовал 2 INNER JOIN с их собственным SELECT для выполнения вычислений, и мне удалось получить все, что мне нужно. Спасибо за подсказку в правильном направлении!