Як я можу повернути висновок зведеної таблиці в MySQL?


312

Якщо у мене таблиця MySQL виглядає приблизно так:

company_name action pagecount page
-------------------------------
Компанія A PRINT 3
Компанія A PRINT 2
Компанія A PRINT 3
Компанія B EMAIL   
Компанія B PRINT 2
Компанія B PRINT 2
Компанія B PRINT 1
Компанія A PRINT 3

Чи можливо запустити запит MySQL, щоб отримати такий вихід:

ім'я компанії EMAIL PRINT 1 сторінки PRINT 2 сторінки PRINT 3 сторінки
-------------------------------------------------- -----------
КомпаніяA 0 0 1 3
КомпаніяB 1 1 2 0

Ідея полягає в тому, що вона pagecountможе змінюватись, тому кількість вихідного стовпця має відображати це, один стовпець для кожної action/ pagecountпари, а потім кількість звернень на кожного company_name. Я не впевнений, чи називається це зведена таблиця, але хтось це запропонував?


3
Це називається поворотним, і це набагато, набагато швидше зробити це перетворення поза SQL.
NB

1
Excel перебирає подібні речі, в MySQL це дуже складно, оскільки немає оператора "CROSSTAB" :(
Дейв Рікс

Так, це в даний час робиться вручну в Excel, і ми намагаємося його автоматизувати.
пеку

3
Тут я знайшов крок за кроком приклад: як автоматизувати зведені таблиці . і це
Девід Г

1
@giannischristofakis - це дійсно залежить від того, що ви та ваші колеги вважаєте простішими. Технологія наздогнала зовсім небагато, коли я опублікував коментар (4 роки), так що це повністю залежить від того, що вам здається краще - будь то в додатку чи SQL. Наприклад, в моїй роботі ми маємо справу з подібною проблемою, але ми поєднуємо в собі і SQL, і підхід в додатку. По суті, я не можу вам допомогти, окрім як дати сумнівну відповідь, і це не те, що вам потрібно :)
NB

Відповіді:


236

Це в основному є зведена таблиця.

Гарний підручник про те, як цього досягти, ви можете прочитати тут: http://www.artfulsoftware.com/infotree/qrytip.php?id=78

Раджу прочитати цю публікацію та адаптувати це рішення до ваших потреб.

Оновлення

Після того, як посилання вище в даний час більше не доступне, я відчуваю обов'язок надати деяку додаткову інформацію для всіх вас, хто шукає сюди відповіді mysql. У неї дійсно було величезна кількість інформації, і я не буду вкладати все звідси сюди (тим більше, що я просто не хочу копіювати їх величезні знання), але я дам кілька порад, як боротися зі стрижком. наводить таблицю sql, як правило, на прикладі пеку, який задав питання в першу чергу.

Можливо, посилання повернеться незабаром, я буду стежити за цим.

Спосіб електронної таблиці ...

Багато людей просто використовують для цієї мети такий інструмент, як MSExcel, OpenOffice або інші електронні таблиці. Це вірне рішення, просто скопіюйте дані туди і використовуйте інструменти, які пропонує графічний інтерфейс для вирішення цього питання.

Але ... це не питання, і це навіть може призвести до деяких недоліків, як, наприклад, як занести дані в електронну таблицю, проблематичне масштабування тощо.

Шлях SQL ...

З огляду на його стіл виглядає приблизно так:

CREATE TABLE `test_pivot` (
  `pid` bigint(20) NOT NULL AUTO_INCREMENT,
  `company_name` varchar(32) DEFAULT NULL,
  `action` varchar(16) DEFAULT NULL,
  `pagecount` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`pid`)
) ENGINE=MyISAM;

Тепер загляньте в його бажану таблицю:

company_name    EMAIL   PRINT 1 pages   PRINT 2 pages   PRINT 3 pages
-------------------------------------------------------------
CompanyA        0       0               1               3
CompanyB        1       1               2               0

Рядки ( EMAIL, PRINT x pages) нагадують умови. Основне угрупування - по company_name.

Для того, щоб створити умови, це досить кричить для використання CASE-повідання. Для того , щоб згрупувати по чому - то, ну, використання ... GROUP BY.

Основний SQL, що забезпечує цей стрижень, може виглядати приблизно так:

SELECT  P.`company_name`,
    COUNT(
        CASE 
            WHEN P.`action`='EMAIL' 
            THEN 1 
            ELSE NULL 
        END
    ) AS 'EMAIL',
    COUNT(
        CASE 
            WHEN P.`action`='PRINT' AND P.`pagecount` = '1' 
            THEN P.`pagecount` 
            ELSE NULL 
        END
    ) AS 'PRINT 1 pages',
    COUNT(
        CASE 
            WHEN P.`action`='PRINT' AND P.`pagecount` = '2' 
            THEN P.`pagecount` 
            ELSE NULL 
        END
    ) AS 'PRINT 2 pages',
    COUNT(
        CASE 
            WHEN P.`action`='PRINT' AND P.`pagecount` = '3' 
            THEN P.`pagecount` 
            ELSE NULL 
        END
    ) AS 'PRINT 3 pages'
