Любое предложение для оптимизации следующего запроса, который учитывает общие и всех соседей? - PullRequest
2 голосов
/ 22 ноября 2010

Таблица состоит из столбцов Call_party и Call_party, и запись описывает соединение между двумя пользователями, где один играет роль вызывающей стороны, а другой называется вызывающей стороной.

Одни и те же два пользователя могут иметь два соединения - в этом случае роли вызывающей / вызываемой стороны переключаются при изменении направления.

В исходной таблице (month_connections) я добавил дополнительные столбцы common_neighbors и total_neighbors, в которых хранится количество общих и общих соседей. Чтобы уточнить термины common и total_neighbors, я добавил следующее изображение:

alt text

В этом случае для наблюдаемого соединения есть 2 общих соседа вызывающей и вызываемой стороны и 6 общих соседей.

Чтобы получить эти два значения, я написал следующую хранимую процедуру:

CREATE PROCEDURE [dbo].[spCountNeighbors]  

AS

Declare 
@CallingParty varchar(50),
@CalledParty varchar(50),
@RecordsUpdated int

SET @CallingParty ='a'
SET @RecordsUpdated = 0
PRINT GETDATE()
WHILE @CallingParty IS NOT NULL BEGIN
    SET @CallingParty = NULL
    SELECT TOP 1 @CallingParty = calling_party, @CalledParty = called_party FROM    monthly_connections WHERE common_neighbors IS NULL
    --PRINT @CallingParty
    IF @CallingParty IS NOT NULL BEGIN
    WITH callingPartyNeighbors AS
    (
        SELECT called_party as neighbor FROM monthly_connections WHERE calling_party = @CallingParty
        UNION
        SELECT calling_party as neighbor FROM monthly_connections WHERE called_party = @CallingParty
    ),
    calledPartyNeighbors AS
    (
        SELECT calling_party as neighbor FROM monthly_connections WHERE called_party = @CalledParty
        UNION
        SELECT called_party as neighbor FROM monthly_connections WHERE calling_party = @CalledParty
    )

        UPDATE mc SET common_neighbors = (SELECT COUNT (*) FROM
        (
        SELECT neighbor FROM callingPartyNeighbors
        INTERSECT
        SELECT neighbor FROM calledPartyNeighbors
        )
        t1
        ),
        total_neighbors = (SELECT COUNT (*) FROM
        (
        SELECT neighbor FROM callingPartyNeighbors
        UNION
        SELECT neighbor FROM calledPartyNeighbors
        )
        t2
        )
         FROM monthly_connections mc WHERE (mc.calling_party = @CallingParty AND mc.called_party = @CalledParty) OR (mc.called_party = @CallingParty AND mc.calling_party = @CalledParty);
        SET @RecordsUpdated = @RecordsUpdated + @@ROWCOUNT
        PRINT @RecordsUpdated
    END 
END
PRINT @RecordsUpdated

Предполагается, что описанная выше процедура проходит через таблицу соединений, которая содержит 23M соединений, и обновляет значения common_neighbors и total_neighbors для каждой строки. Проблема, однако, в том, что процедура слишком медленная - для обновления 1000 записей потребовалось 212 с.

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

Спасибо!

Ответы [ 2 ]

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

Следующий скрипт выдает тот же вывод для common_neighbors, что и ваша хранимая процедура.

Почему-то у меня такое ощущение, что это не совсем (пока) то, что вам нужно, но вы можете поднять его для новых идей.

DECLARE @monthly_connections TABLE (
  calling_party VARCHAR(50)
  , called_party VARCHAR(50)
  , common_neighbors INTEGER
  , total_neighbors INTEGER)

INSERT INTO @monthly_connections
          SELECT '1', '3', NULL, NULL
UNION ALL SELECT '2', '4', NULL, NULL
UNION ALL SELECT '3', '2', NULL, NULL
UNION ALL SELECT '3', '4', NULL, NULL
UNION ALL SELECT '3', '6', NULL, NULL
UNION ALL SELECT '3', '7', NULL, NULL
UNION ALL SELECT '4', '5', NULL, NULL
UNION ALL SELECT '8', '4', NULL, NULL

;WITH q AS (
  SELECT  calling_party, called_party
  FROM    @monthly_connections mc1
  UNION ALL
  SELECT  called_party, calling_party
  FROM    @monthly_connections mc1
)
UPDATE  @monthly_connections
SET     common_neighbors = common_neighbors.cnt
FROM    @monthly_connections mc
        INNER JOIN (
          SELECT  q1.calling_party, q1.called_party, cnt = COUNT(*) 
          FROM    q q1
                  INNER JOIN q q2 ON q2.calling_party = q1.called_party                          
                  INNER JOIN q q3 ON q3.calling_party = q2.called_party
                                     AND q3.called_party = q1.calling_party
          GROUP BY
                  q1.calling_party, q1.called_party
        ) common_neighbors ON common_neighbors.calling_party = mc.calling_party
                              AND common_neighbors.called_party = mc.called_party


SELECT *
FROM  @monthly_connections        
0 голосов
/ 22 ноября 2010

В вашей процедуре вы выполняете много подзапросов, которые, я полагаю, являются основным источником вашей потери производительности. Разве вы не можете просто заменить множественный запрос большим соединением, а затем отфильтровать его? Что-то вроде

SELECT T.calling_party, T.called_party, A.called_party, B.called_party
from table T
join table as A
on T.calling_party = A.calling_party
join table as B
on T.calling_party = B.calling_party
where A.called_party = B.called_party --to get the commong neighbour 

Вам, вероятно, понадобится еще одно объединение в call_party, чтобы получить полный список, однако я думаю, что это может быть быстрее, чем перебирать записи 23M и вызывать несколько запросов для всех них.

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