Đếm nơi hai hoặc nhiều cột trong một hàng vượt quá một giá trị nhất định [bóng rổ, gấp đôi, gấp đôi]


20

Tôi chơi một trò chơi bóng rổ cho phép xuất số liệu thống kê của nó dưới dạng tệp cơ sở dữ liệu, vì vậy người ta có thể tính toán số liệu thống kê từ trò chơi không được triển khai trong trò chơi. Cho đến nay tôi không gặp vấn đề gì trong việc xác định số liệu thống kê mà tôi muốn, nhưng bây giờ tôi gặp phải một vấn đề: đếm số lần nhân đôi và / hoặc nhân ba số người chơi được thực hiện trong mùa giải từ số liệu thống kê trò chơi của anh ta.

Định nghĩa của một nhân đôi và một nhân đôi như sau:

Gấp đôi:

Nhân đôi được định nghĩa là hiệu suất trong đó người chơi tích lũy tổng số có hai chữ số trong hai trong số năm loại thống kê Điểm điểm, rebound, hỗ trợ, đánh cắp và chặn các cú sút trong trò chơi.

Nhân đôi:

Bộ ba nhân đôi được định nghĩa là hiệu suất trong đó người chơi tích lũy tổng số hai chữ số trong ba trong số năm loại thống kê Điểm điểm, rebound, hỗ trợ, đánh cắp và bắn bị chặn trong trò chơi.

Tăng gấp đôi ( gấp đôi để làm rõ)

Nhân đôi gấp đôi được định nghĩa là hiệu suất trong đó người chơi tích lũy tổng số hai chữ số trong bốn trong năm loại thống kê Điểm điểm, rebound, hỗ trợ, đánh cắp và bắn chặn trong một trò chơi.

Bảng "PlayerGameStats" lưu trữ số liệu thống kê cho mỗi trò chơi mà người chơi chơi và xem như sau:

CREATE TABLE PlayerGameStats AS SELECT * FROM ( VALUES
  ( 1, 1,  1, 'Nuggets',    'Cavaliers',  6,  8,  2, 2,  0 ),
  ( 2, 1,  2, 'Nuggets',     'Clippers', 15,  7,  0, 1,  3 ),
  ( 3, 1,  6, 'Nuggets', 'Trailblazers', 11, 11,  1, 2,  1 ),
  ( 4, 1, 10, 'Nuggets',    'Mavericks',  8, 10,  2, 2, 12 ),
  ( 5, 1, 11, 'Nuggets',       'Knicks', 23, 12,  1, 0,  0 ),
  ( 6, 1, 12, 'Nuggets',         'Jazz',  8,  8, 11, 1,  0 ),
  ( 7, 1, 13, 'Nuggets',         'Suns',  7, 11,  2, 2,  1 ),
  ( 8, 1, 14, 'Nuggets',        'Kings', 10, 15,  0, 3,  1 ),
  ( 9, 1, 15, 'Nuggets',        'Kings',  9,  7,  5, 0,  4 ),
  (10, 1, 17, 'Nuggets',      'Thunder', 13, 10, 10, 1,  0 )
) AS t(id,player_id,seasonday,team,opponent,points,rebounds,assists,steals,blocks);

Đầu ra tôi muốn đạt được trông như thế này:

| player_id |    team | doubleDoubles | tripleDoubles |
|-----------|---------|---------------|---------------|
|         1 | Nuggets |             4 |             1 |

Giải pháp duy nhất tôi tìm thấy cho đến nay là quá khủng khiếp, nó khiến tôi phát điên ...; o) ... Có vẻ như thế này:

SELECT 
  player_id,
  team,
  SUM(CASE WHEN(points >= 10 AND rebounds >= 10) OR
               (points >= 10 AND assists  >= 10) OR
               (points >= 10 AND steals   >= 10) 
                THEN 1 
                ELSE 0 
      END) AS doubleDoubles
FROM PlayerGameStats
GROUP BY player_id

... và bây giờ có lẽ bạn cũng đang cười (hoặc cười rất nhiều) sau khi đọc nó. Tôi thậm chí đã không viết ra tất cả mọi thứ cần thiết để có được tất cả các kết hợp kép, và bỏ qua tuyên bố trường hợp cho bộ ba nhân đôi vì nó thậm chí còn kỳ cục hơn.

Có cách nào tốt hơn để làm điều này? Với cấu trúc bảng tôi có hoặc với cấu trúc bảng mới (tôi có thể viết một tập lệnh để chuyển đổi bảng).

Tôi có thể sử dụng MySQL 5.5 hoặc PostgreQuery 9.2.

Đây là một liên kết đến SqlFiddle với dữ liệu mẫu và giải pháp khủng khiếp của tôi, tôi đã đăng ở trên: http://sqlfiddle.com/#!2/af6101/3

