SQL Join занимает много времени - PullRequest
1 голос
/ 23 июня 2009

У меня есть две таблицы

(1) MonthlyTarget {SalesManCode, TargetMonthYear, TargetValue}; в этой таблице 1966177 строк.

(2) MonthlySales  {SalesManCode, SaleDate, AchievedValue};

в этой таблице 400310 строк.

Я должен сделать запрос, который выдаст результат, подобный следующей таблице:

{SalesManCode, JanTar, JanAch, FebTar, FebAch,....., DecTar, DecAch}

Проблема в том, что объединение этих двух таблиц занимает много времени.

Каким должен быть запрос?

Как оптимизировать запрос?

Я не хочу заниматься индексацией.

Ответы [ 7 ]

4 голосов
/ 23 июня 2009

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

3 голосов
/ 23 июня 2009

Похоже, вам не хватает некоторых столбцов в таблице MonthlyTarget, а именно столбца "TargetDate".

В дополнение к тому, что все уже говорили об индексации, иногда может помочь подход «разделяй и властвуй». Вместо того, чтобы присоединять таблицу строк 1966177 к таблице строк 400310, создайте крошечные временные таблицы и соедините их вместе:

CREATE TABLE #MonthlySalesAgg
(
    SalesManCode int,
    JanTar money,
    FebTar money,
    MarTar money,
    AprTar money,
    MayTar money,
    JunTar money,
    JulTar money,
    AugTar money,
    SepTar money,
    OctTar money,
    NovTar money,
    DecTar money

    PRIMARY KEY CLUSTERED (SalesManCode)
)

INSERT INTO #MonthlySalesAgg
SELECT *
FROM
(SELECT SalesManCode, TargetValue, SaleMonth = Month(TargetDate) FROM MonthlyTarget) as temp
PIVOT
(
    Max(TargetValue)
    FOR [SaleMonth] IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) as p

CREATE TABLE #MonthlyTargetAgg
(
    SalesManCode int,
    JanAch money,
    FebAch money,
    MarAch money,
    AprAch money,
    MayAch money,
    JunAch money,
    JulAch money,
    AugAch money,
    SepAch money,
    OctAch money,
    NovAch money,
    DecAch money

    PRIMARY KEY CLUSTERED (SalesManCode)
)

INSERT INTO #MonthlyTargetAgg
SELECT * FROM
(SELECT SalesManCode, AchievedValue, SaleMonth = Month(SaleDate) FROM MonthlySales) as temp
PIVOT
(
    Sum(AchievedValue)
    FOR [SaleMonth] IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) as p

Приведенные выше запросы создают две промежуточные таблицы, которые должны содержать то же количество записей, что и ваша таблица SalesMan. Присоединиться к ним просто:

SELECT *
FROM #MonthlyTargetAgg target
INNER JOIN #MonthlySalesAgg sales ON target.SalesManCode = sales.SalesManCode

Если вам все время нужно извлекать данные по месяцам, вместо этого переместите код в представление.

PIVOT требует SQL Server 2005 или выше, и это часто очень полезный оператор. Надеемся, что SQL Server 2008 позволит пользователям поворачиваться более чем на один столбец одновременно, что приведет к еще более простому запросу, чем показано выше.

Использование SQL Server 2000:

PIVOT - это синтаксический сахар. Например,

SELECT * FROM
(SELECT SalesManCode, AchievedValue, SaleMonth = Month(SaleDate) FROM MonthlySales) as temp
PIVOT
(
    Sum(AchievedValue)
    FOR [SaleMonth] IN ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) as p

Становится

SELECT
    SalesManCode,
    [1] = Sum(case SaleMonth when 1 then AchievedValue else 0 end),
    [2] = Sum(case SaleMonth when 2 then AchievedValue else 0 end),
    [3] = Sum(case SaleMonth when 3 then AchievedValue else 0 end),
    [4] = Sum(case SaleMonth when 4 then AchievedValue else 0 end),
    [5] = Sum(case SaleMonth when 5 then AchievedValue else 0 end),
    [6] = Sum(case SaleMonth when 6 then AchievedValue else 0 end),
    [7] = Sum(case SaleMonth when 7 then AchievedValue else 0 end),
    [8] = Sum(case SaleMonth when 8 then AchievedValue else 0 end),
    [9] = Sum(case SaleMonth when 9 then AchievedValue else 0 end),
    [10] = Sum(case SaleMonth when 10 then AchievedValue else 0 end),
    [11] = Sum(case SaleMonth when 11 then AchievedValue else 0 end),
    [12] = Sum(case SaleMonth when 12 then AchievedValue else 0 end)
FROM
    (SELECT SalesManCode, AchievedValue, SaleMonth = Month(SaleDate) FROM MonthlySales) as temp
GROUP BY SalesManCode
3 голосов
/ 23 июня 2009

Проверьте правильность индексации в таблицах. Почти невозможно сказать, не глядя на саму базу данных, но в 99% случаев медленное соединение происходит из-за неправильных или отсутствующих индексов таблиц.

1 голос
/ 23 июня 2009

Если уволить кучу продавцов, рассмотрите следующие варианты:

  • создайте пакетный процесс, чтобы запустить его каждую ночь, и заполните несколько таблиц отчетов результатами вашего запроса
  • создать индексированное / материализованное представление нужного вам запроса (хотя вам нужно будет создать индекс для индексированного представления, так что, возможно, это нарушит ваше правило отсутствия индексов)
1 голос
/ 23 июня 2009

Я не хочу заниматься индексацией.

Вы должны рассмотреть вопрос об индексации. Не имеет значения, как вы пишете запрос, у движка БД нет другого выбора, кроме как сканировать каждую таблицу в поисках соединений, и он, вероятно, будет делать это снова и снова. У вас нет выбора.

Если вы не хотите связываться с индексированием, поскольку не можете контролировать базу данных, рассмотрите возможность экспорта данных в локальный экземпляр SQL Express.

Черт, даже экспорт данных в плоские файлы, сортировка файлов по SalesManCode и написание простой программы для их чтения и сопоставления будут быстрее.

0 голосов
/ 23 июня 2009

Если индексация не возможна, единственный другой способ ускорить ее - поставить ее на более быстрый сервер. Что-то подсказывает мне, что индексирование будет проще.

0 голосов
/ 23 июня 2009

У вас есть фильтры? Не могли бы вы сохранить некоторые частичные результаты во временной таблице, а затем объединить их с остальными данными после того, как вы уменьшили их размер?

...