Запрос медленный с двумя (почти одинаковыми) соединениями, но быстрее с любым - PullRequest
0 голосов
/ 22 ноября 2018

У меня сейчас очень медленный запрос.У этого запроса есть комбинированный запрос нашей позиции на акции (я называю это POSITION_QUERY, есть одна строка для одного биржевого кода, торгующего на одной бирже в одну данную дату), затем присоединяется (я называю это FIRST JOIN) к цене акции.В таблице, чтобы получить цену, условие соединения будет в трех столбцах: код акции, биржа и дата сделки.Тогда мне нужно SECOND JOIN, потому что каждая акция принадлежит составному индексу (в POSITION_QUERY каждая строка имеет столбцы, указывающие код индекса и биржу, на которой торгуется индекс).

Итак, мой запрос выглядит примерно так:

SELECT * FROM 
POSITION_QUERY t1 
 JOIN DAILY_PRICE t2
    on t1.STOCK_CODE = t2.STOCK_CODE
       and t1.STOCK_EXCHANGE = t2.EXCHANGE
       and t2.TRADE_DATE = 20181121
 JOIN DAILY_PRICE t3
    on t1.INDEX_CODE = t3.STOCK_CODE
       and t1.INDEX_EXCHANGE = t3.EXCHANGE
       and t3.TRADE_DATE = 20181121

И теперь запрос действительно очень медленный: около 3 минут, чтобы вернуть 50 строк результата.Как я уже говорил, POSITION_QUERY на самом деле является запросом, а не существующей таблицей.Но если я запускаю SELECT * FROM POSITION_QUERY, это все равно быстро (я получаю позицию только на 20181121 внутри POSITION_QUERY, поэтому объем этого запроса уже равен 50, как я упоминал ранее).DAILY_PRICE является view, но почти сопоставлен с одной существующей таблицей, и у меня есть индексы для соединенных столбцов этой таблицы.

Странная вещь для меня, если я только выполняю POSITION_QUERY, ИЛИ POSITION_QUERY с FIRST JOIN (то есть объединить DAILY_PRICE с первым набором условий), ИЛИ POSITION_QUERY с SECOND JOIN (объединить DAILY_PRICE со вторым набором условий), ВСЕ ТРИ запроса выполняются очень быстро (менее одной секунды).

Я проверил фактические планы выполнения, план с двумя объединениями и планы с одним объединением довольно похожи, но в плане с двумя объединениями есть table spool (lazy spool), стоимость которого составляет 49%.Выходной список оператора спулинга таблицы - POSOTION_QUERY, поэтому я предполагаю, что он хранит результат 'POSITION_QUERY' (но почему это не последовательное соединение?).Я плохо интерпретирую планы выполнения, поэтому я не знаю, в этом ли проблема и как я могу ее исправить.

Обновление: я вставил свой план выполнения со структурой реальной таблицы данных и запросом.Ссылка: План выполнения

Ответы [ 3 ]

0 голосов
/ 22 ноября 2018

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

DROP TABLE IF EXISTS #temp;
SELECT *
INTO #temp
FROM DAILY_PRICE
WHERE TRADE_DATE = 20181121;
CREATE INDEX [IX1] ON #temp(STOCK_CODE, EXCHANGE);

SELECT *
FROM POSITION_QUERY t1 
 JOIN #temp t2
    on t1.STOCK_CODE = t2.STOCK_CODE
       and t1.STOCK_EXCHANGE = t2.EXCHANGE
 JOIN #temp t3
    on t1.INDEX_CODE = t3.STOCK_CODE
       and t1.INDEX_EXCHANGE = t3.EXCHANGE

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

0 голосов
/ 22 ноября 2018

Какие типы данных?После генерации 520 000 строк образцов данных с неявными типами данных ваш запрос занимает всего 3 секунды:

CREATE TABLE POSITION_QUERY (STOCK_CODE INT, STOCK_EXCHANGE INT, INDEX_CODE INT, INDEX_EXCHANGE INT, TRADE_DATE INT)
CREATE TABLE DAILY_PRICE (STOCK_CODE INT, EXCHANGE INT, TRADE_DATE INT)

-- Put 520,000 rows of sample data in POSITION_QUERY.
;WITH CTE AS (
    SELECT 1 AS A
    UNION ALL
    SELECT A + 1
    FROM CTE
    WHERE A < 10
),
CTE_DATE AS (
    SELECT CAST(GETDATE() AS DATE) AS D
    UNION ALL
    SELECT DATEADD(DAY, -1, D)
    FROM CTE_DATE
    WHERE D > '10/1/2018'
)
INSERT INTO POSITION_QUERY
SELECT C1.A, C2.A, C3.A, C4.A, FORMAT(C5.D, 'yyyyMMdd')
FROM CTE C1, CTE C2, CTE C3, CTE C4, CTE_DATE C5
OPTION (MAXRECURSION 0)

-- Put 5,200 rows of sample data in DAILY_PRICE that match all POSITION_QUERY records
;WITH CTE AS (
    SELECT 1 AS A
    UNION ALL
    SELECT A + 1
    FROM CTE
    WHERE A < 10
),
CTE_DATE AS (
    SELECT CAST(GETDATE() AS DATE) AS D
    UNION ALL
    SELECT DATEADD(DAY, -1, D)
    FROM CTE_DATE
    WHERE D > '10/1/2018'
)
INSERT INTO DAILY_PRICE 
SELECT C1.A, C2.A, FORMAT(C3.D, 'yyyyMMdd')
FROM CTE C1, CTE C2, CTE_DATE C3
OPTION (MAXRECURSION 0)

-- Create nonclustered indexes on both tables' pertinent columns.
CREATE NONCLUSTERED INDEX IDX_POSITION_QUERY
ON [dbo].[POSITION_QUERY] ([STOCK_CODE],[STOCK_EXCHANGE])
INCLUDE ([INDEX_CODE],[INDEX_EXCHANGE],[TRADE_DATE])
GO

CREATE NONCLUSTERED INDEX IDX_DAILY_PRICE
ON DAILY_PRICE (STOCK_CODE, EXCHANGE, TRADE_DATE)
GO

-- Finally, run the query. It takes 3 seconds to return 520k records.
SELECT * FROM 
POSITION_QUERY t1 
 JOIN DAILY_PRICE t2
    on t1.STOCK_CODE = t2.STOCK_CODE
       and t1.STOCK_EXCHANGE = t2.EXCHANGE
       and t2.TRADE_DATE = 20181121
 JOIN DAILY_PRICE t3
    on t1.INDEX_CODE = t3.STOCK_CODE
       and t1.INDEX_EXCHANGE = t3.EXCHANGE
       and t3.TRADE_DATE = 20181121

И вот план выполнения:

https://www.brentozar.com/pastetheplan/?id=BkSgin7C7

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

0 голосов
/ 22 ноября 2018

Попробуйте это:

WITH DAILY_PRICE_TODAY (STOCK_CODE, EXCHANGE)  
AS  
-- Define the CTE query.  
(  
   SELECT STOCK_CODE, EXCHANGE 

   FROM DAILY_PRICE

   WHERE TRADE_DATE = 20181121
)  

SELECT * FROM 
POSITION_QUERY t1 
 JOIN DAILY_PRICE_TODAY t2
    on t1.STOCK_CODE = t2.STOCK_CODE
       and t1.STOCK_EXCHANGE = t2.EXCHANGE

 JOIN DAILY_PRICE_TODAY t3
    on t1.INDEX_CODE = t3.STOCK_CODE
       and t1.INDEX_EXCHANGE = t3.EXCHANGE
...