Как создать две строки общего итога с помощью SQL - итоги и средние - PullRequest
0 голосов
/ 08 января 2019

Мне нужно создать две строки, которые содержат итоги, в отличие от типичной строки итогов. Великие итоги и средние.

Я создаю отчет, используя базовый SQL, и я работаю с базой данных Oracle, но я не использую PL / SQL.

В настоящее время я использую наборы группировки по группам для создания отчета, а одна строка - это строка, содержащая итоговые итоги. Эти итоги в настоящее время производятся с использованием SUM (столбец) с использованием совокупности агрегатных и аналитических функций для создания моей единой строки общих итогов. Что мне нужно, это еще одна строка, которая производит итоги на том же наборе данных. Каков наилучший способ сделать это? Когда я говорю лучше всего, я думаю о нагрузке на мою базу данных, так как этот отчет будет работать с кучей данных. Мои примеры очень просты, но все понятно.

Ниже приведены некоторые примеры данных, которые дают итоговые итоговые итоги с использованием групп по группам. Чего не хватает, так это еще одной строки под «Великими итогами», которую я хотел бы получить.

WITH sample_data AS
(
  SELECT 1 AS client_key, 'NASA'   AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL UNION ALL
  SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581  AS REVENUE FROM DUAL UNION ALL
  SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851  AS REVENUE FROM DUAL
)

SELECT sd.client_key
  , CASE WHEN grouping(sd.client) = 0 THEN to_char(sd.client) ELSE 'Grand Totals -->' END AS client
  , SUM(sd.spaceships_sold) AS spaceships_sold
  , SUM(sd.revenue)         AS revenue
FROM sample_data sd
GROUP BY 
  GROUPING SETS (
                  (sd.client_key, sd.client),
                  ()
                )
;

Пример изображения того, что я ищу.

enter image description here

Ниже приведены мои мысли о том, как я могу получить этот дополнительный Итоговый ряд, но не уверен, что это то, что я должен делать, чтобы получить это. Кажется запутанным, и я продолжаю думать, что это должно быть существующей особенностью группировок. В приведенном ниже подходе я использую CTE и UNION ALL, чтобы получить дополнительные средние итоги в нижней части моего набора данных, как показано на снимке экрана ниже.

enter image description here

SQL из приведенного выше скриншота.

WITH sample_data AS
(
  SELECT 1 AS client_key, 'NASA'   AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL UNION ALL
  SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581  AS REVENUE FROM DUAL UNION ALL
  SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851  AS REVENUE FROM DUAL
)

, data_Sum_totals AS
(
  SELECT sd.client_key
    , CASE WHEN grouping(sd.client) = 0 THEN to_char(sd.client) ELSE 'Grand Totals -->' END AS client
    , SUM(sd.spaceships_sold) AS spaceships_sold
    , SUM(sd.revenue)         AS revenue
  FROM sample_data sd
  GROUP BY 
    GROUPING SETS (
                    (sd.client_key, sd.client),
                    ()
                  )
)

, data_Avg_totals AS
(
  SELECT grouping(sd.client_key) AS row_group
    , sd.client_key
    , CASE WHEN grouping(sd.client) = 0 THEN to_char(sd.client) ELSE 'AVG Totals -->' END AS client
    , AVG(sd.spaceships_sold) AS spaceships_sold
    , AVG(sd.revenue)         AS revenue
  FROM sample_data sd
  GROUP BY 
    GROUPING SETS (
                    (sd.client_key, sd.client),
                    ()
                  )
  HAVING grouping(sd.client_key) = 1 /* This line restricts the output to only give me the Totals row */
)

SELECT client_key, client, spaceships_sold, revenue
FROM data_Sum_totals
  UNION ALL
SELECT client_key, client, spaceships_sold, revenue
FROM data_Avg_totals
;

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Вы указали:

Я продолжаю думать, что это должно быть существующей функцией группировки Множества. В приведенном ниже подходе я использую CTE и UNION ALL , чтобы получить дополнительные средние итоги в нижней части моего набора данных, как видно из скриншот ниже

и как определяется тег [grouping-sets] :

Оператор GROUPING SETS является расширением предложения GROUP BY . Он может генерировать тот же набор результатов, что и при использовании UNION ALL для объединения одиночные групповые запросы; однако, используя GROUPING SETS , оператор равен обычно более эффективно .

Поэтому у вас был такой хороший подход.

Я думаю, что использование GROUPING_ID лучше всего подходит для вашего случая, как в следующем выражении SQL:

SELECT client_key, 
       CASE WHEN flag = 3 THEN 'AVG Totals -.->' 
            WHEN flag = 2 THEN 'Grand Totals -.->'
            ELSE client 
        END AS client , 
       SUM(spaceships_sold)/ DECODE(flag,3,3,1) AS spaceships_sold, 
       SUM(revenue)/ DECODE(flag,3,3,1) AS revenue
  FROM
  (
    WITH sample_data AS
    (
     SELECT 1 AS client_key, 'NASA'   AS client, 8 AS SPACESHIPS_SOLD, 105585 AS REVENUE FROM DUAL 
     UNION ALL
     SELECT 2 AS client_key, 'Origin' AS client, 3 AS SPACESHIPS_SOLD, 36581  AS REVENUE FROM DUAL 
     UNION ALL
     SELECT 3 AS client_key, 'SpaceX' AS client, 7 AS SPACESHIPS_SOLD, 83851  AS REVENUE FROM DUAL
     )
      SELECT sd.client_key, 
             nvl2(sd.client_key,client,null) AS client
           , SUM(sd.spaceships_sold) AS spaceships_sold
           , SUM(sd.revenue)         AS revenue
           , GROUPING_ID(sd.client_key, sd.client) AS flag
        FROM sample_data sd
       GROUP BY 
      GROUPING SETS (
                      (sd.client_key, sd.client),
                       (sd.client),()
                      )
    )    
  GROUP BY client_key, flag, client
  ORDER BY client_key, revenue desc;



  CLIENT_KEY    CLIENT           SPACESHIPS_SOLD    REVENUE
  -----------   ---------------- ---------------   --------
       1        NASA                   8             105585
       2        Origin                 3              36581
       3        SpaceX                 7              83851
      NULL      Grand Totals -.->     18             226017
      NULL      AVG Totals -.->        6              75339

