Domanda

Ho una tabella (chiamarlo OldTable) con colonne in questo modo:

ID (int), Rango (int), TextLineNumber (int), someText (varchar)

Il PrimaryKey è multi-parte:. ID + Grado + TextLineNumber

Sto cercando di trasformare / unirsi in un'altra tabella (lo chiamano NewTable) con colonne in questo modo:

ID (int), Rango (int), CombinedText (varchar)

e la chiave primaria sarebbero ID + Grado.

ID e Grado sulla nuova tabella sono già popolati, ma ho bisogno di una domanda che avrebbe aggiornare la colonna CombinedText del NewTable con le seguenti considerazioni:

  1. La Classifica dato sulla nuova tabella non può esistere sul vecchio tavolo, nel qual caso deve scegliere il grado più alto a disposizione dalla vecchia tabella che non è maggiore il rango nella nuova tabella.
  2. La colonna CombinedText è una concatenazione di stringhe della colonna "someText" dalla vecchia tabella, concatenato al fine di "TextLineNumber" utilizzando Rank trovato dalla prima considerazione.

Ecco alcuni dati esempio:

vecchiaia http://i54.tinypic.com/jq0vmx.png

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

Sto usando MSSql 2005, se quello che conta. Io attualmente faccio utilizzando T-SQL e while, ma è diventato un collo di bottiglia delle prestazioni gravi (prendendo circa 1 minuto per 10000 righe).

Modifica: Expanded esempio i dati in formato CSV:
Vecchio:

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

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:
Ecco un esempio di query che ho scoperto che funziona, ma non è abbastanza veloce (basandosi su molteplici sub-query):

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),'')

Si assume anche un numero di riga massimo di 5 che non può essere il caso. Non mi dispiace codificare i LINENUMBERS in tutta la strada ad un massimo di 20, se questo è quello che ci vuole, ma idealmente sarebbe in grado di conto per loro in modo diverso. Ottenere il tempo di esecuzione meno di 20 secondi (i dati reali) è l'obiettivo ...

È stato utile?

Soluzione

Questo dovrebbe funzionare, io ripulire poi così il suo più efficiente.

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 

Altri suggerimenti

È possibile creare una funzione che punge i valori insieme utilizzando un cursore all'interno della funzione, ma che su l'unica opzione. Avrai bisogno di fare riga per l'elaborazione di fila per rendere questo accada.

Non sono bravo con CTE ancora, ecco il mio prendere sulla questione con un approccio più tradizionale, senza cursori.

Il requisito # 2 mi ricorda di un progetto ho lavorato che ha richiesto la produzione di una concatenazione separati da virgole dei valori su una colonna raggruppate per qualche categoria. La soluzione che ho usato ha richiesto un UDF per produrre la stringa concatenata utilizzando la categoria id fornito.

Di seguito il atta UDF utilizzando parametri Classifica ID e:

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

Il requisito # 1 può essere realizzato partecipa alla NewTable Rank con tutti uguali distinti o meno OldTable Rank e ottenere la migliore corrispondenza / top:

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

Ora che abbiamo la mappatura del OldTable Rank, possiamo usare l'UDF per generare del CombinedText:

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

Lo svantaggio principale di questa soluzione è che ogni chiamata al FSU fnCombinedText () è essenzialmente un SELECT separato sul OldTable. Scommetto che questo approccio può essere portato su una query CTE più scalabile. E io che dovrei veramente andare in giro a padroneggiare CTE, anche.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top