2015-03-13 5 views
0

У меня возникают проблемы с производительностью при выполнении относительно простого запроса. Ситуация выглядит следующим образом:SQL Server 2008 R2: оптимизация производительности запросов

У меня есть таблица, позволяет назвать его [Old_Table], который устанавливается следующим образом:

Document ID | Parsed_Codes 
-------------+------------- 
Document_1 | a 
Document_1 | b 
Document_1 | c 
Document_2 | a 
Document_2 | d 
Document_3 | a 
Document_3 | c 

Эта таблица имеет в общей сложности 2,5 миллиона строк и примерно 500k уникальных [Document ID] значений.

То, что я хочу сделать, это агрегированный эту таблицу в новую таблицу с именем [New_Table] который должен выглядеть следующим образом:

Document ID | New_Parsed_Codes 
-------------+----------------- 
Document_1 | a; b; c 
Document_2 | a; d 
Document_3 | a; c 

Для того, чтобы сделать это, я создал следующий запрос:

SELECT 
    t1.[Document ID] as [Document ID], 
    Stuff((SELECT '; ' + CONVERT(NVARCHAR(max), Parsed_Codes) 
      FROM dbo.[Old_Table] t2 
      WHERE t2.[Document ID] = t1.[Document ID] 
      FOR XML PATH('')), 1, 2, '') as [New_Parsed_Codes] 
INTO dbo.[New_Table] 
FROM dbo.[Old_Table] t1 
GROUP BY t1.[Document ID] 

Проблема в том, что эти цифры не слишком велики, но запрос легко занимает от 16 до 32 часов. Машина, на которой я работаю, имеет 120 ГБ оперативной памяти и 24 ядра.

Вопрос сейчас; есть ли способ изменить запрос, чтобы сделать его более эффективным. Или, может быть, существует другой метод все вместе.

+0

Насколько велика таблица «байт-мудрый»? (например, результат столбца данных sp_spaceused). Из примера «вывести» мы говорим о 2,5 М строк порядка 40 байт каждый, давая менее 100 МБ (релевантных) данных. Я предполагаю, что вы упростили пример? – deroby

ответ

0

Другой способ выполнения group_concat использует cross apply вместо correlated subquery. Попробуй это.

SELECT t1.[Document ID] AS [Document ID], 
LEFT(cs.New_Parsed_Codes, Len(cs.New_Parsed_Codes) - 1) AS New_Parsed_Codes 
FROM Old_Table t1 
     CROSS APPLY (SELECT '; ' + CONVERT(NVARCHAR(max), Parsed_Codes) 
        FROM dbo.[Old_Table] t2 
        WHERE t2.[Document ID] = t1.[Document ID] 
        FOR XML PATH('')) cs ([New_Parsed_Codes]) 
GROUP BY t1.[Document ID], 
      LEFT(cs.New_Parsed_Codes, Len(cs.New_Parsed_Codes) - 1) 
+0

Спасибо. В настоящее время я запускаю запрос и сообщим вам, улучшила ли производительность. –

0

Поскольку число участвующих (2,5М строк, казалось бы, небольших записей) звучат не слишком огромна и машина описал казалось впечатляющим, я задавался вопросом, как плохо это будет работать на моем ноутбуке. Таким образом, я создал этот тест, который 'имитирует' вопрос:

IF DB_ID('test') IS NULL CREATE DATABASE test 
GO 
USE test 
GO 

SET NOCOUNT ON 

PRINT Convert(varchar, current_timestamp, 113) + ' - starting up, creating t_old_table...' 

IF OBJECT_ID('t_old_table') IS NOT NULL DROP TABLE t_old_table 

GO 
CREATE TABLE t_old_table (row_id int IDENTITY(1, 1) PRIMARY KEY, 
          document_id nvarchar(50) NOT NULL, 
          parsed_codes nvarchar(50) NOT NULL) 
GO 

PRINT Convert(varchar, current_timestamp, 113) + ' - starting up, creating docuemnt_id''s...' 

-- create unique document_id's first 
DECLARE @counter int = 1, 
     @target int = 500000, 
     @block int = 10000 

INSERT t_old_table (document_id, parsed_codes) VALUES (Reverse(Convert(nvarchar(50), NewID())), Convert(nvarchar(50), BINARY_CHECKSUM(NewID()))) 

WHILE @counter < @target 
    BEGIN 
     INSERT t_old_table (document_id, parsed_codes) 
     SELECT TOP (CASE WHEN @counter + @block > @target THEN @target - @counter ELSE @block END) 
       Reverse(Convert(nvarchar(50), NewID())), 
       Convert(nvarchar(50), BINARY_CHECKSUM(NewID())) 
      FROM t_old_table 

     SELECT @counter = @counter + @@ROWCOUNT 
    END 

PRINT Convert(varchar, current_timestamp, 113) + ' - starting up, adding parsed codes...' 

-- add parsed-codes to existing document_id's 
SELECT @target = @target * 5 