FROM    test_pivot P
GROUP BY P.`company_name`;

Це має забезпечити бажаний результат дуже швидко. Основний недолік цього підходу, чим більше рядків потрібно у вашій зведеній таблиці, тим більше умов потрібно визначити у своєму операторі SQL.

З цим теж можна впоратися, тому люди, як правило, використовують підготовлені заяви, процедури, лічильники тощо.

Деякі додаткові посилання на цю тему:


4
Здається, посилання наразі працює ... якщо воно коли-небудь знову знизиться, спробуйте такі: кеш Google webcache.googleusercontent.com/… або Інтернет- зворотна машина ( web.archive.org/web/20070303120558 * / artfulsoftware.com/ infotree / queries.php )
Lykegenes

посилання доступне за цією URL-адресою artfulsoftware.com/infotree/qrytip.php?id=78
MrPandav

1
Є ще один спосіб генерувати зведену таблицю без використання "if", "case" або "GROUP_CONCAT": en.wikibooks.org/wiki/MySQL/Pivot_table
user2513149

Ви можете вилучити ELSE NULL зі свого CASE, оскільки капелюх є поведінкою за замовчуванням (а умовне агрегування досить багатослівне)
Caius Jard

86

Моє рішення знаходиться в T-SQL без будь-яких поворотів:

SELECT
    CompanyName,  
    SUM(CASE WHEN (action='EMAIL') THEN 1 ELSE 0 END) AS Email,
    SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END) AS Print1Pages,
    SUM(CASE WHEN (action='PRINT' AND pagecount=2) THEN 1 ELSE 0 END) AS Print2Pages,
    SUM(CASE WHEN (action='PRINT' AND pagecount=3) THEN 1 ELSE 0 END) AS Print3Pages
FROM 
    Company
GROUP BY 
    CompanyName

2
Це працює для мене навіть на PostgreSQL. Я вважаю за краще цей метод, ніж використання розширення
crosstab

2
"Моє рішення знаходиться в T-SQL без поворотів:" Не тільки на SQL Server він повинен працювати на більшості постачальників баз даних, що відповідає стандартам ANSI SQL. Зауважте, що SUM()працювати з цифровими даними можна лише в тому випадку, якщо ви не зможете зводити рядки, вам доведеться використовуватиMAX()
Raymond Nijland

1
Я думаю, що CASE зайвий SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END), ви можете просто зробити, SUM(action='PRINT' AND pagecount=1)оскільки умова буде перетворена на значення 1true, 0коли false
kajacx

1
@kajacx так, хоча це потрібно в базі даних, яка не має такого булевого маніпулювання. З огляду на вибір між "довшим синтаксисом, який працює на всіх дБ", і "коротким синтаксисом, який працює лише на ...", я вибрав би колишнє
Caius Jard

66

Для MySQL ви можете безпосередньо поставити умови у SUM()функцію, і вона буде оцінюватися як булева 0або,1 таким чином, ви можете мати свій підрахунок за вашими критеріями, не використовуючи IF/CASEзаяви

SELECT
    company_name,  
    SUM(action = 'EMAIL')AS Email,
    SUM(action = 'PRINT' AND pagecount = 1)AS Print1Pages,
    SUM(action = 'PRINT' AND pagecount = 2)AS Print2Pages,
    SUM(action = 'PRINT' AND pagecount = 3)AS Print3Pages
FROM t
GROUP BY company_name

DEMO


1
Це справді акуратно. Чи знаєте ви, чи це стандарти відповідають стандартам інших платформ (наприклад, Postgres)?
itsols

3
@itsols Немає лише для Mysql конкретних
M Khalid Junaid

@itsols: я додав ще одну стандартну версію SQL . Постгрес також має виділену crosstab()функцію.
Ервін Брандстеттер

2
Працює також для SQLite
SBF

37

Для динамічного повороту використовуйте GROUP_CONCATс CONCAT. Функція GROUP_CONCAT об'єднує рядки з групи в одну рядок з різними параметрами.

SET @sql = NULL;
SELECT
    GROUP_CONCAT(DISTINCT
    CONCAT(
      'SUM(CASE WHEN action = "',
      action,'"  AND ', 
           (CASE WHEN pagecount IS NOT NULL 
           THEN CONCAT("pagecount = ",pagecount) 
           ELSE pagecount IS NULL END),
      ' THEN 1 ELSE 0 end) AS ',
      action, IFNULL(pagecount,'')

    )
  )
INTO @sql
FROM
  t;

