Какие моменты делают запросы sql быстрее

Я очень новичок в кодах SQL, я выбираю только несколько столбцов и использую в основном функцию GROUP BY, но мой код занимает 2 минуты, чтобы показать результат, возможно, это не длинный запрос, но мне нужно сделать быстрее. Как сделать SQL-запрос быстрее?

Для моего кода у меня есть таблица с совпадениями лиг;

Пример:

CustomerID        MatchDate         League              Matches                HomeTeam                AwayTeam
------------------------------------------------------------------------------------------------------------------------
1                 11-12-2006        La Liga             Barcelone-R.Madrid     Barcelona               RealMadrid
2                 10-10-2006        Premier League      Everton-Arsenal        Everton                 Arsenal
3                 09-10-2006        Premier League      Arsenal-Tottenham      Barcelona               RealMadrid
4                 10-10-2006        Bundesliga          Bayern-Mainz           Bayern                  MainZ 

Моя цель - подсчитать общее количество идентификаторов для каждого матча, предоставленного домашней и выездной командам, чтобы найти общее количество просмотров для каждой команды и группы по лигам, а также по командам и сезонам. Одни и те же команды могут быть HomeTeam и AwayTeam, поэтому я использовал этот код.

SELECT League, SUM(totalnum), Teams, Season FROM 
    (
        (SELECT DATE_FORMAT(MatchDate, '%Y') as 'Season', HomeTeam as Teams, League, count(distinct CustomerID) as "totalnum" 
            FROM MY_TABLE GROUP BY League, Teams, Season ) 
        UNION ALL 
        (SELECT DATE_FORMAT(MatchDate, '%Y') as 'Season', AwayTeam as Teams, League, count(distinct CustomerID) as "totalnum" 
            FROM MY_TABLE GROUP BY League, Teams, Season )
    ) aa
GROUP BY League, Teams, Season
ORDER BY totalnum DESC

Я могу получить результат, но мне нужно короче. Какие моменты могут повлиять на мой запрос.


