นับว่าคอลัมน์สองแถวขึ้นไปในแถวหนึ่งมีค่าเกินกว่าค่าใด [บาสเกตบอลคู่คู่สามเท่า]


20

ฉันเล่นเกมบาสเก็ตบอลที่อนุญาตให้ส่งออกสถิติเป็นไฟล์ฐานข้อมูลดังนั้นจึงสามารถคำนวณสถิติจากเกมที่ไม่ได้นำมาใช้ในเกมได้ จนถึงตอนนี้ฉันก็ไม่มีปัญหาในการคำนวณสถิติที่ฉันต้องการ แต่ตอนนี้ฉันพบปัญหา: การนับจำนวน double double และ / หรือ triple doubles ที่ผู้เล่นทำในช่วงฤดูกาลจากสถิติของเกม

คำจำกัดความของ double double และ double double มีดังนี้:

สองครั้งสองครั้ง:

double-double ถูกกำหนดเป็นผลการทำงานที่ผู้เล่นสะสมตัวเลขสองหลักรวมในสองในห้าหมวดหมู่สถิติ - คะแนน, รีบาวน์, ช่วยเหลือ, ขโมยและช็อตที่ถูกบล็อก - ในเกม

สามคู่:

Triple-Double ถูกกำหนดให้เป็นการแสดงที่ผู้เล่นสะสมตัวเลขสองหลักรวมในสามในห้าหมวดหมู่สถิติ - คะแนน, รีบาวน์, ช่วยเหลือ, ขโมยและช็อตที่ถูกบล็อก - ในเกม

Quadruple-double (เพิ่มเพื่อความกระจ่าง)

quadruple-double ถูกกำหนดให้เป็นประสิทธิภาพที่ผู้เล่นสะสมตัวเลขสองหลักรวมในสี่ในห้าหมวดหมู่ทางสถิติ ได้แก่ คะแนนรีบาวน์การช่วยเหลือการขโมยและช็อตที่ถูกบล็อกในเกม

ตาราง "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

โปรดทราบว่าฉันไม่ได้สนใจ quadruple-doubles (ดูด้านบน) เนื่องจากพวกเขาไม่ได้เกิดขึ้นในเกมที่ฉันเล่นเท่าที่ฉันรู้ แต่มันจะเป็นข้อดีถ้าแบบสอบถามสามารถขยายได้อย่างง่ายดายโดยไม่ต้องเขียนบัญชีอีกมาก สำหรับสี่เท่าคู่

คำตอบ:


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คำสั่งเนื่องจากนิพจน์บูลีนประเมินเป็น 1 เมื่อเป็นจริงและ 0 เมื่อเป็นเท็จ ฉันได้เพิ่มไว้ในคำตอบของฉันด้านล่างโดยไม่ต้องตะโกนใส่คุณเนื่องจากไม่สามารถโพสต์บล็อกโค้ด SQL แบบเต็มในความคิดเห็นได้ที่นี่
Joshua Huber

ขอบคุณโจชัว มองข้ามไปโดยสิ้นเชิงและมันก็ดูดีขึ้นมาก
SQLChao

1
@JoshuaHuber ใช่แล้วแบบสอบถามจะทำงานใน MySQL เท่านั้น การใช้CASEและSUM/COUNTช่วยให้สามารถทำงานกับ Postgres ได้เช่นกัน
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 ประเมินเป็น {1,0} เมื่อ {True, False}:

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

@percube จุดดี & ขอบคุณ เพิ่งถามว่าการชี้แจงที่แน่นอนเป็นความคิดเห็นในคำถามข้างต้น อรรถศาสตร์ ฉันเชื่อว่าสี่เป้าหมายในฮ็อกกี้ยังถือว่าเป็น "การดึงหมวก" แต่การนัดหยุดงานติดต่อกันสี่ครั้งในโบว์ลิ่งอาจไม่ถือว่าเป็น "ไก่งวง" ที่เหมาะสม แต่เป็น "รูปสี่เหลี่ยม" ฉันไม่เชี่ยวชาญในความหมายของแต่ละเกม คุณตัดสินใจและเลือก=หรือ>=เหมาะสม
Joshua Huber

ขอบคุณสำหรับการแก้ปัญหาของคุณ ทำในสิ่งที่ฉันต้องการอย่างแน่นอน เช่นเดียวกับเวอร์ชั่นย่อจาก JChao ที่คุณให้ไว้
keth

1
แม้ว่าการเพิ่มบูลีนจะไม่สามารถใช้งานใน PostgreSQL ได้โปรดจำไว้
Craig Ringer

@ CraigRinger - ขอบคุณที่ชี้ให้เห็น เนื่องจากฉันยังคงเป็นสีเขียวหลังใบหูเมื่อพูดถึง SQL โดยทั่วไปและโดยเฉพาะอย่างยิ่ง PostgreSQl นี่เป็นข้อมูลที่มีค่าสำหรับฉัน :)
keth

