Làm thế nào tôi có thể trả lại đầu ra bảng trụ trong MySQL?


312

Nếu tôi có một bảng MySQL trông giống như thế này:

công ty hành động pagecount
-------------------------------
Công ty A IN 3
Công ty A IN 2
Công ty A IN 3
Công ty B EMAIL   
Công ty B IN 2
Công ty B IN 2
Công ty B IN 1
Công ty A IN 3

Có thể chạy truy vấn MySQL để nhận đầu ra như thế này:

company_name EMAIL IN 1 trang IN 2 trang IN 3 trang
-------------------------------------------------- -----------
Công ty 0 0 1 3
Công tyB 1 1 2 0

Ý tưởng là pagecountcó thể thay đổi để số lượng cột đầu ra sẽ phản ánh điều đó, một cột cho mỗi action/ pagecountcặp và sau đó số lần truy cập mỗi company_name. Tôi không chắc chắn nếu điều này được gọi là bảng xoay vòng nhưng ai đó đã đề xuất điều đó?


3
Nó được gọi là xoay vòng và nhanh hơn nhiều để thực hiện chuyển đổi này bên ngoài SQL.
NB

1
Excel vượt qua những thứ như thế này, thật sự khó khăn trong MySQL vì không có toán tử "CROSSTAB" :(
Dave Rix

Có, hiện tại nó được thực hiện bằng tay trong Excel và chúng tôi đang cố gắng tự động hóa nó.
peku

3
Ở đây tôi tìm thấy ví dụ từng bước: làm thế nào để tự động hóa các bảng trụ . và điều này
Devid G

1
@giannischristofakis - nó thực sự phụ thuộc vào những gì bạn và đồng nghiệp của bạn cho là đơn giản hơn. Công nghệ bắt kịp khá nhiều kể từ khi tôi đăng bình luận (4 năm) vì vậy nó hoàn toàn phụ thuộc vào những gì bạn cảm thấy tốt hơn - có thể là trong ứng dụng hoặc SQL. Ví dụ: trong công việc của tôi, chúng tôi xử lý vấn đề tương tự nhưng chúng tôi đang kết hợp cả phương pháp tiếp cận SQL và trong ứng dụng. Về cơ bản, tôi không thể giúp bạn ngoài việc đưa ra câu trả lời có ý kiến ​​và đó không phải là điều bạn cần :)
NB

Câu trả lời:


236

Điều này về cơ bản một bảng trụ.

Một hướng dẫn tốt đẹp về cách đạt được điều này có thể được tìm thấy ở đây: http://www.artfulsoftware.com/infotree/qrytip.php?id=78

Tôi khuyên bạn nên đọc bài viết này và điều chỉnh giải pháp này cho nhu cầu của bạn.

Cập nhật

Sau khi liên kết ở trên hiện không còn khả dụng nữa, tôi cảm thấy bắt buộc phải cung cấp một số thông tin bổ sung cho tất cả các bạn đang tìm kiếm câu trả lời xoay vòng của mysql tại đây. Nó thực sự có một lượng thông tin khổng lồ và tôi sẽ không đưa mọi thứ từ đây vào đây (thậm chí nhiều hơn vì tôi không muốn sao chép kiến ​​thức rộng lớn của họ), nhưng tôi sẽ đưa ra một số lời khuyên về cách đối phó với trục đặt bảng theo cách sql nói chung với ví dụ từ peku, người đã đặt câu hỏi ở vị trí đầu tiên.

Có lẽ liên kết quay lại sớm, tôi sẽ để mắt đến nó.

Cách bảng tính ...

Nhiều người chỉ sử dụng một công cụ như MSExcel, OpenOffice hoặc các công cụ bảng tính khác cho mục đích này. Đây là một giải pháp hợp lệ, chỉ cần sao chép dữ liệu ở đó và sử dụng các công cụ mà GUI cung cấp để giải quyết vấn đề này.

Nhưng ... đây không phải là câu hỏi và thậm chí nó có thể dẫn đến một số nhược điểm, như làm thế nào để đưa dữ liệu vào bảng tính, mở rộng có vấn đề, v.v.

Cách SQL ...

Cho bảng của anh ấy trông giống như thế này:

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;

Bây giờ hãy nhìn vào bảng mong muốn của anh ấy / cô ấy:

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

Các hàng ( EMAIL, PRINT x pages) giống với điều kiện. Nhóm chính là bởi company_name.

Để thiết lập các điều kiện, điều này thay vì sử dụng CASE-statement. Để nhóm bởi một cái gì đó, tốt, sử dụng ... GROUP BY.

