Dynami c Pivotting в PostgreSQL - PullRequest
       125

Dynami c Pivotting в PostgreSQL

1 голос
/ 02 мая 2020

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

Order ID     Employee      Product Category
-------------------------------------------
1             Alan          Automobile
2             Barry         Beauty
3             Charlie       Clothing
4             Alan          Beauty

Я бы хотел иметь возможность запрашивать и получать результат:

Employee     Count Auto     Count Beauty     Count Clothing
------------------------------------------------------------
Alan         1              1                0
Barry        0              1                0
Charlie      0              0                1

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

SELECT employee, category, COUNT(*) FROM sales GROUP BY employee, category;

Что возвращает:

Employee     Category     Count
-------------------------------
Alan         Automobile   1
Alan         Beauty       1
Alan         Clothing     0

et c. Но с большим количеством категорий это может стать немного избыточным. Есть ли способ вернуть его в виде одной строки для каждого сотрудника со столбцом для каждой категории?

1 Ответ

2 голосов
/ 02 мая 2020

Вы можете использовать JSON заход на посадку

SELECT employee,
  json_object_agg(ProductCategory,total ORDER BY ProductCategory)
FROM (
 SELECT employee, ProductCategory, count(*) AS total
    FROM tbl
    GROUP BY employee,ProductCategory
) s
GROUP BY employee
ORDER BY employee;

или с двухступенчатым заходом на посадку

CREATE FUNCTION dynamic_pivot(central_query text, headers_query text)
 RETURNS refcursor AS
$$
DECLARE
  left_column text;
  header_column text;
  value_column text;
  h_value text;
  headers_clause text;
  query text;
  j json;
  r record;
  curs refcursor;
  i int:=1;
BEGIN
  -- find the column names of the source query
  EXECUTE 'select row_to_json(_r.*) from (' ||  central_query || ') AS _r' into j;
  FOR r in SELECT * FROM json_each_text(j)
  LOOP
    IF (i=1) THEN left_column := r.key;
      ELSEIF (i=2) THEN header_column := r.key;
      ELSEIF (i=3) THEN value_column := r.key;
    END IF;
    i := i+1;
  END LOOP;

  --  build the dynamic transposition query (based on the canonical model)
  FOR h_value in EXECUTE headers_query
  LOOP
    headers_clause := concat(headers_clause,
     format(chr(10)||',min(case when %I=%L then %I::text end) as %I',
           header_column,
       h_value,
       value_column,
       h_value ));
  END LOOP;

  query := format('SELECT %I %s FROM (select *,row_number() over() as rn from (%s) AS _c) as _d GROUP BY %I order by min(rn)',
           left_column,
       headers_clause,
       central_query,
       left_column);

  -- open the cursor so the caller can FETCH right away
  OPEN curs FOR execute query;
  RETURN curs;
END 
$$ LANGUAGE plpgsql;

затем

=> BEGIN;

-- step 1: get the cursor (we let Postgres generate the cursor's name)
=> SELECT dynamic_pivot(
       'SELECT employee,ProductCategory,count(*) 
          FROM tbl GROUP BY employee,ProductCategory
          ORDER BY 1',
       'SELECT DISTINCT productCategory FROM tbl ORDER BY 1'
     ) AS cur
     \gset

-- step 2: read the results through the cursor
=> FETCH ALL FROM :"cur";

Ссылка

...