Запит виконується дуже повільно, чи є якийсь спосіб його вдосконалити?


9

У мене є наступний запит, і через велику кількість SUMвикликів функцій мій запит працює надто повільно. У моїй базі даних є багато записів, і я хотів би отримати звіт за поточний і минулий рік (останні 30 днів, останні 90 днів і останні 365 днів) для кожного:

SELECT 
    b.id as [ID]
    ,d.[Title] as [Title]
    ,e.Class as [Class]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]

    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
    ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]


    FROM 
    tb1 a
INNER JOIN 
    tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN 
    tb3 c on b.fid = c.col5
INNER JOIN       
    tb4 d on c.id = d.col6
INNER JOIN 
    tb5 e on c.col7 = e.id
GROUP BY
    b.id, d.Title, e.Class

Хтось має ідею, як я можу покращити запит, щоб швидше працювати?

EDIT: Мене рекомендували перенести DATEADDвиклик функції до whereоператора та завантажувати спочатку два роки, потім фільтрувати їх у стовпцях, але я не впевнений, що запропонована відповідь виконується та працює, її можна знайти тут: https: // stackoverflow. com / a / 59944426/12536284

Якщо ви згодні з вищезазначеним рішенням, будь ласка, покажіть мені, як я можу застосувати його у своєму поточному запиті?

Просто FYI, я використовую цей SP в C #, Entity Framework (DB-First), приблизно так:

var result = MyDBEntities.CalculatorSP();

4
Покажіть нам свій план виконання ...
Дейл К

1
ПРО що завгодно - це те, що може зробити запит повільним
Фабіо


2
Знову, будь ласка, опублікуйте план виконання.
SQL Police

2
Досі ми цього не бачимо Execution Plan. Опублікуйте, будь ласка
Арун Паланісамі

Відповіді:


10

Як вже було сказано, план виконання буде дуже корисним у цьому випадку. Виходячи з того, що ви показали, здається, ви вилучили 12 стовпців із 15 загальних стовпців tb1 (a), тож ви можете спробувати запустити запит без будь-якого приєднання і просто проти того, tb1щоб побачити, чи працює ваш запит як очікувалося. Оскільки я не бачу нічого поганого у ваших викликах функцій SUM, я найкраще здогадуюсь, що у вас є проблеми з вашими приєднаннями, я б запропонував зробити наступне. Ви можете почати, виключивши, наприклад, останнє з'єднання, INNER JOIN tb5 e on c.col7 = e.idі будь-яке пов'язане з ним використання, як e.Class as [Class]іe.Classу вашій групі за заявою. Ми не збираємось його повністю виключати, це лише тест, щоб переконатися, що проблема з цим чи ні, якщо ваш запит працює краще і, як очікувалося, ви можете спробувати використовувати таблицю темп як спосіб вирішення замість останнього приєднання , щось на зразок цього:

SELECT *
INTO #Temp
FROM
  (
     select * from tb5
  ) As tempTable;

SELECT 
    b.id as [ID]
    ,d.[Title] as [Title]
    ,e.Class as [Class]

    -- SUM Functions

FROM 
    tb1 a
INNER JOIN 
    tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN 
    tb3 c on b.fid = c.col5
INNER JOIN       
    tb4 d on c.id = d.col6
INNER JOIN 
    #Temp e on c.col7 = e.id
GROUP BY
    b.id, d.Title, e.Class

Насправді тимчасові таблиці - це таблиці, які тимчасово існують на SQL сервері. Тимчасові таблиці корисні для зберігання безпосередніх наборів результатів, до яких звертаються кілька разів. Детальніше про це ви можете прочитати тут https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/ А тут https://codingsight.com/introduction-to-temporary-tables-in -sql-сервер /

Крім того, я настійно рекомендую, якщо ви використовуєте збережену процедуру, встановіть NOCOUNTдля ON, він також може забезпечити значний приріст продуктивності, оскільки мережевий трафік значно знижується:

SET NOCOUNT ON
SELECT *
INTO #Temp
-- The rest of code

Виходячи з цього :

SET NOCOUNT ON - це набір операторів, який забороняє повідомлення, яке показує кількість рядків, на які впливає оператор T-SQL запитів. Це використовується в межах збережених процедур і тригерів, щоб уникнути показу повідомлення про порушені рядки. Використання SET NOCOUNT ON у межах збереженої процедури може значно покращити продуктивність збереженої процедури.


1
Чи можете ви пояснити, чому копіювання цілого tb5до #Tempтаблиці та приєднання до таблиці темп працюють швидше, ніж tb5безпосередньо приєднання ? поверхневі вони містять однакові дані (і #Tempможе бути відсутнім індексом, якщо він був у ньому tb5). Я дійсно не можу зрозуміти, чому це ефективніше (адже я знаю, що це має бути менш ефективно копіювати всі дані та приєднуватися).
зиг

