Запрос возвращает неверную ошибку в столбце, который уже существует?

Я хочу сгруппировать по дате и платежу, а также названию и коду филиала. Я не знаю, почему этот SQL-запрос возвращает эту ошибку:

Столбец «FactSalesTable.SalesTotal» недопустим в списке выбора, поскольку он не содержится ни в агрегатной функции, ни в предложении GROUP BY.

SELECT 
    FactSalesTable.Date,
    FactSalesTable.PaymentID,
    Branch.* ,
    SUM(FactSalesTable.SalesTotal) AS TotalSales,
    AVG (FactSalesTable.SalesTotal) OVER (PARTITION BY Branch.BCode) AS AvgSales,
    CASE
        WHEN SUM(FactSalesTable.SalesTotal) OVER (PARTITION BY Branch.BCode) /AVG(FactSalesTable.SalesTotal) OVER (PARTITION BY Branch.BCode) > 1 THEN 'Above Avg' 
        WHEN SUM(FactSalesTable.SalesTotal) OVER (PARTITION BY Branch.BCode)/AVG(FactSalesTable.SalesTotal) OVER (PARTITION BY Branch.BCode) = 1 THEN 'Avg'
        ELSE 'Below Avg'
    END AS SalesResult
FROM Branch
LEFT JOIN FactSalesTable ON Branch.BCode = FactSalesTable.BCode
WHERE FactSalesTable.SalesTotal > 0 AND Branch.BCode = 1021
GROUP BY FactSalesTable.Date, Branch.BranchName, Branch.BCode, FactSalesTable.PaymentID
ORDER BY FactSalesTable.Date DESC

Я хочу показать общий объем продаж по дате, например

дата БКод общий объем продаж среднее 2023-04-30 1021 4333 5050 30 апреля 2022 г. 1021 6392 5050 2022-04-29 1021 3544 5050 2022-04-29 1021 2300 5050

Это фрагмент запроса без группы по

🤔 А знаете ли вы, что...
SQL был создан в 1970-х годах компанией IBM.


51
1

Ответ:

Решено

Вы агрегируете данные на двух уровнях:

  1. Обычная агрегация SUM(SalesTotal) для каждого отделения и платежа, как определено в GROUP BY.
  2. Оконная агрегация SUM(...) OVER(...) и AVG(...) OVER(...) для всех платежей по филиалу, как определено спецификатором окна OVER(,,,).

Ваша проблема в том, что ваши оконные функции агрегации пытаются получить доступ к необработанным данным, хотя я считаю, что вы собираетесь суммировать и усреднять сгруппированные значения. Вместо AVG(SalesTotal) OVER(...), я думаю, вам нужен AVG(SUM(SalesTotal)) OVER(...). Однако для случаев SUM() в расчете SalesResult я считаю, что вам нужна только простая (не оконная) ветвь и сумма платежа SUM(SalesTotal), а не сумма сумм на уровне ветки.

Кроме того, если вы ограничили FactSalesTable.SalesTotal > 0, строки FactSalesTable должны присутствовать, а ваш LEFT JOIN фактически является INNER JOIN.

Следующее должно быть ближе к тому, чего вы пытаетесь достичь.

SELECT 
    FactSalesTable.Date,
    FactSalesTable.PaymentID,
    Branch.* ,
    SUM(FactSalesTable.SalesTotal) AS TotalSales,
    AVG (SUM(FactSalesTable.SalesTotal)) OVER(PARTITION BY Branch.BCode) as AvgSales ,
    case 
        when SUM(FactSalesTable.SalesTotal)
             / AVG(SUM(FactSalesTable.SalesTotal)) OVER(PARTITION BY Branch.BCode) > 1
             THEN 'Above Avg' 
        when SUM(FactSalesTable.SalesTotal)
             / AVG(SUM(FactSalesTable.SalesTotal)) OVER(PARTITION BY Branch.BCode) = 1
            THEN 'Avg'
        else 'Below Avg'
    end as SalesResult
FROM Branch
LEFT JOIN FactSalesTable  ON Branch.BCode = FactSalesTable.BCode
WHERE FactSalesTable.SalesTotal > 0 and Branch.BCode = 1021
Group BY FactSalesTable.Date,Branch.BranchName ,Branch.BCode,FactSalesTable.PaymentID
ORDER BY FactSalesTable.Date DESC

Результаты:

Дата Идентификатор платежа БКод Имя ветки Всего продаж Средние продажи Результат продаж 30 апреля 2022 г. 57517 1021 Главный филиал 19939.00 16223.000000 Выше среднего 30 апреля 2022 г. 57519 1021 Главный филиал 12507.00 16223.000000 Ниже среднего

См. эту db<>fiddle. (Спасибо Барту МакЭндри за настройку тестовых данных.)

Вероятно, существуют способы дальнейшей реструктуризации запроса, чтобы ограничить избыточные выражения и улучшить читаемость, например, переместить исходную логику группировки в подзапрос или CTE и выполнить оконные операции в конечном/внешнем запросе. Другой вариант — обернуть всю логику платежей в CROSS APPLY.