оптимальное сцепление между рядами - PullRequest
1 голос
/ 06 декабря 2010

Я стремлюсь оптимизировать конкатенацию между несколькими строками, и, прочитав несколько похожих вопросов, знаком с использованием пути STUFF + XML и т. Д. Однако, когда я применяю их к моему запросу, он обычно истекает при применении к 9 миллионам строк или около того строк у меня

То, что я ищу, является более эффективным способом перевода этого:

create table #fruit
(
Contact_id NVARCHAR(50)
,fruit_type NVARCHAR(50)
,[2005_orders] int
,[2006_orders] int
,[2007_orders] int
,[2008_orders] int
,[2009_orders] int
)
INSERT INTO #fruit VALUES ('id001','banana',1,3,0,25,4)
INSERT INTO #fruit VALUES ('id001','apple',0,7,19,1,0)
INSERT INTO #fruit VALUES ('id001','orange',0,0,0,9,0)
INSERT INTO #fruit VALUES ('id001','strawberry',1,1,1,1,4)
INSERT INTO #fruit VALUES ('id001','grapes',0,3,0,0,0)
INSERT INTO #fruit VALUES ('id001','lemon',1,1,1,0,0)

В это:

CREATE TABLE #results
(
contact_id NVARCHAR(255)
,fruit_type NVARCHAR(50)
,[2005_orders] int
,[2006_orders] int
,[2007_orders] int
,[2008_orders] int
,[2009_orders] int
,combination2005 NVARCHAR(500)
,combination2006 NVARCHAR(500)
,combination2007 NVARCHAR(500)
,combination2008 NVARCHAR(500)
,combination2009 NVARCHAR(500)
)
INSERT INTO #results VALUES ('id001','banana',1,3,0,25,4,'banana + strawberry + lemon','banana + apple + strawberry + grapes + lemon','apple + strawberry + lemon','banana + apple + orange + strawberry','banana + strawberry')
INSERT INTO #results VALUES ('id001','apple',0,7,19,1,0,'banana + strawberry + lemon','banana + apple + strawberry + grapes + lemon','apple + strawberry + lemon','banana + apple + orange + strawberry','banana + strawberry')
INSERT INTO #results VALUES ('id001','orange',0,0,0,9,0,'banana + strawberry + lemon','banana + apple + strawberry + grapes + lemon','apple + strawberry + lemon','banana + apple + orange + strawberry','banana + strawberry')
INSERT INTO #results VALUES ('id001','strawberry',1,1,1,1,4,'banana + strawberry + lemon','banana + apple + strawberry + grapes + lemon','apple + strawberry + lemon','banana + apple + orange + strawberry','banana + strawberry')
INSERT INTO #results VALUES ('id001','grapes',0,3,0,0,0,'banana + strawberry + lemon','banana + apple + strawberry + grapes + lemon','apple + strawberry + lemon','banana + apple + orange + strawberry','banana + strawberry')
INSERT INTO #results VALUES ('id001','lemon',1,1,1,0,0,'banana + strawberry + lemon','banana + apple + strawberry + grapes + lemon','apple + strawberry + lemon','banana + apple + orange + strawberry','banana + strawberry')

Где ключевые факторы, которые нужно учитывать, это то, что я хочу, чтобы по строке для каждого типа фруктов на контакт (как эта таблица будет использоваться в другом месте), и чтобы я только хотел, чтобы фрукты превращались в тип комбинации, если число больше, чем 0.

Возможно, это никогда не будет очень эффективным, учитывая количество строк, с которыми я имею дело, но если есть шанс, я могу добавить эту информацию в свою таблицу, что было бы здорово:)

Использованные методы

Способ 1)

SELECT *
,STUFF(
(SELECT ' ' + fruit_type
FROM #fruit fr2
WHERE fr.contact_id = fr2.contact_id
AND 2005_orders > 0
order by contact_id,fruit_type
FOR XML path ('')
)
,1,1,''
) AS combination
FROM #fruit fr

Способ 2)

SELECT *
,ISNULL((MAX(CASE WHEN fruit_type = 'banana' AND 2005_orders > 0 THEN 'banana ' END) OVER (PARTITION BY contact_id)),'')+
ISNULL((MAX(CASE WHEN fruit_type = 'apple' AND 2005_orders > 0 THEN 'apple ' END) OVER (PARTITION BY contact_id)),'')+
ISNULL((MAX(CASE WHEN fruit_type = 'orange' AND 2005_orders > 0 THEN 'orange' END) OVER (PARTITION BY contact_id)),'')+
ISNULL((MAX(CASE WHEN fruit_type = 'strawberry' AND 2005_orders > 0 THEN 'strawberry ' END) OVER (PARTITION BY contact_id)),'')+
ISNULL((MAX(CASE WHEN fruit_type = 'grapes' AND 2005_orders > 0 THEN 'grapes ' END) OVER (PARTITION BY contact_id)),'')+
ISNULL((MAX(CASE WHEN fruit_type = 'lemon' AND 2005_orders > 0 THEN 'lemon ' END) OVER (PARTITION BY contact_id)),'')+
AS combination05
FROM #fruit fr