Lưu ý rằng tôi không thực sự quan tâm đến việc nhân đôi (xem ở trên) vì chúng không xảy ra trong trò chơi mà tôi biết, nhưng sẽ là một điểm cộng nếu truy vấn có thể dễ dàng mở rộng mà không cần viết lại nhiều vào tài khoản cho tăng gấp bốn lần.

Câu trả lời:


10

Không biết đây có phải là cách tốt nhất không. Trước tiên tôi đã chọn để tìm hiểu xem một stat có hai chữ số không và gán cho nó 1 nếu có. Tổng hợp tất cả những thứ đó để tìm ra tổng số chữ số đôi cho mỗi trò chơi. Từ đó chỉ cần tổng hợp tất cả các đôi và ba. Có vẻ như để làm việc

select a.player_id, 
a.team, 
sum(case when a.doubles = 2 then 1 else 0 end) as doubleDoubles, 
sum(case when a.doubles = 3 then 1 else 0 end) as tripleDoubles
from
(select *, 
(case when points > 9 then 1 else 0 end) +
(case when rebounds > 9 then 1 else 0 end) +
(case when assists > 9 then 1 else 0 end) +
(case when steals > 9 then 1 else 0 end) +
(case when blocks > 9 then 1 else 0  end) as Doubles
from PlayerGameStats) a
group by a.player_id, a.team

Xin chào, cảm ơn bạn đã giải pháp. Tôi thực sự thích nó. Có chính xác những gì tôi muốn và có thể dễ dàng mở rộng để bao gồm Quadruple-double và Quintuple-double mà không cần viết nhiều. Sẽ làm cho điều này câu trả lời được chấp nhận cho bây giờ. :)
vào

Tôi thích mã của bạn, nhưng bạn có thể hack nó thậm chí còn ngắn hơn. Không cần sử dụng các CASEcâu lệnh vì các biểu thức boolean đánh giá là 1 khi đúng và 0 khi sai. Tôi đã thêm nó vào câu trả lời của mình bên dưới và hét to với bạn vì không thể đăng toàn bộ khối mã SQL trong bình luận ở đây.
Joshua Huber

Cảm ơn Joshua. Hoàn toàn bỏ qua điều đó và nó có vẻ tốt hơn nhiều.
SQLChao

1
@JoshuaHuber Đúng nhưng sau đó truy vấn sẽ chỉ hoạt động trong MySQL. Sử dụng CASESUM/COUNTcho phép nó hoạt động trên Postgres.
ypercubeᵀᴹ

@ypercube: Trên thực tế, việc thêm booleans cũng hoạt động trong Postgres. Bạn chỉ cần đúc một cách rõ ràng. Nhưng CASEthường là nhanh hơn một chút. Tôi đã thêm một bản demo với một vài cải tiến nhỏ khác.
Erwin Brandstetter

7

Hãy thử điều này (làm việc cho tôi trên MySQL 5.5):

SELECT 
  player_id,
  team,
  SUM(
    (   (points   >= 10)
      + (rebounds >= 10)
      + (assists  >= 10)
      + (steals   >= 10)
      + (blocks   >= 10) 
    ) = 2
  ) double_doubles,
  SUM(
    (   (points   >= 10)
      + (rebounds >= 10)
      + (assists  >= 10)
      + (steals   >= 10)
      + (blocks   >= 10) 
    ) = 3
  ) triple_doubles
FROM PlayerGameStats
GROUP BY player_id, team

Hoặc thậm chí ngắn hơn, bằng cách trích xuất mã của JChao một cách rõ ràng từ câu trả lời của anh ta, nhưng lấy ra các CASEcâu lệnh không cần thiết vì expr boolean đánh giá là {1,0} khi {Đúng, Sai}:

select a.player_id, 
a.team, 
sum(a.doubles = 2) as doubleDoubles, 
sum(a.doubles = 3) as tripleDoubles
from
(select *, 
(points > 9) +
(rebounds > 9) +
(assists > 9) +
(steals > 9) +
(blocks > 9) as Doubles
from PlayerGameStats) a
group by a.player_id, a.team

Dựa trên các nhận xét rằng đoạn mã trên sẽ không chạy trong PostgreSQL vì không muốn làm boolean + boolean. Tôi vẫn không thích CASE. Đây là một lối thoát trên PostgreSQL (9.3), bằng cách chuyển sang int:

select a.player_id, 
a.team, 
sum((a.doubles = 2)::int) as doubleDoubles, 
sum((a.doubles = 3)::int) as tripleDoubles
from
(select *, 
(points > 9)::int +
(rebounds > 9)::int +
(assists > 9)::int +
(steals > 9)::int +
(blocks > 9)::int as Doubles
from PlayerGameStats) a
group by a.player_id, a.team

