Эффективно сгруппируйте запрос по одному столбцу, взяв максимум другого столбца и третьего столбца, поступающего из той же строки, что и максимальный столбец - PullRequest
0 голосов
/ 09 июля 2019

У меня есть таблица с 100 000 000+ значений, поэтому эффективность очень важна для меня. Мне нужно взять информацию из таблицы A, присоединить ее к индексной таблице B, а затем присоединить к таблице C, используя индекс, полученный из таблицы B. Проблема в том, что для каждого значения в таблице A есть несколько индексов, и я хочу получить тот, с самой последней датой.

Запрос ниже создает дубликаты:

SELECT ID_1, ID_2, Date
INTO #DEST_TABLE FROM Table_1 t1
INNER JOIN Table_2 t2 ON t1.ID_1=t2.ID_1
INNER JOIN Table_3 t3 ON t2.ID_2=t3.ID_2

Это не так, но при работе с более чем 35 000 против 40 000 элементов время выполнения изменяется от <5 с до> 1 мин:

SELECT ID_1, ID_2, Date
INTO #DEST_TABLE FROM 
(SELECT * FROM Table_1 l CROSS APPLY Table_2 t2 WHERE t1.ID_1=t2.ID_1) t_temp
LEFT JOIN Table_3 t3 ON t_temp.ID_2=t3.ID_2

Как я могу максимально сократить время выполнения? Вот пример таблицы: enter image description here

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

Ответы [ 2 ]

1 голос
/ 10 июля 2019
  1. Прежде всего, когда вы работаете над 100 миллионами + записей, и это слишком объединяясь с другими таблицами, первое, что я хотел бы спросить, является то, что является обоснование не создания индексов, которые могут покрыть ваш запрос. Если Вы не администратор этой системы, я бы предложил вам следует довести это до группы администраторов и попытаться понять, что точная причина (если таковая имеется) они не хотят индексировать эту огромную таблицу. Тем более, что вы упомянули " эффективность очень важна для меня ». Помните, что «Настройка SQL» - это только один из этапов «Настройка производительности базы данных», и вы можете настроить только столько, сколько сможете написать хороший SQL-запрос. Когда объем данных становится огромным, хорошего SQL-запроса никогда не бывает достаточно без принятия других мер по настройке производительности.
  2. Помимо того, что уже предоставил Роджер, вот несколько решений, которые вы можете попробовать:

Раствор 1

SELECT  T1.ID_1, OA.ID_2, OA.Location
FROM    Table1 T1
        OUTER APPLY (
                        SELECT  TOP 1 T3.ID_2, T3.Location
                        FROM    Table2 T2
                                INNER JOIN Table3 T3
                                    ON T2.ID_2 = T3.ID_2
                        WHERE   T2.ID_1 = T1.ID_1
                        ORDER   BY T3.Date DESC
                    ) OA;

Решение 2:

SELECT  DISTINCT
        T1.ID_1
        ,T2.ID_2
        ,Location = FIRST_VALUE(T3.Location) OVER (PARTITION BY T1.ID_1 ORDER BY T3.Date DESC)
FROM    Table1 T1
        INNER JOIN Table2 T2
            ON T1.ID_1 = T2.ID_1
        INNER JOIN Table3 T3
            ON T2.ID_2 = T3.ID_2;

Подготовка данных:

DROP TABLE IF EXISTS Table1

DROP TABLE IF EXISTS Table2

DROP TABLE IF EXISTS Table3

SELECT  TOP 10000 ID_1 = object_id, name
INTO    Table1
FROM    sys.all_objects 
ORDER   BY object_id

SELECT  ID_1 = T1.ID_1, ID_2 = IDENTITY(INT, 1, 1)
INTO    Table2
FROM    Table1 T1
        CROSS JOIN Table1 T2

SELECT  ID_2, Location = 'City_'+ CAST(ID_2 AS VARCHAR(100)), Date = CAST(DATEADD(DAY, ID_2/10000, GETDATE()) AS DATE)
INTO    Table3
FROM    Table2

Указатели для решения 1:

CREATE NONCLUSTERED INDEX IX_TABLE1_ID_1 ON Table1 (ID_1)
CREATE NONCLUSTERED INDEX IX_TABLE2_ID_2 ON Table2 (ID_1, ID_2)
CREATE NONCLUSTERED INDEX IX_TABLE3_ID_2 ON Table3 (ID_2, Date DESC) INCLUDE (Location)

План выполнения: enter image description here

Вы можете видеть, что все они - «Поиск по индексу», кроме Таблицы 1, которая является законным «Сканированием по индексу», поскольку вы выполняете сканирование для каждого значения значения ID_1 в Таблице1. Если вы поместите предложение where во внешний цикл для поиска нескольких конкретных значений ID_1, то это «сканирование индекса» также превратится в «поиск индекса».

Я оставлю Индексную стратегию для 2-го решения вам (в качестве домашней работы :)). Советы: Вы должны сделать местоположение как ключ, а также. Или вы можете использовать индексный подход COLUMNSTORE.

1 голос
/ 09 июля 2019

Вы можете использовать что-то вроде этого:

select top (1) with ties
    a.A_Id, b.B_Id, b.Date
from dbo.TableA a
    inner join dbo.TableB b on a.A_Id = it.A_Id
    inner join dbo.TableC c on c.B_Id = b.B_Id
order by row_number() over(partition by a.A_Id order by b.Date desc);

В качестве альтернативы, вы можете попробовать подход olde fashioneth:

select a.A_Id, b.B_Id, b.Date
from dbo.TableA a
  inner join dbo.TableB b on a.A_Id = b.A_Id
  inner join dbo.TableC c on c.B_Id = b.B_Id
where not exists (
  select 0 from dbo.TableB pb where pb.B_Id = b.B_Id and pb.Date > b.Date
);

Однако, как и во всех таких ситуациях, его производительность будет сильнозависит от показателей.SSMS может предложить вам некоторые, если вы посмотрите на план выполнения;Вдобавок ко всему, вам нужно будет индексировать все Id столбцы, и вам понадобится либо один (Date), либо составной (A_Id, Date, B_Id) на TableB.

.UPD : Если вы не можете создавать или изменять какие-либо индексы, а производительность имеет первостепенное значение, я бы предложил скопировать данные в отдельную схему или базу данных, где у вас могут быть соответствующие разрешения.Кроме того ... невозможно извлечь что-то из ничего.

...