2
@zig Ви маєте рацію в цьому випадку, але що робити, якщо файл tb5знаходиться на іншому сервері? У цьому випадку використання темп-таблиці, безумовно, швидше, ніж пряме приєднання до іншого сервера. Це була лише пропозиція протестувати і побачити, чи щось змінилося. У мене була схожа ситуація в минулому, і, на щастя, таблиця тимчасових тем допомогла ОП також і в цьому випадку.
Салах Акбарі

2

Найкращий підхід - вставити в таблицю змінну / хеш-таблицю (якщо кількість рядків невелика, використовуйте змінну таблиці або використовуйте хеш-таблицю, якщо кількість рядків досить велика). Потім оновіть агрегацію, а потім, нарешті, виберіть зі змінної таблиці або хеш-таблиці. Вивчення плану запитів є необхідним.

DECLARE @MYTABLE TABLE (ID INT, [Title] VARCHAR(500), [Class] VARCHAR(500),
[Current - Last 30 Days Col1] INT, [Current - Last 30 Days Col2] INT,
[Current - Last 90 Days Col1] INT,[Current - Last 90 Days Col2] INT,
[Current - Last 365 Days Col1] INT, [Current - Last 365 Days Col2] INT,
[Last year - Last 30 Days Col1] INT, [Last year - Last 30 Days Col2] INT,
[Last year - Last 90 Days Col1] INT, [Last year - Last 90 Days Col2] INT,
[Last year - Last 365 Days Col1] INT, [Last year - Last 365 Days Col2] INT)



INSERT INTO @MYTABLE(ID, [Title],[Class], 
[Current - Last 30 Days Col1], [Current - Last 30 Days Col2],
[Current - Last 90 Days Col1], [Current - Last 90 Days Col2],
[Current - Last 365 Days Col1], [Current - Last 365 Days Col2],
[Last year - Last 30 Days Col1], [Last year - Last 30 Days Col2],
[Last year - Last 90 Days Col1], [Last year - Last 90 Days Col2],
[Last year - Last 365 Days Col1], [Last year - Last 365 Days Col2]
  )
SELECT    b.id  ,d.[Title] ,e.Class ,0,0,0,0,0,0,0,0,0,0,0,0        
FROM     tb1 a
INNER JOIN   tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN   tb3 c on b.fid = c.col5
INNER JOIN   tb4 d on c.id = d.col6
INNER JOIN  tb5 e on c.col7 = e.id
GROUP BY b.id, d.Title, e.Class

UPDATE T 
SET [Current - Last 30 Days Col1]=K.[Current - Last 30 Days Col1] , 
[Current - Last 30 Days Col2]    =K.[Current - Last 30 Days Col2],
[Current - Last 90 Days Col1]    = K.[Current - Last 90 Days Col1], 
[Current - Last 90 Days Col2]    =K.[Current - Last 90 Days Col2] ,
[Current - Last 365 Days Col1]   =K.[Current - Last 365 Days Col1], 
[Current - Last 365 Days Col2]   =K.[Current - Last 365 Days Col2],
[Last year - Last 30 Days Col1]  =K.[Last year - Last 30 Days Col1],
 [Last year - Last 30 Days Col2] =K.[Last year - Last 30 Days Col2],
[Last year - Last 90 Days Col1]  =K.[Last year - Last 90 Days Col1], 
[Last year - Last 90 Days Col2]  =K.[Last year - Last 90 Days Col2],
[Last year - Last 365 Days Col1] =K.[Last year - Last 365 Days Col1],
 [Last year - Last 365 Days Col2]=K.[Last year - Last 365 Days Col2]
    FROM @MYTABLE T JOIN 
     (
SELECT 
    b.id as [ID]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Current - Last 30 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Current - Last 30 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Current - Last 90 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Current - Last 90 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Current - Last 365 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Current - Last 365 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Last year - Last 30 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Last year - Last 30 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Last year - Last 90 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Last year - Last 90 Days Col2]

    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END),0) as [Last year - Last 365 Days Col1]
    ,ISNULL(Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END),0) as [Last year - Last 365 Days Col2]
    FROM     tb1 a
INNER JOIN   tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN   tb3 c on b.fid = c.col5
INNER JOIN   tb4 d on c.id = d.col6
INNER JOIN  tb5 e on c.col7 = e.id
GROUP BY    b.id
) AS K ON T.ID=K.ID


SELECT *
FROM @MYTABLE

0

Я припускаю, що tb1 - велика таблиця (відносно tb2, tb3, tb4 і tb5).

Якщо так, то тут є сенс обмежити вибір цієї таблиці (із пунктом WHERE).