SET @sql = CONCAT('SELECT company_name, ', @sql, ' 
                  FROM t 
                   GROUP BY company_name');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

ДЕМО ТУТ


2
Pacerier, справжня людина, але для динамічного підкреслення свого найкращого підходу
Абхішек Гупта

2
Це добре працює, якщо у вас є багато значень у стовпці "дії" або очікуєте, що цей список зростатиме з часом, оскільки написання заяви про кожне значення може зайняти багато часу та важко оновлювати його.
Патрік Мерфі

23

Версія stardard-SQL з використанням булевої логіки :

SELECT company_name
     , COUNT(action = 'EMAIL' OR NULL) AS "Email"
     , COUNT(action = 'PRINT' AND pagecount = 1 OR NULL) AS "Print 1 pages"
     , COUNT(action = 'PRINT' AND pagecount = 2 OR NULL) AS "Print 2 pages"
     , COUNT(action = 'PRINT' AND pagecount = 3 OR NULL) AS "Print 3 pages"
FROM   tbl
GROUP  BY company_name;

SQL Fiddle.

Як?

TRUE OR NULL врожайність TRUE.
FALSE OR NULLврожайність NULL.
NULL OR NULLврожайність NULL.
І COUNTпідраховує лише ненульові значення. Войла.


@Erwin, але як би ти знав, що є три колонки? Що робити, якщо їх 5? 10? 20?
Pacerier

@Pacerier: Приклад у запитанні, здається, це підказує. У будь-якому випадку SQL вимагає знати тип повернення. повністю динамічний клопотання не представляється можливим. Якщо кількість вихідних стовпців може змінюватися, вам потрібно два кроки: 1-й - складіть запит, 2-й: виконайте його.
Ервін Брандштеттер

11

Правильна відповідь:

select table_record_id,
group_concat(if(value_name='note', value_text, NULL)) as note
,group_concat(if(value_name='hire_date', value_text, NULL)) as hire_date
,group_concat(if(value_name='termination_date', value_text, NULL)) as termination_date
,group_concat(if(value_name='department', value_text, NULL)) as department
,group_concat(if(value_name='reporting_to', value_text, NULL)) as reporting_to
,group_concat(if(value_name='shift_start_time', value_text, NULL)) as shift_start_time
,group_concat(if(value_name='shift_end_time', value_text, NULL)) as shift_end_time
from other_value
where table_name = 'employee'
and is_active = 'y'
and is_deleted = 'n'
GROUP BY table_record_id

1
Це лише приклад, який ти мав під рукою? Яка структура other_valueстолу?
Патрік Мерфі

1
"Правильна відповідь:" Швидше за все, це не так, як це не вистачає SETзапиту на збільшення значення defualt, яке обмежене 1024 для GROUP_CONCAT. Після 1024 GROUP_CONCAT просто обрізає рядок без помилки. Це означає, що можливі несподівані результати.
Raymond Nijland,

Вибачте, хлопці не можуть згадати більше деталей. Я роблю речі для розваги, а потім забуваю або знищую весь проект. Але коли я натрапляю на виклик, я ділюсь тим, як я його виправив. Я знаю , що мій приклад не дуже докладно , але я припускаю , що це може давати вказівки тим , хто знає , що вони проти :)
Talha

9

Існує інструмент під назвою MySQL Pivot генератор таблиць, який допоможе вам створити веб-перетворювальну таблицю, яку згодом ви зможете експортувати в excel (якщо вам подобається). він може працювати, якщо ваші дані є в одній таблиці або в декількох таблицях.

Все, що вам потрібно зробити - це вказати джерело даних стовпців (він підтримує динамічні стовпці), рядки, значення в тілі таблиці та співвідношення таблиці (якщо такі є) Сводна таблиця MySQL

Домашня сторінка цього інструменту - http://mysqlpivottable.net


3
select t3.name, sum(t3.prod_A) as Prod_A, sum(t3.prod_B) as Prod_B, sum(t3.prod_C) as    Prod_C, sum(t3.prod_D) as Prod_D, sum(t3.prod_E) as Prod_E  
from
(select t2.name as name, 
case when t2.prodid = 1 then t2.counts
else 0 end  prod_A, 

case when t2.prodid = 2 then t2.counts
else 0 end prod_B,

case when t2.prodid = 3 then t2.counts
else 0 end prod_C,

case when t2.prodid = 4 then t2.counts
else 0 end prod_D, 

case when t2.prodid = "5" then t2.counts
else 0 end prod_E

from 
(SELECT partners.name as name, sales.products_id as prodid, count(products.name) as counts
FROM test.sales left outer join test.partners on sales.partners_id = partners.id
left outer join test.products on sales.products_id = products.id 
where sales.partners_id = partners.id and sales.products_id = products.id group by partners.name, prodid) t2) t3

group by t3.name ;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.