Frage

Ich habe eine Tabelle (nennen Sie es OldTable) mit Spalten wie SO:

ID (int), Rang (int), textLineNumber (int), irgendwann (varchar)

Der PrimaryKey ist mehrteilig: ID+Rank+TextLinEnumber.

Ich versuche, es mit Spalten wie SO in eine andere Tabelle zu verwandeln (nennen Sie es Newtable):

ID (int), Rang (int), kombinierter text (varchar)

Und der Hauptschlüssel wäre ID+Rang.

ID und Rang in der neuen Tabelle sind bereits besiedelt, aber ich brauche eine Abfrage, die die kombinierte Spalte der Newtable mit den folgenden Überlegungen aktualisiert:

  1. Der Rang auf der neuen Tabelle gibt möglicherweise nicht in der alten Tabelle. In diesem Fall muss er den höchsten verfügbaren Rang aus der alten Tabelle auswählen, die nicht größer als der Rang auf der neuen Tabelle ist.
  2. Die kombinierte Spalte ist eine String -Verkettung der Spalte "Somext" aus der alten Tabelle, die in der Reihenfolge der "textLinEnumber" unter Verwendung des aus der ersten Überlegung gefundenen Ranges verkettet wurde.

Hier sind einige Beispieldaten:

alt-http://i54.tinypic.com/jq0vmx.png

Neu-http://i53.tinypic.com/dhfyn8.png

Ich benutze MSSQL 2005, wenn das wichtig ist. Ich mache dies derzeit mit T-SQL und während der Schleifen, aber es ist zu einem ernsthaften Flaschenhals der Performance (dauert ungefähr 1 Minute für 10000 Zeilen).

Bearbeiten: Erweiterte Beispieldaten in CSV:
Alt:

ID,Rank,LineNumber,SomeText
1,1,1,the qu  
1,1,2,ick br  
1,1,3,own  
1,2,1,some te  
1,2,2,xt  
1,3,1,sample  
2,7,1,jumped ov  
2,7,2,er the  
2,7,3,lazy  
2,13,1,samp  
2,13,2,le text  
3,1,1,ABC  
3,1,2,DEF  
3,1,3,GHI  
3,1,4,JKL  
3,50,1,XYZ

Neu:

ID,Rank,CombinedText
1,2,some text
2,13,sample text
2,14,sample text
3,4,ABCDEFGHIJKL
3,5,ABCDEFGHIJKL
3,50,XYZ
3,55,XYZ

edit2:
Hier ist eine Beispielabfrage, die ich gefunden habe, die funktioniert, aber nicht schnell genug ist (stützt sich auf mehrere Untergrenze):

update newtable set combinedtext = 
coalesce ((select top 1 sometext from OldTable where OldTable.id=newtable.id and oldtable.rank=(select top 1 rank from oldtable where oldtable.id=newtable.id and oldtable.rank<=newtable.rank order by rank desc) and oldtable.linenumber=1),'') +
coalesce ((select top 1 sometext from OldTable where OldTable.id=newtable.id and oldtable.rank=(select top 1 rank from oldtable where oldtable.id=newtable.id and oldtable.rank<=newtable.rank order by rank desc) and oldtable.linenumber=2),'') +
coalesce ((select top 1 sometext from OldTable where OldTable.id=newtable.id and oldtable.rank=(select top 1 rank from oldtable where oldtable.id=newtable.id and oldtable.rank<=newtable.rank order by rank desc) and oldtable.linenumber=3),'') +
coalesce ((select top 1 sometext from OldTable where OldTable.id=newtable.id and oldtable.rank=(select top 1 rank from oldtable where oldtable.id=newtable.id and oldtable.rank<=newtable.rank order by rank desc) and oldtable.linenumber=4),'') +
coalesce ((select top 1 sometext from OldTable where OldTable.id=newtable.id and oldtable.rank=(select top 1 rank from oldtable where oldtable.id=newtable.id and oldtable.rank<=newtable.rank order by rank desc) and oldtable.linenumber=5),'')

Es setzt auch eine maximale Zeilenzahl von 5 an, die möglicherweise nicht der Fall ist. Ich habe nichts dagegen, die Leinenzahlen im ganzen Weg zu maximal 20 zu kodieren, wenn es so ist, aber im Idealfall könnte es in der Lage sein, sie anders zu erklären. Die Ausführungszeit unter 20 Sekunden (die tatsächlichen Daten) zu erhalten ist das Ziel ...

War es hilfreich?

Lösung

Dies sollte funktionieren, ich werde es später aufräumen, damit es effizienter ist.

DECLARE @Old TABLE ( 
  id         INT, 
  rank       INT, 
  linenumber INT, 
  sometext   VARCHAR(1000)) 
DECLARE @New TABLE ( 
  id           INT, 
  rank         INT, 
  combinedtext VARCHAR(1000)) 


