Как использовать индексирование таблиц SQL Server CE - стоит ли делать этот запрос? - PullRequest
1 голос
/ 27 марта 2012

Был процесс обучения, и мне наконец-то удалось получить запрос, работающий для SQL Server Compact Edition.

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

Этот запрос возьмет всех игроков в одной школе, которые принадлежат к одному весу, и выберет самый высокий навык среди них и установит, что начальное значение игроков равно true.

        cmd.CommandText = "UPDATE player " &
            "SET starter = 'TRUE' " &
            "WHERE NOT EXISTS" &
            "(SELECT school, weight, skill " &
            "FROM player b " &
            "WHERE b.school = player.school " &
            "AND b.weight = player.weight " &
            "AND b.skill > player.skill)"
        cmd.ExecuteNonQuery()

Запрос выполняется очень медленно.

  • На моем игровом столе ~ 170 000 игроков
  • Каждый игрок принадлежит к одной из 4500 школ
  • Каждый игрок принадлежит одному из 14 весов
  • Каждая школа принадлежит одному из 50 штатов

Можно ли как-нибудь индексировать этот запрос, чтобы он выполнялся быстрее? Или что я могу сделать, потому что сейчас время, затрачиваемое на выполнение этого запроса, безумно.

Если это поможет объяснить, с чем я работаю, я предоставлю изображение стола игрока. (Идентификатор и имя не отображаются)

http://i44.tinypic.com/jkygcp.png

Спасибо!

@ rob - это запрос, набранный в моей программе.

        cmd.CommandText = "update p1 " &
                    "set starter = 'TRUE' " &
                    "from player as p1 " &
                    "left outer join player as p2 " &
                    "on p1.school = p2.school " &
                    "and p1.weight = p2.weight " &
                    "and p1.playerId <> p2.playerId " &
                    "and p1.skill <= p2.skill " &
                    "where(p2.playerId Is null)"
        cmd.ExecuteNonQuery()

Ответы [ 4 ]

2 голосов
/ 27 марта 2012

Я бы согласился с Натаниэлем Фордом, что первым шагом, вероятно, будет немного переписать запрос, но я мог бы предложить другое направление (которое могло бы или не могло бы сработать).

Я думаю, что ваша цельпометить «лучшего» игрока в каждой школе в каждой весовой категории в качестве стартера.Я также думаю, что вы хотите пометить этот стартер только в том случае, если есть лучший результат (навык строго выше, чем у всех в школе / вес).Я мог бы не иметь этого последнего критерия правильно ... так как он мог бы оставить некоторые школы / веса без отмеченного стартера (это легко исправить с помощью второго запроса аналогичной формы).

Этот тип "лучший из группы"запрос должен быть выражен аккуратно с использованием оконных функций, но я не думаю, что SQL Compact их поддерживает.Вы можете выразить это как anti-join .Это странно, но может помочь предложить индекс.

update p1
set starter = 'TRUE'
from player as p1
left outer join player as p2
   on p1.school = p2.school
  and p1.weight = p2.weight
  and p1.playerId <> p2.playerId
  and p1.skill <= p2.skill
where p2.playerId is null

Мы обновляем p1.Давайте попробуем присоединиться как p2 ко всем игрокам в той же школе / весе, что и p1, которые так же хороши или лучше, чем p1 (и давайте исключим игрока в p1, о котором мы говорим, так как всетак же хорош, как и он сам).Если мы найдем кого-то такого же хорошего или лучшего, то p1 будет , а не стартером.Однако, если мы не можем найти кого-то столь же хорошего или лучшего, что означает, что left outer join оставит p2.playerId NULL, тогда у нас будет лучший выбор для этой школы / веса и мы должны отметить p1 в качестве стартера.(Этот абзац был немного свободным; надеюсь, он был понятен.)

Анти-объединения - это способ выражения несуществующих запросов.Я думаю, что оптимизатор может переписать некоторые или все "несуществующие" запросы как анти-объединения.Суть в том, чтобы предложить индекс (и, возможно, помочь оптимизатору, если он пропустил этот трюк).

Если этот запрос выполняет то, что вы хотите, и работает на SQL Compact (к которому у меня нет доступа), и на самом деле это правильно (опять же - нулевое тестирование), тогда вы можете подумать об индексе примерно так:

create index player_i01 on player ( school, weight, skill, playerId )

