행에서 두 개 이상의 열이 특정 값을 초과하는 위치를 계산합니다 (농구, 더블 더블, 트리플 더블)


20

나는 통계를 데이터베이스 파일로 출력 할 수있는 농구 경기를하고 있으므로 게임에서 구현되지 않은 통계를 계산할 수 있습니다. 지금까지 내가 원하는 통계를 계산하는 데 아무런 문제가 없었지만 이제는 게임 통계에서 시즌 동안 플레이어가 만든 더블 더블 및 / 또는 트리플 더블의 수를 세는 문제가 발생했습니다.

더블 더블과 트리플 더블의 정의는 다음과 같습니다.

더블 더블 :

더블 더블 (double-double)은 플레이어가 게임에서 5 가지 통계 범주 (포인트, 리바운드, 어시스트, 훔치기 및 막힌 샷) 중 두 개의 총 두 자리 숫자를 누적하는 성능으로 정의됩니다.

트리플 더블 :

트리플 더블은 플레이어가 게임에서 5 가지 통계 범주 (포인트, 리바운드, 어시스트, 훔치기 및 막힌 샷) 중 3 개의 총 두 자리 숫자를 누적하는 성능으로 정의됩니다.

쿼드 러플-더블 (설명을 위해 추가됨)

쿼드 러플 더블은 플레이어가 게임에서 5 가지 통계 범주 (포인트, 리바운드, 어시스트, 훔치기 및 막힌 샷) 중 4 개의 통계 범주에서 총 두 자리 숫자를 누적하는 성능으로 정의됩니다.

"PlayerGameStats"테이블은 플레이어가 플레이하는 각 게임에 대한 통계를 저장하며 다음과 같습니다.

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

달성하려는 출력은 다음과 같습니다.

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

내가 지금까지 찾은 유일한 해결책은 너무 끔찍해서 나를 놀리 게 만드는 것입니다 ...; o) ... 그것은 다음과 같습니다 :

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

... 그리고 지금 당신은 아마 이것을 읽은 후 펑킹하거나 웃고있을 것입니다. 나는 모든 이중 이중 조합을 얻는 데 필요한 모든 것을 작성하지 않았으며 트리플 더블에 대한 사례 진술은 더 어리석기 때문에 생략했습니다.

더 좋은 방법이 있습니까? 내가 가지고있는 테이블 구조 또는 새로운 테이블 구조로 (테이블을 변환하는 스크립트를 작성할 수 있습니다).

MySQL 5.5 또는 PostgreSQL 9.2를 사용할 수 있습니다.

여기에 예제 데이터와 위에 게시 한 끔찍한 솔루션이있는 SqlFiddle에 대한 링크가 있습니다 : http://sqlfiddle.com/#!2/af6101/3

내가 아는 한 게임에서 발생하지 않기 때문에 쿼드 러플 (위 참조)에 관심이 없지만 계정에 다시 쓰지 않고 쿼리를 쉽게 확장 할 수 있다면 더할 나위가 있습니다. 쿼드 러플 용.

답변:


10

이것이 최선의 방법인지 모릅니다. 먼저 통계가 두 자리 숫자인지 확인하고 1이면 할당합니다. 게임당 총 두 자릿수를 찾기 위해 모든 것을 합산했습니다. 거기에서 모든 복식과 트리플을 요약하십시오. 작동하는 것 같습니다

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

안녕하세요, 당신이 해결책 주셔서 감사합니다. 난 정말 좋아. 내가 원하는 것을 정확하게하고 많은 쓰기없이 Quadruple-double 및 Quintuple-doubles를 포함하도록 쉽게 확장 할 수 있습니다. 현재로서는 이것을 정답으로 삼을 것입니다. :)
keth

나는 당신의 코드를 좋아하지만 더 짧게 해킹 할 수 있습니다. CASE부울 표현식은 true이면 1, false이면 0으로 평가되므로 명령문 을 사용할 필요가 없습니다 . 주석에 완전한 SQL 코드 블록을 게시 할 수 없으므로 아래 답변에 추가했습니다.
Joshua Huber

여호수아 감사합니다. 그것을 완전히 간과하고 훨씬 좋아 보입니다.
SQLChao

1
@JoshuaHuber 맞지만 쿼리는 MySQL에서만 작동합니다. 사용 CASE하고하는 것은 SUM/COUNT물론 포스트 그레스에서 작업을 할 수 있습니다.
ypercubeᵀᴹ

@ ypercube : 실제로 부울을 추가하면 Postgres에서도 작동합니다. 명시 적으로 만 캐스팅하면됩니다. 그러나 CASE일반적으로 조금 더 빠릅니다. 몇 가지 사소한 개선으로 데모를 추가했습니다.
Erwin Brandstetter

7