SQL cơ bản cung cấp trục này có thể trông giống như thế này:

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`;

Điều này sẽ cung cấp kết quả mong muốn rất nhanh. Nhược điểm chính của cách tiếp cận này, càng nhiều hàng bạn muốn trong bảng trụ của bạn, bạn càng cần xác định nhiều điều kiện hơn trong câu lệnh SQL.

Điều này cũng có thể được xử lý, do đó mọi người có xu hướng sử dụng các tuyên bố, thói quen, quầy và chuẩn bị.

Một số liên kết bổ sung về chủ đề này:


4
liên kết dường như hoạt động ngay bây giờ ... nếu nó bị hỏng lần nữa, hãy thử những điều sau: bộ nhớ cache của webcache.googleusercontent.com/ , hoặc máy Wayback Internet ( web.archive.org/web/20070303120558 * / artfulsoftware.com/ infotree / query.php )
Lykegenes

liên kết có thể truy cập tại url artfulsoftware.com/infotree/qrytip.php?id=78
MrPandav

1
Có một cách khác để tạo bảng xoay vòng mà không cần sử dụng "if", "case" hoặc " GROUP_CONCAT
user2513149

Bạn có thể xóa ELSE NULL khỏi CASE của mình vì mũ là hành vi mặc định (và tập hợp có điều kiện là đủ dài)
Caius Jard

86

Giải pháp của tôi là trong T-SQL mà không có bất kỳ pivots nào:

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
Điều này làm việc cho tôi ngay cả trên PostgreSQL. Tôi thích phương pháp này hơn là sử dụng tiện ích mở rộng chéo trên Postgres vì ​​nó sạch hơn
vào

2
"Giải pháp của tôi là trong T-SQL mà không có bất kỳ trục trặc nào:" Không chỉ SQL Server, nó sẽ hoạt động trên hầu hết các nhà cung cấp cơ sở dữ liệu tuân theo các tiêu chuẩn SQL ANSI. Lưu ý rằng SUM()chỉ có thể hoạt động với dữ liệu số nếu bạn đã sử dụng chuỗi trục mà bạn sẽ phải sử dụngMAX()
Raymond Nijland

1
Tôi nghĩ rằng CASE là không cần thiết SUM(CASE WHEN (action='PRINT' AND pagecount=1) THEN 1 ELSE 0 END), bạn chỉ có thể làm SUM(action='PRINT' AND pagecount=1)vì điều kiện sẽ được chuyển đổi thành 1khi đúng và 0khi sai
kajacx

1
@kajacx có, mặc dù nó cần trên cơ sở dữ liệu không có loại thao tác Boolean đó. Đưa ra lựa chọn giữa "cú pháp dài hơn hoạt động trên tất cả dB" và "cú pháp ngắn hơn chỉ hoạt động trên ..." Tôi sẽ chọn cái trước
Caius Jard

66

Đối với MySQL, bạn có thể trực tiếp đưa các điều kiện vào SUM()chức năng và nó sẽ được đánh giá là Boolean 0hoặc1 do đó bạn có thể có số lượng dựa trên tiêu chí của mình mà không cần sử dụng IF/CASEcâu lệnh

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
Đó là một trong những thực sự gọn gàng. Bạn có biết đây có phải là tiêu chuẩn tuân thủ trên các nền tảng khác (như Postgres) không?
itols

3
@itsols Không chỉ dành riêng cho Mysql
M Khalid Junaid

@itsols: Tôi đã thêm một phiên bản SQL tiêu chuẩn khác . Postgres cũng có chức năng chuyên dụng crosstab().
Erwin Brandstetter

2
Cũng hoạt động cho SQLite
SBF

37

Đối với trục động, sử dụng GROUP_CONCATvới CONCAT. Hàm GROUP_CONCAT ghép các chuỗi từ một nhóm thành một chuỗi với nhiều tùy chọn khác nhau.

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;

DEMO TẠI ĐÂY


2
Pacerier, người đàn ông đích thực nhưng vì năng động xoay quanh một trong những cách tiếp cận tốt nhất
Abhishek Gupta

2
Điều này hoạt động tốt nếu bạn có nhiều giá trị trong cột "hành động" hoặc mong muốn danh sách đó tăng theo thời gian, vì việc viết một tuyên bố trường hợp cho mỗi giá trị có thể tốn thời gian và khó cập nhật.
Patrick Murphy

23

Một standard-SQL phiên bản sử dụng logic boolean :

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;

Câu đố SQL.

Làm sao?

TRUE OR NULL sản lượng TRUE.
FALSE OR NULLsản lượng NULL.
NULL OR NULLsản lượng NULL.
COUNTchỉ tính các giá trị khác không. Voilá.


@Erwin, nhưng làm sao bạn biết rằng có ba cột? Nếu có 5 thì sao? 10? 20?
Pacerier

@Pacerier: Ví dụ trong câu hỏi dường như gợi ý điều đó. Dù bằng cách nào, SQL yêu cầu phải biết kiểu trả về. một truy vấn hoàn toàn năng động là không thể. Nếu số lượng cột đầu ra có thể thay đổi, bạn cần hai bước: Thứ nhất xây dựng truy vấn, thứ 2: thực hiện nó.
Erwin Brandstetter

11

Câu trả lời đúng là:

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
Đây chỉ là một ví dụ bạn có trong tay? Cấu trúc của other_valuebảng là gì?
Patrick Murphy

1
"Câu trả lời đúng là:" Rất có thể không phải vì nó thiếu SETtruy vấn để tăng giá trị xác thực bị giới hạn ở 1024 cho GROUP_CONCAT sau 1024 GROUP_CONCAT chỉ cần cắt ngắn chuỗi mà không có lỗi có nghĩa là kết quả không mong muốn có thể xảy ra ..
Raymond Nijland

Xin lỗi các bạn không thể nhớ thêm chi tiết. Tôi làm công cụ cho vui và sau đó quên hoặc phá hủy toàn bộ dự án. Nhưng khi tôi vấp phải một thử thách, tôi chia sẻ cách tôi sửa nó. Tôi biết ví dụ của tôi không chi tiết lắm nhưng tôi đoán nó có thể đưa ra hướng dẫn cho những người biết họ đang chống lại điều gì :)
Talha

9

Có một công cụ gọi là trình tạo bảng MySQL Pivot, nó có thể giúp bạn tạo bảng xoay vòng dựa trên web mà sau này bạn có thể xuất ra excel (nếu bạn muốn). nó có thể hoạt động nếu dữ liệu của bạn nằm trong một bảng hoặc trong một vài bảng.

Tất cả những gì bạn cần làm là chỉ định nguồn dữ liệu của các cột (nó hỗ trợ các cột động), các hàng, các giá trị trong phần thân của bảng và mối quan hệ bảng (nếu có) Bảng Pivot của MySQL

Trang chủ của công cụ này là 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 ;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.