WHILE @counter < @target 
    BEGIN 
     INSERT t_old_table (document_id, parsed_codes) 
     SELECT TOP (CASE WHEN @counter + @block > @target THEN @target - @counter ELSE @block END) 
       document_id, 
       Convert(nvarchar(50), BINARY_CHECKSUM(NewID())) 
      FROM t_old_table 
     ORDER BY NewID() -- some document_id's will have more, some will have less 

     SELECT @counter = @counter + @@ROWCOUNT 
    END 

UPDATE STATISTICS t_old_table 

PRINT Convert(varchar, current_timestamp, 113) + ' - Creating t_new_table...' 

GO 
IF OBJECT_ID('t_new_table') IS NOT NULL DROP TABLE t_new_table 
GO 
CREATE TABLE t_new_table (document_id nvarchar(50) NOT NULL PRIMARY KEY, 
          parsed_codes_list nvarchar(max) NOT NULL) 

GO 

Запуск этого потребовалось около 6 минут на мой (т) ноутбук ржавый i5

  • 13 Mar +2015 22: 20: 09: 053 - запуск, t_old_table ...
  • 13 марта 2015 г. 22: 20: 09: 073 - запуск, создание документа ...
  • 13 марта 2015 г. 22: 20: 13: 273 - запуск, добавление разобранных кодов ...
  • 13 мар 2015 22: 26: 27: 023 - Создание t_new_tabl д ...

Далее я принял следующий подход:

PRINT Convert(varchar, current_timestamp, 113) + ' - Creating #numbered table...' 

-- step 1, make temp-table that holds 'correct' order 
IF OBJECT_ID('tempdb..#numbered') IS NOT NULL DROP TABLE #numbered 
GO 
SELECT document_id, 
     parsed_codes, 
     order_nbr = ROW_NUMBER() OVER (PARTITION BY document_id ORDER BY parsed_codes) 
    INTO #numbered 
    FROM t_old_table 
WHERE 1 = 2 

CREATE UNIQUE CLUSTERED INDEX uq0 ON #numbered (order_nbr, document_id) 

INSERT #numbered 
SELECT document_id, 
     parsed_codes, 
     order_nbr = ROW_NUMBER() OVER (PARTITION BY document_id ORDER BY parsed_codes) 
    FROM t_old_table 

GO 
-- extract parsed codes 
DECLARE @nbr int = 1, 
     @rowcount int 

SET NOCOUNT OFF 
PRINT Convert(varchar, current_timestamp, 113) + ' - Converting to t_new_table, step ' + convert(varchar, @nbr) + '...' 

INSERT t_new_table (document_id, parsed_codes_list) 
SELECT document_id, parsed_codes 
    FROM #numbered 
WHERE order_nbr = @nbr 

SELECT @rowcount = @@ROWCOUNT 

UPDATE STATISTICS t_new_table 

WHILE @rowcount > 0 
    BEGIN 
     SELECT @nbr = @nbr + 1 

     PRINT Convert(varchar, current_timestamp, 113) + ' - Converting to t_new_table, step ' + convert(varchar, @nbr) + '...' 

     UPDATE t_new_table 
      SET parsed_codes_list = parsed_codes_list + ';' + n.parsed_codes 
      FROM t_new_table 
      JOIN #numbered n 
      ON n.document_id = t_new_table.document_id 
      AND n.order_nbr = @nbr 

     SELECT @rowcount = @@ROWCOUNT 
    END 

GO 

-- all done 
PRINT Convert(varchar, current_timestamp, 113) + ' - All done.' 

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

  • 13 Mar 2015 22: 26: 27: 030 - Создание #numbered таблицы ...
  • 13 марта 2015 22: 26: 41: 307 - Преобразование t_new_table, шаг 1 ...
  • (Затронуто 500000 строк)
  • 13 Mar 2015 22: 26: 45: 543 - Преобразование в t_new_table, шаг 2 ...
  • (400986 строк (ы) пострадавших)
  • 13 марта 2015 22: 26: 49: 863 - Преобразование в t_new_table, шаг 3 ...
  • (+322042 строку (ы) пострадавших)
  • [. ..]
  • 13 марта 2015 22: 27: 15: 713 - Преобразование в t_new_table, шаг 62 ...
  • (1 строку (ы) пострадавших)
  • 13 марта 2015 22: 27: 15: 900 - Преобразование в t_new_table, этап 63 ...
  • (0 ряд (-ых) затронутых)
  • 13 марта 2015 г. 22: 27: 15: 940 - Все сделано.

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

SELECT 
    t1.document_id as document_id, 
    Stuff((SELECT '; ' + CONVERT(NVARCHAR(max), parsed_codes) 
      FROM t_old_table t2 
      WHERE t2.document_id = t1.document_id 
      FOR XML PATH('')), 1, 2, '') as [New_parsed_codes] 
INTO dbo.[New_Table] 
FROM t_old_table t1 
GROUP BY t1.document_id 

и он побежал за 40 секунд.

Это заставляет меня поверить, что вам нужно будет объяснить немного дальше ситуацию (размер имеет значение =), поэтому мы можем лучше понять, где тратится время; или у вас есть проблема с оборудованием ...

Смежные вопросы