person Axis    schedule 07.01.2018    source источник
comment
Сколько строк в таблице?   -  person Lasse V. Karlsen    schedule 07.01.2018
comment
Убедитесь, что у вас есть индексы для вашего запроса.   -  person Dragonthoughts    schedule 07.01.2018
comment
почти миллион строк   -  person Axis    schedule 07.01.2018
comment
индексы будут бесполезны, OP нужны все строки - нет предложения where   -  person Gerard H. Pille    schedule 07.01.2018
comment
Перед комментарием проверьте код @ GerardH.Pille   -  person Axis    schedule 07.01.2018
comment
@Axis Я сделал и сделал это снова: вы выбираете все строки дважды, поэтому индексы бесполезны.   -  person Gerard H. Pille    schedule 07.01.2018
comment
Я думаю, что вы могли бы вдвое сократить время, которое требуется, выбрав данные только один раз, и отменив сворачивание на домашнюю и гостевую команды: для каждой прочитанной строки вы вернете две.   -  person Gerard H. Pille    schedule 07.01.2018
comment
@Axis вы пишете count(distinct ID), поэтому я предполагаю, что в MY_TABLE один и тот же идентификатор клиента появляется несколько раз, но вы хотите подсчитать уникальный идентификатор клиента. И вопрос: ЕСЛИ CustomerID = 1 наблюдает за матчем, в котором «Барселона» является гостевой командой, а CustomerID = 1 также наблюдает за матчем, в котором «Барселона» является домашней командой, то как CustomerID = 1 должен учитываться в команде = Barcelona? В вашем запросе ID = 1 считается дважды.   -  person noymer    schedule 07.01.2018
comment
@noymer Извините, таблица с примерами выглядит непонятной, но я могу объяснить. Столбец ID - это ID каждого человека, который смотрел за матч, поэтому человек может смотреть много матчей в течение сезона. Я дал вам всего 4 строки и 1.2.3.4 это может быть любой идентификационный номер. Также я изменил свой код, пожалуйста, проверьте сейчас   -  person Axis    schedule 07.01.2018
comment
@ GerardH.Pille Даже если запрос в основном содержит full table scan, он все равно может извлечь выгоду из индексов. В этом случае индексы могут избежать необходимости в шаге сортировки, который в настоящее время требуется из-за GROUP BY.   -  person MatBailie    schedule 07.01.2018
comment
Другое наблюдение, однако, заключается в том, что в вашем запросе вы COUNT() уникальные домашние клиенты, а затем COUNT() уникальные выездные клиенты, а затем SUM() их. Это означает, что любой, кто был на домашнем матче И на выездном матче, засчитывается дважды. Это предназначено?   -  person MatBailie    schedule 07.01.2018
comment
@MatBailie Это хороший аргумент. На самом деле мне нужна SUM (), потому что результаты показывают, что домашняя и гостевая команды раздельны, но я попытался использовать только один счет (только одно совпадение, чтобы каждое такое же число), я получил ошибку   -  person Axis    schedule 07.01.2018
comment
@MatBailie Я не согласен: чтение данных из большой таблицы при отслеживании индексов будет намного медленнее, чем сортировка данных, извлеченных вами при полном сканировании таблицы.   -  person Gerard H. Pille    schedule 07.01.2018
comment
@ GerardH.Pille Если запрос может быть удовлетворен индексом покрытия, который был построен в правильном порядке, необходимом для выполнения вычислений, INDEX SCAN обычно будет иметь меньшую или равную стоимость TABLE SCAN. Только если индекс имеет недостаточное покрытие (и поэтому ему необходимо присоединиться к базовой таблице), INDEX SCAN обычно будет давать более высокую стоимость, чем TABLE SCAN. Это плюс удаление шага SORT означает, что да индекс может снизить стоимость. (Обратите внимание, что я уточняю свои предложения, вы делаете абсолютные / категоричные утверждения, которые редко всегда верны)   -  person MatBailie    schedule 07.01.2018
comment
@MatBailie, вы делаете абсолютные утверждения, правда, но это потому, что у меня только более 30 лет опыта работы с SQL, и поэтому у меня не осталось много времени, чтобы терять его зря. Включите в статистику производительности время, необходимое для обновления индекса. Я не знаю о других СУБД, но у Oracle есть таблица с индексированием, к которой можно применить вашу теорию.   -  person Gerard H. Pille    schedule 07.01.2018
comment
@ GerardH.Pille Твои 30 лет, мои 15, неважно. Я видел эмпирические доказательства того, что существует ряд случаев, которые можно решить с меньшими затратами с помощью FULL INDEX SCAN, чем с помощью FULL TABLE SCAN. Что касается стоимости обслуживания индекса, это гораздо более широкое обсуждение, чем один запрос: как часто записываются данные и какова повышенная стоимость, как часто выполняются запросы и какова экономия затрат, приносят ли индексы пользу другим запросам, будет ли триггерная / производная таблица / и т. д. лучшим решением. ВСЕ индексы связаны с расходами на запись, это звучит так, как будто вы выдвинули соломенный аргумент.   -  person MatBailie    schedule 07.01.2018
comment
Пожалуйста, прочтите это и обратите особое внимание на раздел о производительности запросов. meta.stackoverflow.com/a/271056 Пожалуйста, отредактируйте свой вопрос, чтобы предоставить дополнительные сведения.   -  person O. Jones    schedule 07.01.2018
comment
Я бы не стал возиться с агрегацией подзапросов   -  person Strawberry    schedule 07.01.2018
comment
Используете InnoDB? Сколько оперативной памяти? Насколько велик innodb_buffer_pool_size?   -  person Rick James    schedule 08.01.2018


Ответы (2)


Если вы примените эти два индекса, я ожидаю, что ваш существующий запрос должен ускориться ...

CREATE INDEX MY_TABLE_league_home_date_cust
    ON MY_TABLE(
        League, HomeTeam, MatchDate, CustomerID
    );

CREATE INDEX MY_TABLE_league_away_date_cust
    ON MY_TABLE(
        League, AwayTeam, MatchDate, CustomerID
    );