@ypercube, điểm tốt & cảm ơn. Đã chỉ yêu cầu làm rõ chính xác như nhận xét về câu hỏi trên. Ngữ nghĩa. Tôi tin rằng bốn mục tiêu trong khúc côn cầu vẫn được coi là "kéo theo một cú hat-trick", nhưng bốn lần tấn công liên tiếp trong bowling có thể không được coi là "gà tây" thích hợp, thay vào đó là "quad". Tôi không có chuyên gia về ngữ nghĩa của mỗi trò chơi. Bạn đưa ra quyết định và lựa chọn =hoặc >=là phù hợp.
Joshua Huber

Cảm ơn giải pháp của bạn. Chắc chắn làm những gì tôi muốn. Cũng giống như phiên bản rút gọn từ JChao bạn cung cấp.
keth

1
Thêm booleans sẽ không hoạt động trong PostgreSQL, hãy ghi nhớ điều đó.
Craig Ringer

@CraigRinger - cảm ơn bạn đã chỉ ra điều đó. Vì tôi vẫn còn xanh sau tai khi nói về SQL nói chung và PostgreSQl nói riêng, đây là thông tin có giá trị đối với tôi. :)
keth

1
@CraigRinger Đẹp, nhưng tôi không nghĩ MySQL hỗ trợ CAST(... AS int) ( stackoverflow.com/questions/12126991/ mẹo ). MySQL có thể làm CAST(... AS UNSIGNED), hoạt động trong truy vấn này, nhưng PostgreSQL thì không. Không chắc chắn có một điểm chung CASTmà cả hai có thể làm cho tính di động. CASE tồi tệ nhất, CASEcuối cùng có thể bị mắc kẹt nếu tính di động là tối quan trọng.
Joshua Huber

6

Đây là một vấn đề khác.

Theo cách tôi nghĩ, về cơ bản, bạn đang làm việc với dữ liệu xoay vòng cho vấn đề hiện tại, vì vậy điều đầu tiên cần làm là không xoay vòng nó. Thật không may, PostgreSQL không cung cấp các công cụ tuyệt vời để làm điều đó, vì vậy mà không cần phải tạo ra SQL động trong PL / PGQuery, ít nhất chúng ta có thể làm:

SELECT player_id, seasonday, 'points' AS scoretype, points AS score FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'rebounds' AS scoretype, rebounds FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'assists' AS scoretype, assists FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'steals' AS scoretype, steals FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'blocks' AS scoretype, blocks FROM playergamestats

Điều này đặt dữ liệu ở dạng dễ uốn hơn, mặc dù nó chắc chắn không đẹp. Ở đây tôi giả sử rằng (player_id, seaonday) là đủ để xác định duy nhất người chơi, tức là ID người chơi là duy nhất giữa các đội. Nếu không, bạn sẽ cần bao gồm đủ thông tin khác để cung cấp một khóa duy nhất.

Với dữ liệu không được xoay vòng đó, giờ đây có thể lọc và tổng hợp dữ liệu theo những cách hữu ích, như:

SELECT
  player_id,
  count(CASE WHEN doubles = 2 THEN 1 END) AS doubledoubles,
  count(CASE WHEN doubles = 3 THEN 1 END) AS tripledoubles
FROM (
    SELECT
      player_id, seasonday, count(*) AS doubles
    FROM
    (
        SELECT player_id, seasonday, 'points' AS scoretype, points AS score FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'rebounds' AS scoretype, rebounds FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'assists' AS scoretype, assists FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'steals' AS scoretype, steals FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'blocks' AS scoretype, blocks FROM playergamestats
    ) stats
    WHERE score >= 10
    GROUP BY player_id, seasonday
) doublestats
GROUP BY player_id;

Điều này là xa đẹp, và nó có thể không nhanh như vậy. Mặc dù vậy, nó vẫn có thể duy trì, yêu cầu thay đổi tối thiểu để xử lý các loại thống kê mới, cột mới, v.v.

Vì vậy, nó giống như một "hey, bạn đã nghĩ đến" hơn là một đề nghị nghiêm túc. Mục tiêu là mô hình hóa SQL để tương ứng với câu lệnh vấn đề càng trực tiếp càng tốt, thay vì làm cho nó nhanh.


Điều này đã được thực hiện dễ dàng hơn rất nhiều bằng cách bạn sử dụng các phần chèn đa giá trị và trích dẫn ANSI trong SQL định hướng MySQL của bạn. Cảm ơn bạn; thật tuyệt khi không thấy backticks một lần. Tất cả tôi phải thay đổi là thế hệ khóa tổng hợp.