Якщо використовується лише невелика частина tb1, наприклад, тому що з'єднання з tb2, tb3, tb4 і tb5 зменшують потрібні рядки лише до кількох відсотків, то слід перевірити, чи таблиці індексуються на стовпчики, які ви використовуєте в з'єднаннях. .

Якщо використовується велика частина tb1, то може мати сенс групувати її результати перед тим, як приєднатись до tb2, tb3, tb4 та tb5. Нижче наведено приклад цього.

SELECT 
    b.id as [ID]
    ,d.[Title] as [Title]
    ,e.Class as [Class]
    ,SUM(a.[Current - Last 30 Days Col1]) AS [Current - Last 30 Days Col1]
    ,SUM(a.[Current - Last 30 Days Col2]) AS [Current - Last 30 Days Col2]
    ,SUM(a.[Current - Last 90 Days Col1]) AS [Current - Last 90 Days Col1]
    -- etc.
    FROM (
      SELECT a.id, a.col3

      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]

      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
      ,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]

      FROM  tb1 a
      WHERE a.DateCol >= DATEADD(YEAR,-2,GETDATE())
      GROUP BY a.id, a.col3
    ) AS a
INNER JOIN 
    tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN 
    tb3 c on b.fid = c.col5
INNER JOIN       
    tb4 d on c.id = d.col6
INNER JOIN 
    tb5 e on c.col7 = e.id
GROUP BY
    b.id, d.Title, e.Class

Набагато краще було б спочатку переглянути план виконання, а потім приймати рішення щодо створення індексів та відтворення статистики.
SQL Police

Я дуже ненавиджу, що мій пост отримує негативну оцінку, не пояснюючи чому. Звичайно, я погоджуюся, що, щоб дістати до суті питання про виконання, треба переглянути план виконання. Сказавши це, я відстоююсь від своєї рекомендації щодо перевірки індексів на наявність сторонніх ключів, релевантних у запиті.
Герт-

1
Ви щось «припускаєте», не знаючи. Тож ви надсилаєте відповідь на основі невідомого. Тому downvote. Краще доручити ОП покращити його питання, розмістивши план виконання.
SQL Police

Це ще не все, що я написав. Особисто я б сказав, якщо відповідь є поганою чи неправильною, а не тоді, коли я просто не згоден. Але дякую за відгук.
Герт-

Певним чином це неправильно, бо як ви можете довести це правильно?
SQL Police


0

Для оптимізації таких обчислень ви розглядаєте попереднє обчислення деяких значень. Ідея попередніх обчислень полягає у зменшенні кількості рядків, які потрібно прочитати чи продовжити.

Один із способів досягти цього - використовувати індексований вигляд і залишити двигун робити самі розрахунки. Оскільки цей тип переглядів має деякі обмеження, ви в кінцевому підсумку створюєте просту таблицю і замість цього виконуєте обчислення. В основному, це залежить від потреб бізнесу.

Так, у наведеному нижче прикладі я створюю таблицю RowIDі RowDatetimeстовпці і вставку 1 мільйон рядків. Я використовую індексований вигляд для підрахунку сутностей за добу, тому замість того, щоб запитувати 1 мільйон рядків на рік, я запитую 365 рядків на рік для підрахунку цих показників.

DROP TABLE IF EXISTS [dbo].[DataSource];
GO

CREATE TABLE [dbo].[DataSource]
(
    [RowID] BIGINT IDENTITY(1,1) PRIMARY KEY
   ,[RowDateTime] DATETIME2
);

GO

DROP VIEW IF EXISTS [dbo].[vw_DataSource];
GO

CREATE VIEW [dbo].[vw_DataSource] WITH SCHEMABINDING
AS
SELECT YEAR([RowDateTime]) AS [Year]
      ,MONTH([RowDateTime]) AS [Month]
      ,DAY([RowDateTime]) AS [Day]
      ,COUNT_BIG(*) AS [Count]
FROM [dbo].[DataSource]
GROUP BY YEAR([RowDateTime])
        ,MONTH([RowDateTime])
        ,DAY([RowDateTime]);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_vw_DataSource] ON [dbo].[vw_DataSource]
(
    [Year] ASC,
    [Month] ASC,
    [Day] ASC
);

GO

DECLARE @min bigint, @max bigint
SELECT @Min=1 ,@Max=1000000

INSERT INTO [dbo].[DataSource] ([RowDateTime])
SELECT TOP (@Max-@Min+1) DATEFROMPARTS(2019,  1.0 + floor(12 * RAND(convert(varbinary, newid()))), 1.0 + floor(28 * RAND(convert(varbinary, newid())))          )       
FROM master..spt_values t1 
CROSS JOIN master..spt_values t2

GO


SELECT *
FROM [dbo].[vw_DataSource]