Тем не менее, я подозреваю, что самая высокая стоимость вашего запроса - это COUNT(DISTINCT CustomerID). Для этого нужно отсортировать все данные. Это может означать, что следующие индексы могли бы быть лучше ...

CREATE INDEX MY_TABLE_cust_league_home_date
    ON MY_TABLE(
        CustomerID, League, HomeTeam, MatchDate
    );

CREATE INDEX MY_TABLE_cust_league_away_date
    ON MY_TABLE(
        CustomerID, League, AwayTeam, MatchDate
    );


Другое наблюдение, однако, заключается в том, что в вашем запросе вы COUNT() уникальные "домашние клиенты", а затем COUNT() уникальные "выездные клиенты", а затем SUM() их. Это означает, что любой, кто был на домашнем матче И на выездном матче, засчитывается дважды. Это предназначено?

Если это не предназначено, вы можете обнаружить, что стоимость вашего запроса еще выше ...

SELECT
  Team,
  League,
  DATE_FORMAT(MatchDate, '%Y')   AS Season,
  COUNT(DISTINCT CustomerID)     AS total
FROM 
(
  SELECT CustomerID, League, HomeTeam AS Team, MatchDate FROM MyTable
  UNION ALL
  SELECT CustomerID, League, AwayTeam AS Team, MatchDate FROM MyTable
)
  combined_view
GROUP BY
  Team, League, Season
ORDER BY
  total DESC


Я думаю, что в целом лучше всего добавить вычисляемый столбец для Season, а затем использовать слегка измененную версию первых индексов ...

ALTER TABLE
  MY_TABLE
ADD Season VARCHAR(4) AS (
  DATE_FORMAT(MatchDate, '%Y')
);

CREATE INDEX MY_TABLE_league_home_season_cust
    ON MY_TABLE(
        League, HomeTeam, Season, CustomerID
    );

CREATE INDEX MY_TABLE_league_away_season_cust
    ON MY_TABLE(
        League, AwayTeam, Season, CustomerID
    );

SELECT
  Team,
  League,
  Season,
  COUNT(DISTINCT CustomerID)     AS total
FROM 
(
  SELECT CustomerID, League, HomeTeam AS Team, Season FROM MyTable
  UNION ALL
  SELECT CustomerID, League, AwayTeam AS Team, Season FROM MyTable
)
  combined_view
GROUP BY
  Team, League, Season
ORDER BY
  total DESC