Если ни одна из этих вещей не даст вам желаемой производительности, то подумайте о созданииРабочий стол (возможно, временный стол) для разделения работы.Я сохраню это для другого ответа ...; -)

1 голос
/ 27 марта 2012

Для общего учебника по теме индексации и производительности я настоятельно рекомендую прочитать: Использовать Индекс, Люк! .

В этом конкретном случае вы захотите включитьСУБД для эффективного определения (не) существования строки (ий), удовлетворяющих критериям согласно предложению WHERE вашего запроса.Я не слишком знаком с SQL Server Compact, но на большинстве СУБД составной индекс для {school, weight, skill} в таблице player должен помочь.

1 голос
/ 27 марта 2012

В этом случае код, который вы выполняете для запуска sql, несколько сбивает с толку.Я переформулировал это ниже:

UPDATE player SET starter = 'TRUE' WHERE NOT EXISTS 
(
  SELECT school, weight, skill 
    FROM player b 
   WHERE b.school = player.school 
     AND b.weight = player.weight 
     AND b.skill > player.skill
)

Давайте сначала посмотрим на внутреннее выражение select.Это утверждение, по сути, запрашивает таблицу результатов с тремя столбцами (школа, вес, навык) из таблицы «игрок».Вы называете эту таблицу как 'b'.Вы тогда продолжаете фильтровать тремя вещами;является ли b.school эквивалентом player.school и так далее.

Ваша первая проблема заключается в том, что по вашему псевдониму b.school = player.school эквивалентно player.school = player.school.Для первых двух фильтров будут выбраны все строки - это ненужный расчет.Однако ваш последний фильтр всегда должен возвращать false, и поэтому ваш запрос, по всей вероятности, занимает так много времени: все строки будут возвращены из вашего внутреннего оператора select.

Предполагая, что во внешнем операторе выпсевдоним игрока, скажем, a, тогда вы получите отдельный результат, потому что теперь вы спрашиваете, находится ли игрок a в той же школе, имеет тот же вес и лучше.(1) Однако в данном случае вы сравниваете каждого игрока с любым другим игроком: для 170 000 игроков это 170 000 ^ 2 или примерно 39 миллиардов сравнений ... вот почему этот запрос такой медленный.

Есть два способа решения вашей проблемы.Во-первых, некоторые из этих программ должны обрабатываться программно.

  1. выберите все уникальные школы в качестве запроса
  2. для каждой школы, запустите аналогичный запрос

.Другое - сделать умное соединение SQL.Здесь я использую 'playerId' в качестве некоторого столбца, который является уникальным идентификатором.(Надеюсь, у вашей таблицы есть один.) (2)

SELECT a.playerId 
  FROM player a INNER JOIN player b
    ON ( a.school = b.school AND a.weight = b.weight)
 WHERE a.skill > b.skill

Используя встроенную функциональность соединения (3), этот запрос должен выполняться намного быстрее и привести к таблице всех возможных совпадений (только игроки в той же школе и в том же весе), который затем фильтруется по навыку.Исходная таблица результатов, предварительно отфильтрованная, будет намного меньше той, с которой вы работали.

И последнее.использование «не существует» довольно грязно и может повлиять на вашу производительность.Попробуйте что-то вроде:

UPDATE player c SET starter = 'TRUE' WHERE c.player IN
(
  SELECT a.playerId 
    FROM player a INNER JOIN player b
      ON ( a.school = b.school AND a.weight = b.weight)
   WHERE a.skill > b.skill
)

(1) примечание: в некоторых реализациях sql возможно, что 'player' во внутреннем выражении будет интерпретироваться как 'player' во внешнем выражении, но я 'Я не уверен, что это гарантировано.Поэтому вы должны использовать псевдоним, чтобы быть уверенным.

(2) Я не на 100%, это будет работать, потому что у меня нет вашего стола для игры.

(3) SQL-серверыкак правило, построены, чтобы сделать это быстро.Но вы должны знать, как его использовать.

0 голосов
/ 27 марта 2012

Первое место, где нужно искать улучшения, связанные с индексами, - это предложения join и where.Я ожидаю, что сложный кластеризованный ключ о состоянии, весе и школе (в таблице игроков) приведет к заметному улучшению.

Я бы также предложил добавить колонку автоинкремента «Идентификатор строки» в таблицу игроков.Поместите некластеризованный индекс первичного ключа в эту таблицу.Затем обновите таблицу, чтобы использовать новое поле первичного ключа в запросе:

WHERE b.RowId = player.RowId
...