Mysql - Как создать представление, представляющее динамику c pivot - PullRequest
0 голосов
/ 06 мая 2020

У меня есть две таблицы базы данных customers, которые содержат данные о клиентах с такой схемой:

mysql> SELECT * FROM customers;

customer_id created_at              partner_id
1           "2019-08-20 09:17:58"   cats
2           "2019-09-12 11:46:37"   dogs

и customers_facts, в которых хранятся факты о клиентах в форме fact_name и соответствующих fact_value.

mysql> SELECT * FROM customers_facts;

customer_id fact_name   fact_value
1,          name        Milton
1           city        Milan
2           surname     Bloom
2           name        Orlando

Я хочу создать сводную таблицу, в которой в каждой строке будет клиент и факты, каждый в отдельном столбце. Примерно так:

mysql> SELECT * FROM pivot_table;

customer_id created_at              partner_id  name    city    surname
1           "2019-08-20 09:17:58"   cats        Milton  Milan   
2           "2019-09-12 11:46:37"   dogs        Orlando         Bloom

Я нашел сценарий, который позволяет мне создать такую ​​таблицу:

SET @sql = '';
SELECT
    @sql := CONCAT(@sql,if(@sql='','',', '),temp.output)
FROM
(
    SELECT
      DISTINCT
        CONCAT(
         'MAX(IF(cf.fact_name = ''',
          fact_name,
          ''', cf.fact_value, NULL)) AS ''',
          fact_name,
          ''''
        ) as output
    FROM
        customers_facts
) as temp;
SET @sql = CONCAT('SELECT c.customer_id, c.created_at, c.partner_id, ', @sql, ' 
                   FROM customers c
                   LEFT JOIN customers_facts AS cf 
                   ON cf.customer_id = c.customer_id
                   GROUP BY c.customer_id, c.created_at, c.partner_id');

, но у меня есть проблема, как это сделать:

a) Я смогу запросить сводную таблицу

b) Когда я добавлю новую запись / обновлю старую в одной из этих двух исходных таблиц, сводная таблица будет обновлена ​​

Как решить? Возможно ли это?

1 Ответ

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

Рассмотрим следующее:

DROP TABLE IF EXISTS customers;

CREATE TABLE customers
(customer_id SERIAL PRIMARY KEY
,created_at DATETIME NOT NULL
,partner_id INT NOT NULL
);

INSERT INTO customers VALUES
(1,"2019-08-20 09:17:58",108),
(2,"2019-09-12 11:46:37",110);

DROP TABLE IF EXISTS customers_facts ;

CREATE TABLE customers_facts 
(customer_id INT NOT NULL
,fact_name  VARCHAR(20) NOT NULL
,fact_value VARCHaR(20) NOT NULL
,PRIMARY KEY(customer_id,fact_name)
);

INSERT INTO customers_facts VALUES
(1,'name','Milton'),
(1,'city','Milan'),
(2,'surname','Bloom'),
(2,'name','Orlando');

Теперь мы можем создать ВИД, как вы описываете ...

DROP VIEW IF EXISTS my_pivot;

CREATE VIEW my_pivot AS
SELECT c.customer_id
     , c.created_at
     , c.partner_id
     , MAX(CASE WHEN fact_name = 'name' THEN fact_value END) name
     , MAX(CASE WHEN fact_name = 'surname' THEN fact_value END) surname
     , MAX(CASE WHEN fact_name = 'city' THEN fact_value END) city
  FROM customers c
  LEFT 
  JOIN customers_facts f
    ON f.customer_id = c.customer_id
 GROUP
    BY c.customer_id;

Мы можем опросить этот ВИД с помощью простого запроса - например, SELECT customer_id FROM my_pivot WHERE name = 'Milton', однако, здесь нельзя использовать индекс, поэтому он не очень эффективен.

Кроме того, из-за того, как мы создали ВИД, его нельзя обновить ...

UPDATE my_pivot SET name = 'Leonardo' WHERE customer_id = 1;
ERROR 1288 (HY000): The target table my_pivot of the UPDATE is not updatable

Однако, если бы мы создали ВИД немного по-другому, то он может быть обновлен ...

DROP VIEW IF EXISTS my_pivot;

CREATE VIEW my_pivot AS
SELECT c.customer_id
     , c.created_at
     , c.partner_id
     , name.fact_value name
     , surname.fact_value surname
     , city.fact_value city
  FROM customers c
  LEFT 
  JOIN customers_facts name
    ON name.customer_id = c.customer_id 
   AND name.fact_name = 'name'
  LEFT
  JOIN customers_facts surname
    ON surname.customer_id = c.customer_id 
   AND surname.fact_name = 'surname'
  LEFT
  JOIN customers_facts city
    ON city.customer_id = c.customer_id 
   AND city.fact_name = 'city';

UPDATE my_pivot SET name = 'Leonardo' WHERE customer_id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

SELECT * FROM customers_facts;
+-------------+-----------+------------+
| customer_id | fact_name | fact_value |
+-------------+-----------+------------+
|           1 | city      | Milan      |
|           1 | name      | Leonardo   |
|           2 | name      | Orlando    |
|           2 | surname   | Bloom      |
+-------------+-----------+------------+

... но это все еще не может использовать индекс.

РЕДАКТИРОВАТЬ: Чтобы ответить на вопрос, заданный в комментариях под вашим вопросом, вы можете сделать ...

SELECT customer_id 
  FROM customers_facts 
 WHERE 
     ( fact_name,fact_value ) IN (('name','Orlando'),('surname','Bloom')) 
 GROUP 
    BY customer_id 
HAVING COUNT(*) = 2;

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

SELECT customer_id 
  FROM customers_facts 
 WHERE 
     ( fact_name = 'name'
   AND fact_value = 'Orlando'
     )
    OR 
     ( fact_name = 'surname'
   AND fact_value = 'Bloom'
     )
 GROUP 
    BY customer_id HAVING COUNT(*) = 2;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...