;WITH combinedresults(ctid, id, rank, linenumber, combinedtext) 
     AS (SELECT 0, 
                id, 
                rank, 
                linenumber, 
                CAST (sometext AS VARCHAR(8000)) 
         FROM   @Old o 
         WHERE  NOT EXISTS (SELECT TOP 1 1 
                            FROM   @Old 
                            WHERE  id = o.id 
                                   AND rank = o.rank 
                                   AND linenumber < o.linenumber) 
         UNION ALL 
         SELECT ctid + 1, 
                o.id, 
                o.rank, 
                o.linenumber, 
                ct.combinedtext + o.sometext 
         FROM   @Old o 
                INNER JOIN combinedresults ct 
                  ON ct.id = o.id 
                     AND ct.rank = o.rank 
         WHERE  o.linenumber > ct.linenumber) 

UPDATE n 
SET    combinedtext = ct.combinedtext 
FROM   @New n 
       INNER JOIN (SELECT n.id, 
                          n.rank, 
                          MAX(o.rank) orank 
                   FROM   @new n 
                          INNER JOIN @Old o 
                            ON n.id = o.id 
                               AND o.rank <= n.rank 
                   GROUP  BY n.id, 
                             n.rank) r 
         ON n.id = r.id 
            AND n.rank = r.rank 
       INNER JOIN (SELECT id, 
                          ct.rank, 
                          MAX(ctid) ctid 
                   FROM   combinedresults ct 
                   GROUP  BY ct.id, 
                             ct.rank) r2 
         ON r2.id = r.id 
            AND r2.rank = r.orank 
       INNER JOIN combinedresults ct 
         ON r.id = ct.id 
            AND ct.rank = r.orank 
            AND ct.ctid = r2.ctid 

SELECT * 
FROM   @New 

Andere Tipps

Sie können eine Funktion erstellen, die die Werte mit einem Cursor innerhalb der Funktion zusammenstößt, aber das geht aus Ihrer einzigen Option. Sie müssen eine Reihe durch Zeilenverarbeitung durchführen, um dies zu erreichen.

Ich bin noch nicht gut mit CTE, also hier ist meine Meinung zur Frage, ob ein traditionellerer Ansatz ohne Cursors verwendet wird.

Anforderung Nr. 2 erinnert mich an ein Projekt, an dem ich gearbeitet habe, an dem eine von der Kommas getrennte Verkettung der Werte in einer von einer Kategorie gruppierten Spalte erstellt werden musste. Die Lösung, die ich verwendet habe, erforderte eine UDF, um die verkettete Zeichenfolge unter Verwendung der angegebenen Kategorie -ID zu erstellen.

Unten finden Sie die angepassten UDF mit ID- und Rangparametern:

CREATE FUNCTION fnCombinedText
(
    @ID int,
    @Rank int
)
RETURNS varchar(MAX)
AS
BEGIN

DECLARE @CombinedText varchar(MAX)
SET @CombinedText = ''

SELECT @CombinedText = @CombinedText + SomeText
FROM oldTable
WHERE [ID] = @ID 
AND [Rank] = @Rank
ORDER BY [Rank]

RETURN @CombinedText

END

Anforderung Nr. 1 kann erreicht werden, indem Sie den Newtable -Rang mit allen unterschiedlichen oder weniger alten Ranglisten anschließen und das Beste/Top -Spiel erhalten:

CREATE TABLE #RankMap
(
    newID int,
    newRank int,
    oldID int,
    oldRank int
)
INSERT INTO #RankMap
SELECT newID, newRank, oldID, oldRank
FROM
(
    SELECT
        n.id AS newID,
        n.rank AS newRank,
        o.id AS oldID,
        o.rank as oldRank,
        RANK() OVER(PARTITION BY n.rank ORDER BY o.rank DESC) AS topRank
    FROM
        newtable AS n 
        LEFT OUTER JOIN (SELECT DISTINCT id, rank FROM oldtable) AS o
            ON n.id = o.id
            AND n.rank >= o.rank
) AS matchEqualLess
WHERE topRank = 1

Jetzt, da wir die kartierten OldTable -Rang haben, können wir die UDF verwenden, um die kombinierten Texten zu generieren:

SELECT
    newID,
    newRank,
    dbo.fnCombinedText(oldID, oldRank) AS CombinedText
FROM #RankMap

--Below is the resultset:
newID       newRank     CombinedText
----------- ----------- --------------------
1           2           some text
3           4           ABCDEFGHIJKL
3           5           ABCDEFGHIJKL
2           13          sample text
2           14          sample text
3           50          XYZ
3           55          XYZ

Der Hauptnachteil für diese Lösung besteht darin, dass jeder Aufruf zum UDF fncomBInedText () im Wesentlichen ein separater Auswahl für den alten Table ist. Ich wette, dieser Ansatz kann auf eine skalierbarere CTE -Abfrage portiert werden. Und ich denke, ich sollte auch wirklich zum Beherrschen von CTE.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top