person MatBailie    schedule 07.01.2018
comment
Привет, @MatBailie, спасибо за попытку. К сожалению, после создания индексов и использования вашего кода время выполнения было немного больше. Ps, не могу что-то добавить или убрать из таблицы. - person Axis; 07.01.2018
comment
Тогда вам нужно рассмотреть альтернативные пути, помимо оптимизации. Является ли добавленный мной точный запрос обязательным или по вашей оценке SUM(COUNT(DISTINCT)+COUNT(DISTINCT)) достаточно? Если вашего достаточно, это более простая разумная оценка; просто удалите DISTINCT? Что важнее, точность или время выполнения? Вам необходимо четко изучить свои требования и формулировку проблемы, нередко требование основывается на представлении о том, что это будет легко, но после осознания того, что это сложно / медленно / дорого, выдвигается более простое требование. - person MatBailie; 07.01.2018
comment
Точно. Во-первых, когда я задал этот вопрос, я подумал, что у меня есть глупый вопрос о времени, но похоже, что я должен выбрать время / точность! - person Axis; 07.01.2018
comment
@Axis - Улучшила ли какая-либо пара индексов время выполнения вашего запроса (из исходного сообщения)? - person MatBailie; 07.01.2018
comment
@Axis - Кроме того, не могли бы вы включить определение таблицы, включая существующие индексы и т. Д.? Кроме того, почему вы можете добавлять индексы, но не столбцы? - person MatBailie; 07.01.2018
comment
Мне очень жаль, @MatBailie, я пропустил это. У него есть только индексы, которые вы написали, больше нет индексов. Я не мог понять Кроме того, почему вы можете добавлять индексы, но не столбцы, которые я ввел, которые вы предложили, и это было успешным, но, к сожалению, не изменило время выполнения. - person Axis; 07.01.2018
comment
Вы написали Ps, I cannot be able to add or remove something from the table., я так понял, что вы не можете протестировать параметр ALTER TABLE MY_TABLE ADD COLUMN? - person MatBailie; 07.01.2018
comment
Когда вы добавили предложенные индексы, повлияло ли это на время выполнения? Каково было время выполнения до добавления индексов? Каково было время выполнения после добавления индексов? Вы пробовали все четыре потенциальных индекса или только одну пару индексов? - person MatBailie; 07.01.2018
comment
Извините, английский - мой второй язык :( Я имел в виду, что я не могу изменить свою таблицу или создать какую-либо таблицу. Я должен сохранить исходную таблицу. Да, раньше было 1 мин 57 сек (мой оригинал), тогда ваш код был 2 мин 24 сек после индексов, которые я вводил не отдельно тогда было 2 минуты 27 секунд - person Axis; 07.01.2018
comment
@Axis - Буквально это означает, что вы также не можете добавить индекс. Столбец, который я предлагаю добавить, является вычисляемым столбцом, это не настоящий столбец: вы не вставляете в него данные, MySQL вычисляет значение в вычисляемом столбце для вас на основе других столбцов, которые у вас уже есть. Почему вы можете добавлять индексы, но не вычисляемые столбцы? (Или, что более важно, каков полный список того, что вам разрешено / запрещено делать?) - person MatBailie; 07.01.2018
comment
Кстати, ваш английский лучше, чем у многих носителей английского языка. - person MatBailie; 07.01.2018
comment
Также можно использовать тип данных YEAR и функцию YEAR(MatchDate). - person Rick James; 08.01.2018
comment
Чем больше UNION, тем вероятно будет медленнее. - person Rick James; 08.01.2018

Вы можете это попробовать?

SELECT DATE_FORMAT(A.MatchDate, '%Y') as 'Season',  
       case c.col
         when 'home' then A.HomeTeam
         when 'away' then A.AwayTeam
       end as Teams,
       A.League, count(distinct A.CustomerID) as "totalnum" 
        FROM MY_TABLE A
        cross join ( select 'home' as col union all select 'away') c
     GROUP BY League, Teams, Season
ORDER BY totalnum DESC

См. Результаты в SQL Fiddle: новое: http://sqlfiddle.com/#!9/dd0335/11 (предыдущее: http://sqlfiddle.com/#!9/dd0335/9)

person Gerard H. Pille    schedule 07.01.2018
comment
Почти такое же время исполнения - person Axis; 07.01.2018
comment
Конечно. У меня есть таблица с миллионами строк, включающая команды, матчи, лиги и так далее. Мне нужен уникальный клиент (что важно), который следит за каждой командой, но строки магазинов совпадают, поэтому две команды, моя идея состоит в том, чтобы добавить каждого клиента по двум командам, чтобы найти количество часов. Окончательная идея - найти общее количество просмотров по лигам и командам (я буду использовать этот результат для другой платформы данных, поэтому, к сожалению, я не могу использовать в двух разных столбцах) - person Axis; 07.01.2018
comment
Извините, я имел в виду план выполнения MySQL (dev.mysql. com / doc / refman / 5.5 / en / execution-plan-information.html). Я упростил свой ответ. - person Gerard H. Pille; 07.01.2018
comment
MySQL не делает того, о чем мы его не просили. Чтобы быть уверенным в подсчете (различном): если один и тот же клиент смотрел одну и ту же команду, играющую несколько раз в год, вы хотите засчитать это только как одно? - person Gerard H. Pille; 07.01.2018
comment
Это совершенно нормально. Что касается конкуренции, некоторые клиенты могут подключаться много раз в рамках одного и того же матча, поэтому я хотел избежать этого, иначе все клиенты в одном году полностью в порядке. - person Axis; 07.01.2018
comment
Тогда, возможно, нормализация вашей схемы может дать вам прирост производительности: две таблицы, одна из которых содержит совпадения, другая - посещения (содержащие только идентификатор клиента и идентификатор совпадения). Будет намного меньше данных для чтения. - person Gerard H. Pille; 07.01.2018