1
@ CraigRinger ดี แต่ฉันไม่คิดว่า MySQL รองรับCAST(... AS int) ( stackoverflow.com/questions/12126991/… ) MySQL สามารถทำได้CAST(... AS UNSIGNED)ซึ่งทำงานในแบบสอบถามนี้ แต่ PostgreSQL ไม่สามารถทำได้ ไม่แน่ใจว่ามีอยู่ทั่วไปCASTที่ทั้งสองสามารถทำได้เพื่อการพกพา กรณีที่เลวร้ายที่สุดอาจติดอยู่CASEในที่สุดหากการพกพานั้นสำคัญยิ่ง
Joshua Huber

6

นี่เป็นอีกปัญหาหนึ่ง

วิธีที่ฉันคิดคือคุณกำลังทำงานกับข้อมูลที่หมุนเหวี่ยงสำหรับปัญหาปัจจุบันดังนั้นสิ่งแรกที่ต้องทำคือยกเลิกการหมุน น่าเสียดาย PostgreSQL ไม่มีเครื่องมือที่ดีในการทำเช่นนั้นดังนั้นโดยที่ไม่ต้องเข้าสู่การสร้าง SQL แบบไดนามิกใน PL / PgSQL อย่างน้อยเราก็สามารถทำได้

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) เพียงพอที่จะระบุผู้เล่นที่ไม่ซ้ำกันนั่นคือรหัสผู้เล่นจะไม่ซ้ำกันในทีม หากไม่เป็นเช่นนั้นคุณจะต้องรวมข้อมูลอื่น ๆ ให้เพียงพอเพื่อให้รหัสที่ไม่ซ้ำกัน

ด้วยข้อมูลที่ไม่มีการลงทะเบียนตอนนี้คุณสามารถกรองและรวบรวมข้อมูลด้วยวิธีที่มีประโยชน์เช่น:

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 เพื่อให้สอดคล้องกับคำแถลงปัญหาโดยตรงที่สุดเท่าที่จะทำได้แทนที่จะทำให้เร็วขึ้น


สิ่งนี้ทำให้ง่ายขึ้นอย่างมากโดยการใช้เม็ดมีดหลายค่าที่มีสติและ ANSI quoting ใน SQL-oriented SQL ของคุณ ขอขอบคุณ; เป็นเรื่องดีที่จะไม่เห็น backticks สักครั้ง สิ่งที่ฉันต้องเปลี่ยนคือการสร้างคีย์สังเคราะห์


นี่คือสิ่งที่ฉันมีอยู่ในใจ
Colin 't Hart

1
ขอขอบคุณที่โพสต์โซลูชันนี้ หากมีปัญหาของฉันในการใช้สิ่งนี้ตามที่ @ Colin'tHart แนะนำไว้ข้างต้น (ไม่เคยทำอะไรแบบนั้นมาก่อน แต่ดูเหมือนว่าจะมีประโยชน์สำหรับสถิติอื่น ๆ ที่ฉันอาจต้องการคำนวณในอนาคต) เป็นเรื่องที่น่าสนใจที่จะมีหลายวิธีในการบรรลุผลงานที่ฉันต้องการ เรียนรู้มากแน่นอนวันนี้
keth

1
ต้องการเรียนรู้เพิ่มเติมexplain analyzeแบบสอบถามแผน (หรือ MySQL เทียบเท่า) และคิดออกสิ่งที่พวกเขาทุกคนทำและวิธี :)
เครก Ringer

@ CraigRinger - ขอบคุณ คำปรึกษาที่ดี. อันที่จริงแล้วเป็นแบบนั้นกับโซลูชันทั้งหมดที่มีให้จนถึงตอนนี้ (ฉันใช้ SqlFiddles "แผนการดำเนินการดู") แต่แน่นอนฉันต้องทำงานใน "คิดออกว่าพวกเขาทำอะไรและวิธี" ส่วนเมื่ออ่านผลลัพธ์ = O
keth

6

สิ่งที่@Joshua แสดงสำหรับ MySQLทำงานใน Postgres เช่นกัน 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- แม้ว่า verbose เพิ่มเติม - โดยทั่วไปแล้วจะเร็วขึ้นเล็กน้อย และพกพาได้มากกว่านี้หากเรื่องสำคัญ:

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;

ซอ Fiddle


1
TIL: PostgreSQL เช่น MySQL, ใบอนุญาตอ้างอิงคอลัมน์ใน 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 หลายอันมันจะใช้อาร์เรย์และอาร์เรย์ที่ไม่ถูกต้องที่สุด แหล่งที่มาสำหรับแรงบันดาลใจ: /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;

ซอ Fiddle: http://sqlfiddle.com/#!12/4980b/3

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.