Rextester Demo

Обновление до SQL для работы с любым числом или записями, известными как клиенты

SELECT client_key, 
     CASE WHEN flag = 3 THEN 'AVG Totals -->' 
          WHEN flag = 2 THEN 'Grand Totals -->'
          ELSE client 
      END AS client 
      , flag,
     SUM(spaceships_sold)/ DECODE(flag,3,tot_clients,1) AS spaceships_sold, 
     SUM(revenue)/ DECODE(flag,3,tot_clients,1) AS revenue
FROM
(
  WITH sample_data AS
  (
     SELECT 1 AS client_key, 'NASA'   AS client, 8  AS SPACESHIPS_SOLD, 105585  AS REVENUE FROM DUAL 
     UNION ALL
     SELECT 2 AS client_key, 'Origin' AS client, 3  AS SPACESHIPS_SOLD, 36581   AS REVENUE FROM DUAL 
     UNION ALL
     SELECT 3 AS client_key, 'SpaceX' AS client, 7  AS SPACESHIPS_SOLD, 83851   AS REVENUE FROM DUAL
     UNION ALL
     SELECT 4 AS client_key, 'Comp'   AS client, 4  AS SPACESHIPS_SOLD, 95823   AS REVENUE FROM DUAL
     UNION ALL
     SELECT 4 AS client_key, 'CNSA'   AS client, 11 AS SPACESHIPS_SOLD, 135851  AS REVENUE FROM DUAL
   )
    SELECT sd.client_key, 
           nvl2(sd.client_key,client,null) AS client
         , SUM(sd.spaceships_sold) AS spaceships_sold
         , SUM(sd.revenue)         AS revenue
         , COUNT(sd.client_key)    AS tot_clients
         , GROUPING_ID(sd.client_key, sd.client) AS flag
      FROM sample_data sd
     GROUP BY 
    GROUPING SETS (
                    (sd.client_key, sd.client),
                     (sd.client),()
                    )
  )    
GROUP BY client_key, flag, client, tot_clients
ORDER BY client_key, revenue desc
;
0 голосов
/ 08 января 2019

CTE - это оконные функции, поэтому их нельзя переносить так далеко, как вы ожидаете. Для этой проблемы, я думаю, у вас есть хорошая идея, но, вероятно, просто используйте пару временных таблиц для размещения конкретных данных, а затем объедините все вместе в конце.

Вот запрос, который я придумал:

-- Clear out temporary tables
IF OBJECT_ID('tempdb.dbo.#SampleData') IS NOT NULL DROP TABLE #SampleData
IF OBJECT_ID('tempdb.dbo.#TotTable') IS NOT NULL DROP TABLE #TotTable
IF OBJECT_ID('tempdb.dbo.#AvgTable') IS NOT NULL DROP TABLE #AvgTable

-- Create
DECLARE @_tot INT
DECLARE @_avg NUMERIC(18,2)
DECLARE @client_count INT

-- Sample Data
CREATE TABLE #SampleData (
    [CLIENT_KEY] INT,
    [CLIENT] NVARCHAR(10),
    [SPACESHIPS_SOLD] VARCHAR(10),
    [REVENUE] VARCHAR(25)
)

INSERT INTO #SampleData
VALUES (1,'NASA','8','105585'),
        (2,'Origin','3','36581'),
        (3,'SpaceX','7','83851')


-- Get our total numbers
SELECT 'Grand Totals' AS [Name],
SUM(CONVERT(INT, [REVENUE])) AS [Total_Rev],
SUM(CONVERT(INT, [SPACESHIPS_SOLD])) AS [Ships_Sold] 
INTO #TotTable
FROM #SampleData

-- Get our average numbers
SET @client_count = (SELECT COUNT([CLIENT]) FROM #SampleData)
SELECT 'AVG Totals' AS [Name],
SUM(CONVERT(INT, [REVENUE])) / COUNT(*) AS [Avg_Rev],
SUM(CONVERT(INT, [SPACESHIPS_SOLD])) / @client_count AS [Avg_Sold]
INTO #AvgTable
FROM #SampleData

-- Union it all together
SELECT
    [CLIENT_KEY],
    [CLIENT],
    [SPACESHIPS_SOLD],
    [REVENUE]
FROM #SampleData
UNION ALL
SELECT
    NULL AS [CLIENT_KEY],
    [Name] AS [CLIENT],
    [Ships_Sold]  [SPACESHIPS_SOLD],
    [Total_Rev] AS [REVENUE]
FROM #TotTable
UNION ALL
SELECT
    NULL AS [CLIENT_KEY],
    [Name] AS [CLIENT],
    [Avg_Sold]  [SPACESHIPS_SOLD],
    [Avg_Rev] AS [REVENUE]
FROM #AvgTable

--Clear out tables (not necessary, but nice to do)
IF OBJECT_ID('tempdb.dbo.#SampleData') IS NOT NULL DROP TABLE #SampleData
IF OBJECT_ID('tempdb.dbo.#TotTable') IS NOT NULL DROP TABLE #TotTable
IF OBJECT_ID('tempdb.dbo.#AvgTable') IS NOT NULL DROP TABLE #AvgTable
...