- что затем повторяется в течение 2006-2009 годов (что, я знаю, ужасно неэффективно!)

Ответы [ 3 ]

1 голос
/ 06 декабря 2010

Проблема производительности обоих ваших методов будет подзапросом. Попробуйте эту стратегию, чтобы разбить ее и избежать подзапросов.

Вам не нужно использовать внешние объединения, если вам гарантировано наличие записей для каждой комбинации contact_id / fruit_type.

Индекс для contact_id должен значительно улучшить производительность.

SELECT
 f.*
 , combination2005 = 
     CASE WHEN b.[2005_orders] = 0 OR b.[2005_orders] IS NULL THEN '' ELSE 'banana + ' END
     + CASE WHEN a.[2005_orders] = 0 OR a.[2005_orders] IS NULL THEN '' ELSE 'apple + ' END
     + CASE WHEN o.[2005_orders] = 0 OR o.[2005_orders] IS NULL THEN '' ELSE 'orange + ' END
     + CASE WHEN s.[2005_orders] = 0 OR s.[2005_orders] IS NULL THEN '' ELSE 'strawberry + ' END
  , combination2006 = 
     CASE WHEN b.[2006_orders] = 0 OR b.[2006_orders] IS NULL THEN '' ELSE 'banana + ' END
     + CASE WHEN a.[2006_orders] = 0 OR a.[2006_orders] IS NULL THEN '' ELSE 'apple + ' END
     + CASE WHEN o.[2006_orders] = 0 OR o.[2006_orders] IS NULL THEN '' ELSE 'orange + ' END
     + CASE WHEN s.[2006_orders] = 0 OR s.[2006_orders] IS NULL THEN '' ELSE 'strawberry + ' END
FROM
 #fruit f
LEFT OUTER JOIN
 ( SELECT * FROM #fruit WHERE fruit_type = 'banana' ) b
ON
  f.contact_id = b.contact_id
LEFT OUTER JOIN
 ( SELECT * FROM #fruit WHERE fruit_type = 'apple' ) a
ON
  f.contact_id = a.contact_id
LEFT OUTER JOIN
 ( SELECT * FROM #fruit WHERE fruit_type = 'orange' ) o
ON
  f.contact_id = o.contact_id
LEFT OUTER JOIN
 ( SELECT * FROM #fruit WHERE fruit_type = 'strawberry' ) s
ON
  f.contact_id = s.contact_id

и не используйте "SELECT *", я просто ленивый.

Я должен добавить, что если вы не ожидаете, что у каждого contact_id есть запись для каждого fruit_type (таким образом, вам нужно использовать внешние объединения здесь), то выражения case также должны проверять наличие нуля в дополнение к нулю. (Добавлено, что выше)

0 голосов
/ 09 декабря 2010

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

Я использовал выбранное ниже, чтобы поместить название фрукта вместо значения продаж.

SELECT 
contact_id
,substring([fruit_type],1,cast(CAST([2005_orders] as bit)as int)*50) AS [2005_fruit]
,substring([fruit_type],1,cast(CAST([2006_orders] as bit)as int)*50) AS [2006_fruit]
,substring([fruit_type],1,cast(CAST([2007_orders] as bit)as int)*50) AS [2007_fruit]
,substring([fruit_type],1,cast(CAST([2008_orders] as bit)as int)*50) AS [2008_fruit]
,substring([fruit_type],1,cast(CAST([2009_orders] as bit)as int)*50) AS [2009_fruit]
from #fruit

contact_id                                         2005_fruit                                         2006_fruit                                         2007_fruit                                         2008_fruit                                         2009_fruit
-------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- --------------------------------------------------
id001                                              banana                                             banana                                                                                                banana                                             banana
id001                                                                                                 apple                                              apple                                              apple                                              
id001                                                                                                                                                                                                       orange                                             
id001                                              strawberry                                         strawberry                                         strawberry                                         strawberry                                         strawberry
id001                                                                                                 grapes                                                                                                                                                   
id001                                              lemon                                              lemon                                              lemon                                                                                                 

Я полагаю, что следующим шагом будет поворот выходных данных выбранного конкатенации, соединяющей фрукты. Целью должна быть таблица, которую можно объединить с оригинальной таблицей фруктов. При необходимости таблица может быть включена в таблицу результатов, за исключением случаев, когда существует много избыточных данных, которые убьют ваш ввод-вывод.

0 голосов
/ 06 декабря 2010

Это может быть неуместно / невозможно в вашей среде, но, учитывая, что эти цифры относятся к прошлому, рассматривали ли вы предварительную генерацию объединенной строки для каждого контакта в год в таблицу, используя тот метод, который вы используете в настоящее время, и присоединяясь что к твоим результатам?

Возможно, вам придется использовать запланированное задание для ведения строки текущего года, но маловероятно, что данные за 2005-9 годы изменятся.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...