SELECT SUM(CASE WHEN DATEFROMPARTS([Year], [Month], [Day]) >= DATEADD(MONTH,-1,GETDATE()) THEN [Count] ELSE 0 END) as [Current - Last 30 Days Col1]
      ,SUM(CASE WHEN DATEFROMPARTS([Year], [Month], [Day]) >= DATEADD(QUARTER,-1,GETDATE()) THEN [Count] ELSE 0 END) as [Current - Last 90 Days Col1]
      ,SUM(CASE WHEN DATEFROMPARTS([Year], [Month], [Day]) >= DATEADD(YEAR,-1,GETDATE()) THEN [Count] ELSE 0 END) as [Current - Last 365 Days Col1]
FROM [dbo].[vw_DataSource];

Успіх такого рішення дуже залежить від того, як розподіляються дані та скільки у вас рядків. Наприклад, якщо у вас є один запис на день для кожного дня року, представлення та таблиця матимуть однакове збіг рядків, тому операції вводу / виводу не будуть зменшені.

Також вищесказане - лише приклад матеріалізації даних та їх читання. У вашому випадку вам може знадобитися додати більше стовпців визначення перегляду.


0

Я використовую таблицю "Дати" таблиці пошуку, щоб приєднати свої дані до індексу на DatesId. Я використовую дати як фільтр, коли хочу переглядати історичні дані. Об'єднання швидко, і тому це фільтрація, оскільки DatesId є кластеризованим первинним індексом (первинний ключ). Додайте стовпець дати (як додається стовпець) для таблиці даних.

Таблиця дат містить такі стовпці:

DatesId, Дата, Рік, Квартал, РікКварт, Місяць, Число, Ім'я місяця, Короткий, Рік Тижня, Тиждень, Число, День, П'ять, День ОфМісяць, День, NumOfWeek, DayName

Приклад даних: 20310409 2031-04-09 2031 2 2031-Q2 4 квітня квітня 2031_15 15 99 9 3 середа

Ви можете прислати мене, якщо ви хочете отримати цей csv, щоб ви могли імпортувати його до бази даних, але я впевнений, що ви можете легко знайти щось подібне в Інтернеті і зробити своє.

Я також додаю стовпець посвідчення, щоб ви могли отримати ціле число для кожної дати. Це полегшує роботу, але не є вимогою.

SELECT * FROM dbo.dates where dateIndex BETWEEN (getDateIndexDate(getDate())-30 AND getDateIndexDate(getDate())+0) --30 days ago

Це дозволяє мені легко відскочити назад до певного періоду. Створити власні погляди на це досить просто. Звичайно, ви можете використовувати функцію ROW_NUMBER (), щоб зробити це також роками, тижнями тощо.

Після отримання потрібного діапазону дат я приєднуюся до даних. Працює дуже швидко!


0

Оскільки ви завжди групуєте значення на основі цілої кількості місяців, я б спочатку групувався за місяцем у підзапиті у пункті з. Це схоже на використання тимчасової таблиці. Не впевнено, чи це насправді пришвидшить ваш запит.

SELECT f.id, f.[Title], f.Class,
    SUM(CASE WHEN f.MonthDiff = 1 THEN col1 ELSE 0 END) as [Current - Last 30 Days Col1],
    -- etc
FROM (
    SELECT 
        b.id,
        d.[Title],
        e.Class,
        DateDiff(Month, a.DateCol, GETDATE()) as MonthDiff,
        Sum(a.col1) as col1,
        Sum(a.col2) as col2
    FROM  tb1 a
    INNER JOIN tb2 b on a.id = b.fid and a.col3 = b.col4
    INNER JOIN tb3 c on b.fid = c.col5
    INNER JOIN tb4 d on c.id = d.col6
    INNER JOIN tb5 e on c.col7 = e.id
    WHERE a.DateCol between DATEADD(YEAR,-2,GETDATE() and GETDATE()
    GROUP BY b.id, d.Title, e.Class, DateDiff(Month,  a.DateCol, GETDATE())
) f
group by f.id, f.[Title], f.Class

-2

Щоб підвищити швидкість запиту SQL, потрібно додати індекси. Для кожної об'єднаної таблиці потрібно додати один індекс.

Як і цей приклад коду для Oracle:

CREATE INDEX supplier_idx
ON supplier (supplier_name);

це не погана пропозиція. ви бачите з ОП, що створюється тимчасова таблиця без індексу - INNER JOIN #Temp e on c.col7 = e.id. в той час як у відповіді є можливість для вдосконалення, я не думаю, що це слід зволікати масово. особливо для нового користувача.
smoore4

@ smoore4 Погодьтеся, цю опцію заборони без чіткого аргументу слід усунути. Існує велике зловживання цією функціональністю
Greggz
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.