2010-11-22 3 views
2

Die Tabelle besteht aus den Spalten Calling_party und Called_party und der Datensatz beschreibt die Verbindung zwischen zwei Benutzern, wobei einer die Rolle des rufenden Teilnehmers spielt und der andere Teilnehmer heißt.Irgendwelche Vorschläge zur Optimierung der folgenden Abfrage, die gemeinsam und alle Nachbarn zählt?

Die gleichen zwei Benutzer können zwei Verbindungen haben - in diesem Fall werden die Rollen rufender/angerufener Teilnehmer umgeschaltet, wenn die Richtung geändert wird.

In der ursprünglichen Tabelle (Monatsverbindungen) habe ich zusätzliche Spalten common_neighbors und total_neighbors hinzugefügt, in denen die Anzahl der gemeinsamen und gesamten Nachbarn gespeichert sind. Um zu klären, werden die Begriffe häufig und total_neighbors Ich habe folgendes Bild:

alt text

In diesem Fall für die beobachtete Verbindung gibt es zwei gemeinsame Nachbarn des Anrufers und Angerufenen und insgesamt 6 Nachbarn.

Um diese beiden Werte zu erhalten, schrieb ich die folgende gespeicherte Prozedur:

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 

Das Verfahren oben soll durch die Tabelle von Verbindungen gehen, die für jede Zeile 23M Verbindungen und Aktualisierungswerte common_neighbors und total_neighbors enthält . Das Problem ist jedoch, dass die Prozedur zu langsam ist - es dauerte 212 s, um 1000 Datensätze zu aktualisieren.

Ich würde mich sehr freuen, wenn jemand von Ihnen eine Lösung für das obige Verfahren vorschlagen würde, um die Ausführungszeit zu beschleunigen.

Vielen Dank!

Antwort

0

Das folgende Skript erzeugt dieselbe Ausgabe für die common_neighbors wie Ihre gespeicherte Prozedur.

Irgendwie habe ich das Gefühl, es ist nicht genau (noch) was Sie brauchen, aber Sie könnten es für einige neue Ideen abholen.

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

In Ihrer Prozedur machen Sie eine Menge Unterabfragen, was die Hauptursache für Ihren Leistungsverlust ist. Kannst du die multiple Abfrage nicht einfach durch einen großen Join ersetzen und dann filtern? So etwas wie

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 

werden Sie wahrscheinlich eine andere brauchen beitreten auf der called_party die vollständige Liste zu bekommen, aber ich denke, das könnte schneller sein als 23M Datensätze durchlaufen und mehrere Abfragen für alle von ihnen fordern.

Verwandte Themen