Đây là loại những gì tôi có trong tâm trí.
Colin 't Hart

1
Cảm ơn cho bài viết giải pháp này. Có phải các vấn đề của tôi đã thực hiện một cái gì đó như thế này như @ Colin'tHart đã đề xuất ở trên (chưa bao giờ làm điều gì đó tương tự trước đây, nhưng dường như rất hữu ích cho một số thống kê khác mà tôi có thể muốn tính toán trong tương lai). Thật thú vị khi có bao nhiêu cách để thực hiện đầu ra mong muốn của tôi. Chắc chắn đã học được rất nhiều ngày hôm nay.
vào

1
Để tìm hiểu thêm, explain analyzecác kế hoạch truy vấn (hoặc tương đương với MySQL) và tìm hiểu tất cả những gì họ làm và làm thế nào :)
Craig Ringer

@CraigRinger - Cảm ơn. Lời khuyên tốt. Trên thực tế, loại đã làm điều đó với tất cả các giải pháp được cung cấp cho đến bây giờ (Tôi đã sử dụng "xem kế hoạch thực hiện" của SqlFiddles). Nhưng tôi chắc chắn cần phải làm việc trên phần "tìm ra tất cả những gì họ làm và làm thế nào" khi đọc đầu ra. = O
keth

6

Những gì @Joshua hiển thị cho MySQL , cũng hoạt động trong Postgres. Booleancác giá trị có thể được truyền tới integervà thêm vào. Các diễn viên cần phải rõ ràng, mặc dù. Làm cho mã rất ngắn:

SELECT player_id, team
     , count(doubles = 2 OR NULL) AS doubledoubles
     , count(doubles = 3 OR NULL) AS tripledoubles
FROM  (
   SELECT player_id, team,
          (points   > 9)::int +
          (rebounds > 9)::int +
          (assists  > 9)::int +
          (steals   > 9)::int +
          (blocks   > 9)::int AS doubles
   FROM playergamestats
   ) a
GROUP  BY 1, 2;

Tuy nhiên, CASE- mặc dù dài dòng hơn - thường nhanh hơn một chút. Và di động hơn, nếu đó là vấn đề:

SELECT player_id, team
     , count(doubles = 2 OR NULL) AS doubledoubles
     , count(doubles = 3 OR NULL) AS tripledoubles
FROM  (
   SELECT player_id, team,
          CASE WHEN points   > 9 THEN 1 ELSE 0 END +
          CASE WHEN rebounds > 9 THEN 1 ELSE 0 END +
          CASE WHEN assists  > 9 THEN 1 ELSE 0 END +
          CASE WHEN steals   > 9 THEN 1 ELSE 0 END +
          CASE WHEN blocks   > 9 THEN 1 ELSE 0 END AS doubles
   FROM playergamestats
   ) a
GROUP  BY 1, 2;

Câu đố SQL.



2

Sử dụng phép chia số nguyên và số nhị phân

SELECT player_id
     , team
     , SUM(CASE WHEN Doubles = 2 THEN 1 ELSE 0 END) DoubleDouble
     , SUM(CASE WHEN Doubles = 3 THEN 1 ELSE 0 END) TripleDouble
FROM   (SELECT player_id
             , team
             , (BINARY (points DIV 10) > 0)
             + (BINARY (rebounds DIV 10) > 0)
             + (BINARY (assists DIV 10) > 0)
             + (BINARY (steals DIV 10) > 0)
             + (BINARY (blocks DIV 10) > 0)
             AS Doubles
        FROM   PlayerGameStats) d
GROUP BY player_id, team

1

Chỉ muốn để lại một biến thể của phiên bản @Craig Ringers ở đây tôi tình cờ tìm thấy, có thể nó hữu ích cho ai đó trong tương lai.

Thay vì nhiều UNION ALL, nó sử dụng không nhất quán và mảng. Nguồn cảm hứng: /programming/1128737/unpOLL-and-postgresql


SELECT
  player_id,
  count(CASE WHEN doubles = 2 THEN 1 END) AS doubledoubles,
  count(CASE WHEN doubles = 3 THEN 1 END) AS tripledoubles
FROM (
    SELECT
      player_id, seasonday, count(*) AS doubles
    FROM
    (
        SELECT 
          player_id, 
          seasonday,
          unnest(array['Points', 'Rebounds', 'Assists', 'Steals', 'Blocks']) AS scoretype,
          unnest(array[Points, Rebounds, Assists, Steals, Blocks]) AS score
        FROM PlayerGameStats
    ) stats
    WHERE score >= 10
    GROUP BY player_id, seasonday
) doublestats
GROUP BY player_id;

Câu đố về SQL: http://sqlfiddle.com/#!12/4980b/3

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.