이것을 사용해보십시오 (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

또는 JChao의 코드를 그의 답변에서 끔찍하게 제거하지만 CASE부울 expr이 {True, False} 일 때 {1,0}으로 평가되므로 불필요한 구문을 제거함으로써 더 짧아집니다 .

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

위의 코드는 부울 + 부울을 좋아하지 않기 때문에 PostgreSQL에서 실행되지 않는다는 의견을 기반으로합니다. 나는 여전히을 좋아하지 않는다 CASE. PostgreSQL (9.3)에서 다음과 같이 캐스팅하여 나가는 방법이 있습니다 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, 좋은 지적 및 감사합니다. 위의 질문에 대한 의견으로 정확한 설명을 요청했습니다. 의미론. 하키의 4 골 목표는 여전히 "해트 트릭 풀링"으로 여겨지지만 볼링에서 4 번의 연속 스트라이크는 "터키"로 간주되지 않고 오히려 "쿼드"라고 생각할 수 있습니다. 나는 각 게임의 의미에 대해 전문가가 아닙니다. 결정을 내리고 적합 =하거나 선택하십시오 >=.
조슈아 후버

솔루션 주셔서 감사합니다. 내가 원하는 것을 확실하게합니다. 또한 제공 한 JChao의 단기 버전과 같습니다.
케스

1
PostgreSQL에서는 부울을 추가해도 작동하지 않습니다.
Craig Ringer

@CraigRinger-지적 해 주셔서 감사합니다. 일반적으로 SQL과 특히 PostgreSQl에 관해서는 여전히 귀에 들리지 않기 때문에 이것은 나에게 중요한 정보입니다. :)
keth

1
@CraigRinger 훌륭하지만 MySQL이 지원한다고 생각하지 않습니다 CAST(... AS int) ( stackoverflow.com/questions/12126991/… ). CAST(... AS UNSIGNED)이 쿼리에서 작동하는 MySQL은 할 수 있지만 PostgreSQL은 할 수 없습니다. CAST이식성을 위해 둘 다 할 수 있는 공통점 이 있는지 확실하지 않습니다 . CASE이식성이 가장 중요한 경우 최악의 CASE 가 결국 고착 될 수 있습니다 .
Joshua Huber

6

여기에 또 다른 문제가 있습니다.

내가 생각하는 방식으로, 당신은 본질적으로 현재 문제에 대한 피벗 된 데이터로 작업하고 있으므로 가장 먼저해야 할 일은 피벗 해제입니다. 불행히도 PostgreSQL은 훌륭한 도구를 제공하지 않으므로 PL / PgSQL에서 동적 SQL 생성에 들어 가지 않으면 최소한 다음과 같은 작업을 수행 할 수 있습니다.

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

이것은 확실하지 않지만 데이터를보다 가단성있는 형태로 만듭니다. 여기서 (player_id, seasonday)는 플레이어를 고유하게 식별하기에 충분하다고 가정합니다. 즉, 플레이어 ID는 팀 전체에서 고유합니다. 그렇지 않은 경우 고유 키를 제공하기에 충분한 다른 정보를 포함해야합니다.

피벗되지 않은 데이터를 사용하면 다음과 같은 유용한 방법으로 데이터를 필터링하고 집계 할 수 있습니다.

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;

이것은 꽤 멀지 않으며 아마도 그렇게 빠르지 않을 것입니다. 그러나 유지 관리가 가능하므로 새로운 유형의 통계, 새 열 등을 처리하기 위해 최소한의 변경이 필요합니다.

그래서 그것은 진지한 제안보다 "이봐, 생각해 봤니"에 가깝습니다. 목표는 SQL을 신속하게 작성하지 않고 가능한 한 직접 문제점 설명에 대응하도록 SQL을 모델링하는 것입니다.


MySQL 지향 SQL에서 제곱 된 다중 값 삽입과 ANSI 인용을 사용하면 훨씬 쉬워졌습니다. 고맙습니다; 백틱을 한 번 보지 않는 것이 좋습니다. 내가 바꿔야 할 것은 합성 키 생성이었습니다.


이것은 내가 생각했던 것의 일종입니다.
Colin 't Hart

1
이 솔루션을 게시 해 주셔서 감사합니다. @ Colin'tHart가 위에서 제안한 것처럼 이와 같은 것을 구현하는 데 문제가있었습니다 (이전에 그런 일은 한 적이 없지만 앞으로 caluclate하고 싶은 다른 통계에는 거의 유용한 것으로 보입니다). 원하는 결과를 얻는 방법이 얼마나 흥미 롭습니다. 오늘은 많은 것을 배웠습니다.
keth

1
더 알아 보려면, explain analyze쿼리 계획 (또는 MySQL의 상당) 모두가 할 무엇을 어떻게 :)와 그림
Craig Ringer

@CraigRinger-감사합니다. 좋은 조언. 실제로 지금까지 제공된 모든 솔루션을 사용하여 그렇게했습니다 (SqlFiddles의 "실행 계획보기"). 그러나 나는 출력을 읽을 때 "모두가하는 일과 방법을 파악하는"부분을 정의해야한다. = O
keth

6

무엇 MySQL을위한 @Joshua이 표시 ,뿐만 아니라 포스트 그레스에서 작동합니다. Boolean값을 캐스트 integer하고 합산 할 수 있습니다 . 그러나 캐스트는 명시 적이어야합니다. 매우 짧은 코드를 만듭니다.

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;

그러나 CASE더 장황하지만 일반적으로 조금 더 빠릅니다. 그리고 더 휴대하기 편리하다면 :

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;

SQL 바이올린.


1
TIL : MySQL과 마찬가지로 PostgreSQL 은 별명 또는 서수로 GROUP BY의 열을 참조 할 수 있습니다.
Andriy M

2

정수 나누기와 이진 캐스트 사용

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

우연히 발견 한 @Craig Ringers 버전의 변형을 남기고 싶을 수도 있습니다. 미래에 누군가에게 유용 할 수도 있습니다.

여러 UNION ALL 대신 unnest와 array를 사용합니다. 영감을주는 출처 : /programming/1128737/unpivot-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;

SQL 피들 : http://sqlfiddle.com/#!12/4980b/3

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.