J'ai une table (appelez-le oldtable) avec des colonnes comme si:
ID (INT), grade (int), textlinenumber (int), témoin (varchar)
Le primaire est en plusieurs parties: ID + rang + textlinénumber.
J'essaie de transformer/de le rejoindre dans une autre table (appelez-la nouvelle) avec des colonnes comme:
ID (int), rang (int), combinedText (varchar)
et la clé principale serait ID + rang.
ID et classement sur la nouvelle table sont déjà peuplés, mais j'ai besoin d'une requête qui mettrait à jour la colonne combinéeText de la nouvelle carte avec les considérations suivantes:
Voici quelques exemples de données:
old- http://i54.tinypic.com/jq0vmx.png
nOUVEAU- http://i53.tinyypic.com/dhfyn8.png
J'utilise MSSQL 2005 si cela importe. Je fais actuellement cela en utilisant T-SQL et, tandis que des boucles, mais c'est devenu un goulot de bouteille de performance grave (prenant environ 1 minute pour 10000 rangées).
EDIT: Exemple expansé Données dans CSV:
Vieille:
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
Nouvelle:
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:
[.____] Voici un exemple de requête que j'ai trouvé cela fonctionne mais n'est pas assez rapide (en s'appuyant sur plusieurs sous-requêtes):
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),'')
Cela suppose également un numéro de ligne maximum de 5 qui peut ne pas être le cas. Je ne me dérange pas de coder les linenumbers de la manière d'un maximum de 20 si c'est ce qu'il faut, mais idéalement, il serait capable de les rendre compte différemment. Obtenir une heure d'exécution inférieure à 20 secondes (les données réelles) est l'objectif ...
Cela devrait fonctionner, je vais le nettoyer plus tard, donc c'est plus efficace.
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
Vous pouvez créer une fonction qui stingse les valeurs ensemble à l'aide d'un curseur dans la fonction, mais c'est à propos de votre seule option. Vous aurez besoin de faire une ligne par traitement de ligne pour que cela se produise.
Je ne suis pas bon avec CTE encore, alors voici ma prise sur la question en utilisant une approche plus traditionnelle sans curseurs.
L'exigence n ° 2 me rappelle un projet que j'ai travaillé à ce que la création d'une concaténation séparée par des virgules des valeurs sur une colonne regroupée par certaines catégories. La solution que j'ai utilisée nécessitait un UDF pour produire la chaîne concaténée à l'aide de l'ID de catégorie fournie.
Vous trouverez ci-dessous l'UDF adapté à l'aide des paramètres ID et de rang:
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
L'exigence n ° 1 peut être accomplie en reliant les classes de la nouvelle carte avec tous les rangs distincts égaux ou moins d'OldTable et à obtenir le meilleur match:
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
Maintenant que nous avons les rangs d'OldTable mappés, nous pouvons utiliser l'UDF pour générer les 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
L'inconvénient principal de cette solution est que chaque appel à l'UDF FnCombineDtext () est essentiellement une sélection séparée sur l'ancienne. Je parie que cette approche peut être portée à une requête CTE plus évolutive. Et je suppose que je devrais vraiment me contourner à maîtriser CTE.