Порахуйте, де два чи більше стовпців поспіль мають певне значення [баскетбол, подвійний подвійний, потрійний подвійний]


20

Я граю в баскетбольну гру, яка дозволяє виводити її статистику у вигляді файлу бази даних, тому можна обчислити з неї статистику, яка не реалізована в грі. Поки я не мав жодних проблем з підрахунком потрібних статистичних даних, але тепер у мене виникли проблеми: підрахунок кількості подвійних парних та / або потрійних парних гравців, зроблених за сезон, зі своєї статистики гри.

Визначення подвійного подвійного і потрійного подвійного таке:

Двомісний-подвійний:

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

Тримісний:

Потрійний дубль визначається як вистава, при якій гравець накопичує двоцифрове число в трьох з п’яти статистичних категорій - очок, рикошетів, передач, крадіжок та заблокованих пострілів - у грі.

Чотирьох- та подвійний (додано для уточнення)

Чотирикратний дубль визначається як вистава, при якій гравець накопичує двоцифрове число у чотирьох із п’яти статистичних категорій - очок, рикошетів, передач, крадіжок та заблокованих пострілів - у грі.

У таблиці "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

Привіт, дякую за рішення. Мені дуже подобається. Робить саме те, що я хочу, і легко розширюється, включаючи чотиримісний та подвійний парний без особливих записів. Зробимо це на сьогодні прийнятою відповіддю. :)
keth

Мені подобається ваш код, але ви можете зламати його ще коротше. Не потрібно використовувати CASEоператори, оскільки булеві вирази оцінюються до 1, коли істинного та 0, коли помилкового. Я додав його до своєї відповіді нижче, кричу вам, оскільки не можу розмістити тут повний блок коду SQL.
Джошуа Хубер

Дякую Джошуа. Повністю це помітили, і це виглядає набагато краще.
SQLChao

1
@JoshuaHuber Правильно, але тоді запит працюватиме лише в MySQL. Використання CASEта SUM/COUNTдозволяє йому працювати і на Postgres.
ypercubeᵀᴹ

@ypercube: Власне, додавання булевих файлів працює і в Postgres. Потрібно лише чітко виступити. Але, CASEяк правило, крихітний трохи швидше. Я додав демонстрацію з кількома іншими незначними вдосконаленнями.
Ервін Брандстеттер

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висловлювання, оскільки boolean 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, оскільки не любить робити boolean + boolean. Я все ще не люблю 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, хороший момент і спасибі Щойно просив це уточнення як коментар до вищезазначеного питання. Семантика. Я вважаю, що чотири голи в хокеї досі вважаються "тягнути хет-трик", але чотири поспіль удари в боулінгу, можливо, не вважатимуться "індичкою", скоріше це "квадроциклом". Я не знаю семантику кожної гри. Ви приймаєте рішення і вибираєте =або >=як годиться.
Джошуа Хубер

Дякуємо за ваше рішення. Однозначно робить те, що я хочу. Також, як і коротка версія JChao, яку ви надали.
кет

1
Додавання булевих програм не працюватиме в PostgreSQL, майте це на увазі.
Крейг Рінгер

@CraigRinger - спасибі за вказівку на це. Оскільки я все ще зелений за вухами, коли мова заходить про SQL взагалі та PostgreSQl зокрема, для мене це передача цінної інформації. :)
кет

1
@CraigRinger Приємно, але я не думаю, що MySQL підтримує CAST(... AS int) ( stackoverflow.com/questions/12126991/… ). MySQL може робити CAST(... AS UNSIGNED), що працює в цьому запиті, але PostgreSQL не може. Не впевнений, що є спільне, CASTщо обидва можуть зробити для портативності. Найгірший CASE, можливо, CASEв кінцевому підсумку, якщо портативність першорядна.
Джошуа Хубер

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, сезон) достатньо для однозначної ідентифікації гравців, тобто ідентифікатор гравця є унікальним для різних команд. Якщо це не так, вам потрібно буде включити достатньо іншої інформації, щоб надати унікальний ключ.

За допомогою цих непередаваних даних тепер можна фільтрувати та агрегувати корисними способами, як-от:

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 цитуванням у вашому SQL, орієнтованому на MySQL. Дякую; приємно не бачити задниць жодного разу. Все, що мені довелося змінити, це генерація синтетичних ключів.


Це щось таке, що я мав на увазі.
Colin 't Hart

1
Дякуємо за публікацію цього рішення. Якби мої проблеми з реалізацією чогось подібного, як це запропонував @ Colin'tHart вище (ніколи раніше подібного не робив, але здається, корисним є для деяких інших статистичних даних, які я, можливо, захотів би визначити в майбутньому). Цікаво, скільки існує способів досягти бажаного результату. Однозначно сьогодні багато чого навчився.
кет

1
Щоб дізнатися більше, explain analyzeплануйте запити (або еквівалент MySQL) та з’ясуйте, що вони всі роблять і як :)
Крейг Рінгер

@CraigRinger - Дякую Хороша порада. Насправді це робилося з усіма рішеннями, що надавалися до цього часу (я використовував SqlFiddles "переглянути план виконання"). Але мені, безумовно, потрібно попрацювати над частиною "зрозуміти, що вони всі роблять і як", читаючи результати. = О
кет

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;
  • Також використовується коротший метод підрахунку парних значень у зовнішній частині SELECT.
    Деталі у цій відповіді.

Однак, 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 Fiddle.



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 та масив. Джерело для натхнення: /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 Fiddle: http://sqlfiddle.com/#!12/4980b/3

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