Транспонирование таблицы с помощью запроса выбора - PullRequest
2 голосов
/ 10 декабря 2010

У меня есть таблица типа:

Key  type      value
---------------------
40     A        12.34    
41     A        10.24    
41     B        12.89

Я хочу ее в формате:

Types     40    41     42 (keys)
---------------------------------
A       12.34   10.24  XXX    
B       YYY     12.89  ZZZ

Как это можно сделать с помощью SQL-запроса.Заявления дел, расшифровка

Ответы [ 4 ]

3 голосов
/ 10 декабря 2010

То, что вы ищете, называется " pivot " (см. Также " Pivoting Operations " в Руководстве по хранилищу данных Oracle Database):

SELECT * 
  FROM tbl
  PIVOT(SUM(value) FOR Key IN (40, 41, 42))

Был добавлен в Oracle в 11g. Обратите внимание, что вам нужно указать столбцы результата (значения из неотключенного столбца, которые становятся именами поворотного столбца) в предложении сводки. Любые столбцы, не указанные в сводке, неявно группируются. Если в исходной таблице есть столбцы, по которым вы не хотите группировать данные, выберите их из представления или подзапроса, а не из таблицы.

Вы можете немного пообщаться с wizardry и заставить Oracle создать для вас оператор, так что вам не нужно будет выяснять, какие значения столбцов нужно развернуть. В 11g, когда вы знаете, что значения столбцов являются числовыми:

SELECT
    'SELECT * FROM tbl PIVOT(SUM(value) FOR Key IN ('
    || LISTAGG(Key, ',') WITHIN GROUP (ORDER BY Key)
    || ');'
  FROM tbl;

Если значения столбца могут быть не числовыми:

SELECT
    'SELECT * FROM tbl PIVOT(SUM(value) FOR Key IN (\''
    || LISTAGG(Key, '\',\'') WITHIN GROUP (ORDER BY Key)
    || '\'));'
  FROM tbl;

LISTAGG вероятно, повторяет дубликаты (кто-нибудь проверит это?), В этом случае вам понадобится:

SELECT
    'SELECT * FROM tbl PIVOT(SUM(value) FOR Key IN (\''
    || LISTAGG(Key, '\',\'') WITHIN GROUP (ORDER BY Key)
    || '\'));'
  FROM (SELECT DISTINCT Key FROM tbl);

Вы можете пойти дальше, определив функцию, которая принимает имя таблицы, статистическое выражение и имя столбца сводной таблицы, которая возвращает сводную инструкцию, сначала производя, а затем оценивая вышеуказанную инструкцию. Затем вы можете определить процедуру, которая принимает те же аргументы и выдает поворотный результат. У меня нет доступа к Oracle 11g, чтобы проверить его, но я считаю, что это будет выглядеть примерно так:

CREATE PACKAGE dynamic_pivot AS
  -- creates a PIVOT statement dynamically
  FUNCTION pivot_stmt (tbl_name IN varchar2(30),
                       pivot_col IN varchar2(30), 
                       aggr IN varchar2(40), 
                       quote_values IN BOOLEAN DEFAULT TRUE)
      RETURN varchar2(300);
  PRAGMA RESTRICT_REFERENCES (pivot_stmt, WNDS, RNPS);

  -- creates & executes a PIVOT
  PROCEDURE pivot_table (tbl_name IN varchar2(30),
                         pivot_col IN varchar2(30), 
                         aggr IN varchar2(40), 
                         quote_values IN BOOLEAN DEFAULT TRUE);
END dynamic_pivot;

CREATE PACKAGE BODY dynamic_pivot AS
  FUNCTION pivot_stmt (
      tbl_name IN varchar2(30), 
      pivot_col IN varchar2(30), 
      aggr_expr IN varchar2(40), 
      quote_values IN BOOLEAN DEFAULT TRUE
    ) RETURN varchar2(300)
  IS
     stmt VARCHAR2(400);
     quote VARCHAR2(2) DEFAULT '';
  BEGIN
     IF quote_values THEN
       quote :=  '\\\'';
     END IF;
     -- "\||" shows that you are still in the dynamic statement string
     -- The input fields aren't sanitized, so this is vulnerable to injection
     EXECUTE IMMEDIATE 'SELECT \'SELECT * FROM ' || tbl_name 
             || ' PIVOT(' || aggr_expr || ' FOR ' || pivot_col 
             || ' IN (' || quote || '\' \|| LISTAGG(' || pivot_col
                        || ', \'' || quote || ',' || quote
             || '\') WITHIN GROUP (ORDER BY ' || pivot_col || ') \|| \'' || quote 
             || '));\' FROM (SELECT DISTINCT ' || pivot_col || ' FROM ' || tbl_name || ');'
       INTO stmt;
     RETURN stmt;
  END pivot_stmt;

  PROCEDURE pivot_table (tbl_name IN varchar2(30), pivot_col IN varchar2(30), aggr_expr IN varchar2(40), quote_values IN BOOLEAN DEFAULT TRUE) IS
  BEGIN
    EXECUTE IMMEDIATE pivot_stmt(tbl_name, pivot_col, aggr_expr, quote_values);
  END pivot_table;
END dynamic_pivot;

Примечание. Длина параметров tbl_name, pivot_col и aggr_expr определяется максимальной длиной таблицы и имени столбца . Также обратите внимание, что функция уязвима для внедрения SQL.

В версии до 11g можно применять методы генерации сводных операторов MySQL (которые генерируют тип запросов, отправленных другими, на основе явного определения отдельного столбца для каждого значения сводки).

1 голос
/ 10 декабря 2010

Pivot сильно упрощает вещи.Однако до 11g вам нужно сделать это вручную.

select 
  type, 
  sum(case when key = 40 then value end) as val_40,
  sum(case when key = 41 then value end) as val_41,
  sum(case when key = 42 then value end) as val_42
from my_table
group by type;
0 голосов
/ 10 декабря 2010

Если у вас нет доступа к 11g, вы можете использовать агрегирование строк и метод группировки до прибл.то, что вы ищете, например

with data as(
SELECT 40 KEY , 'A' TYPE , 12.34 VALUE FROM DUAL UNION 
SELECT 41 KEY , 'A' TYPE , 10.24 VALUE FROM DUAL UNION 
SELECT 41 KEY , 'B' TYPE , 12.89 VALUE FROM DUAL 
)
          select   
                 TYPE ,                  
                 wm_concat(KEY) KEY ,
                wm_concat(VALUE) VALUE 
              from   data       
        GROUP BY TYPE;  

type     KEY     VALUE
------ ------- -----------
A       40,41   12.34,10.24
B       41      12.89               

Это основано на wm_concat, как показано здесь: http://www.oracle -base.com / Articles / Misc / StringAggregationTechniques.php

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

0 голосов
/ 10 декабря 2010

Никогда не пробовал, но, похоже, в Oracle 11 есть предложение PIVOT

...