8
votes

Comment puis-je "fusionner", "aplatit" ou "pivot" résultant d'une requête qui retourne plusieurs rangées en un seul résultat?

J'ai une simple requête sur une table, qui renvoie des résultats comme les suivants:

id    concatenation 
2702  5,16,17,40:31,14,3,1
2703  23,23,34:4,5,6
2704  1:14


1 commentaires

Édité les balises Un bit "pivot" est le terme le plus couramment utilisé ici, pourrait obtenir les personnes appropriées en regardant la question si vous avez mis à jour pour l'utiliser, en particulier dans le titre.


7 Réponses :


1
votes

Voici une solution: xxx

update: une autre approche xxx


5 commentaires

Quelle version de Sybase utilisez-vous? Je ne peux pas sembler avoir "pour XML ..." pour travailler. Ma version est ASE 12.5.4


Quant à la deuxième solution, n'est-ce pas l'oracle?


J'ai fait ces solutions avec MSSQL, mais la seconde devrait travailler avec Sybase, autant que j'ai trouvé qu'il soutient des expressions de table communes


@Oleg, je pense que nous sommes tombés dans les anciennes "extensions incompatibles" ici :)


@dsm, peut-être, désolé si je vous ai mal guidé.



1
votes

Le meilleur que je puisse penser maintenant est le prochain: xxx

mais le résultat est un peu différent de celui que vous avez dactylographié ... !!! >


3 commentaires

Il est trop simple, mais il n'a pas besoin d'sql supplémentaire et difficile de codage


Veuillez formater votre réponse en utilisant le bloc "Code" pour une lecture plus facile.


J'ai essayé de reformater votre code sans chance. Votre solution fonctionne effectivement pour cet exemple particulier, mais supposons maintenant que nous voulons de la même manière pour plusieurs identifiants ou répétez ID_TYPES



2
votes

Une autre approche qui fonctionne sur Sybase ASE 12.5.4. La table doit avoir un indice en cluster sur ID, afin que cela fonctionne. En supposant que le nom de la table soit Mytable:

declare @strNew varchar(10), @strOld varchar(10), @str1 varchar(1000), @str2 varchar(1000)
set @str1 = NULL, @str2 = NULL, @strNew = NULL, @strOld = NULL

UPDATE MYTABLE
SET @strNew = convert(varchar,id) 
, @str1 = case when @strNew = @strOld then @str1 + convert(varchar,id_type) + "," else @str1 +  '$' + @strNew + '$' + convert(varchar,id_type) + "," end  
, @str2 = case when @strNew = @strOld then @str2 + convert(varchar,id_ref) + "," else @str2 + '$' + @strNew + '$' + convert(varchar,id_ref) + "," end
, @strOld = convert(varchar,id) 


select id, substring(@str1,charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$"),
case when
    charindex(",$",substring(@str1,charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$") + 1,len(@str1)))
    = 0 then len(@str1) - (charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$"))
else
    charindex(",$",substring(@str1,charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$") + 1,len(@str1)))
end
) 
+ ':' + 
substring(@str2,charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$"),
case when 
    charindex(",$",substring(@str2,charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$") + 1,len(@str2)))
    = 0 then len(@str2) - (charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$"))
else
    charindex(",$",substring(@str2,charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$") + 1,len(@str2)))
end
) as concatenation
from MYTABLE 
group by id


3 commentaires

@DSM ajoutez les éléments suivants après la déclaration de mise à jour pour voir les chaînes qu'il crée: impression "% 1!:% 2!", @ str1, @ str2 . L'instruction SELECT qui suit, extrait des sous-chaînes appropriées de ces chaînes en fonction de la valeur de ID . Les précautions doivent être prises pour compter la bonne longueur. L'instruction est nécessaire car une fois la dernière sous-chaîne, il n'y a pas de , $ plus, ce qui indique la fin de la sous-chaîne. J'espère que cela pourra aider.


J'ai testé cela et cela fonctionne bien pour une petite myTable, mais une table arbitrairement importante le brise. Résultats comme les suivants commencent à montrer: 62126 16,17,6,6,6,22 $: 11,5,11,28,1 $


@DSM Oui, je sais que cela ne peut pas être utilisé pour une grande myTable car la solution est limitée par la longueur de @ STR1, @ STR2 qui ne peut pas être plus grand que Varcharate (16384). C'est pourquoi j'ai proposé une autre solution.



0
votes

Je n'ai pas de serveur Sybase pour tester, mais lire les documents en ligne, il apparaît que des expressions de table communes sont prises en charge. Je n'étais pas sûr à propos de Row_Number, tel qu'utilisé dans d'autres solutions, voici une solution qui n'utilise pas cela.

Je crois que Sybase utilise || Pour la concaténation à la chaîne, bien que les docs que j'ai lusent mentionne que "+" puisse également être utilisé, alors j'ai utilisé cela. S'il vous plaît changer selon le cas. P>

J'ai commenté la requête pour essayer d'expliquer ce qui se passe. P>

La requête concaténe toutes les valeurs ID_TYPE et ID_RF avec le même ID, dans l'augmentation de la valeur ID_TYPE 'Commande. P>

/* a common table expression is used to concatenate the values, one by one */
WITH ConcatYourTable([id],  /* the id of rows being concatenated */
      concat_id_type,       /* concatenated id_type so far */
      concat_id_ref,        /* concatenated id_ref so far */
      last_id_type,         /* the last id_type added */
      remain)               /* how many more values are there to concatenate? */
AS 
(
  /* start with the lowest id_type value for some id */
  SELECT id, id_type, id_ref, 
     id_type, /* id_type was concatentated (it's presently the only value) */
     (SELECT COUNT(*) FROM YourTable f2 WHERE f2.id=f.id)-1
     /* how many more values to concatenate -1 because we've added one already */
  FROM YourTable f 
  WHERE NOT EXISTS
  /* start with the lowest value - ensure there are no other values lower. */
     (SELECT 1 FROM YourTable f2 WHERE f2.id=f.id AND f2.id_type<f.id_type)
  UNION ALL
  /* concatenate higher values of id_type for the same id */
  SELECT f.id, 
    c.id_type + ',' + f.id_type,   /* add the new id_type value to the current list */
    c.id_ref + ',' + f.id_ref,     /* add the new id_ref value to the current list */
    f.id_type,  /* the last value added - ensured subsequent added values are greater */
    c.remain-1  /* one less value to add */
  FROM ConcatYourTable c           /* take what we have concatenated so far */    
   INNER JOIN YourTable f  /* add another row with the same id, and > id_type */
     ON f.id = c.id AND f.id_type > c.last_id_type
     /* we really want the next highest id_type, not just one that is greater */
   WHERE NOT EXISTS (SELECT 1 FROM YourTable f2
     WHERE f2.id=f.id AND f2.id_type<f.id_type AND
     f2.id_type>c.last_id_type)
)
/* Select the rows where all values for and id were concatenated (remain=0) */
/* Concatenate the cumulated id_type and id_ref fields to format id_type values:id_ref values*/
SELECT id, id_type+':'+id_ref FROM ConcatYourTable 
WHERE remain=0


2 commentaires

Ceci n'est pas valide SYBASE SYNTAX. Merci quand même :-)


J'ai dû consulter le mauvais manuel. Connaissez-vous-vous où trouver la syntaxe en ligne? Si oui, je vais réécrire la requête le cas échéant.



6
votes

Étant donné qu'il est assez difficile de faire ce fait à Sybase à l'aide d'une instruction SELECT, je suggérerais un pendant code> comme suit. Tandis que les boucles sont préférées sur des curseurs pour être beaucoup plus rapide. En supposant que le nom de la table soit MYTABLE:

CREATE TABLE #temp
(                               
aa            numeric(5,0)  identity,                            
id            int           not null,
id_type       int           not null,
id_ref        int           not null
)

CREATE TABLE #results
(                                                        
id            int           not null,
concatenation varchar(1000) not null,
)

insert into #temp
select id, id_type, id_ref from MYTABLE order by id
declare @aa int, @maxaa int, @idOld int, @idNew int
declare @str1 varchar(1000), @str2 varchar(1000), @j int

set @aa = 1
set @maxaa = (select max(aa) from #temp) 
set @idNew = (select id from #temp where aa = 1) 
, @idOld = @idNew
set @str1 = ':'

while @aa <= @maxaa 
    begin
        set @idNew = (select id from #temp where aa = @aa) 
        IF @idNew = @idOld
          BEGIN
             set @str2 = (select convert(varchar,id_type) + ':' + convert(varchar,id_ref) from #temp where aa = @aa)
             set @j = (select charindex(':',@str2))
             set @str1 = str_replace(@str1, ':', substring(@str2,1,@j - 1) + ',:') + right(@str2,len(@str2) - @j) + ',' 

             IF @aa = @maxaa  
             insert into #results (id, concatenation) 
             VALUES (@idOld, left(str_replace(@str1, ',:', ':'),len(@str1) - 2) )

          END
        ELSE
          BEGIN
             insert into #results (id, concatenation) 
             VALUES (@idOld, left(str_replace(@str1, ',:', ':'),len(@str1) - 2) )
             set @str1 = ':'
             set @str2 = (select convert(varchar,id_type) + ':' + convert(varchar,id_ref) from #temp where aa = @aa)
             set @j = (select charindex(':',@str2))
             set @str1 = str_replace(@str1, ':', substring(@str2,1,@j - 1) + ',:') + right(@str2,len(@str2) - @j) + ','

             IF @aa = @maxaa  
             insert into #results (id, concatenation) 
             VALUES (@idNew, left(str_replace(@str1, ',:', ':'),len(@str1) - 2) )
          END

        set @idOld = @idNew 
        set @aa = @aa+1
    end

select * from #results


4 commentaires

Cette approche prend considérablement plus longtemps que le curseur que j'utilise actuellement avec les données réelles


+1 pour générer des résultats corrects. Je laisserai la question ouverte jusqu'à ce que j'ai fini d'évaluer toutes les autres réponses


@DSM Une raison évidente du succès de la performance est la récréation de la table initiale (comme #temp). Vous pouvez omettre l'étape de la récréation de la table si votre table réelle a une colonne d'identité ou une autre pièce d'identité unique, et vous pouvez donc utiliser cette colonne comme compteur à la place.


La table #TEpp est peuplée très rapidement. Le retard survient pendant la boucle tandis que.



2
votes

OK, pardonnez-moi si je manque quelque chose de crucial ici parce que je ne connais pas la première chose à propos de Sybase. Mais dans MySQL, cela est absurdement simple, donc je pensais que cela ne pouvait pas être aussi mauvais que les réponses jusqu'à présent. Donc, tirer de la documentation qui peut être pertinente ou non:

SELECT id, LIST(id_type) + ":" + LIST(id_ref) AS concatentation


2 commentaires

Pour ma propre édification, l'esprit expliquant qu'en est-il de cela ne fonctionne pas?


Ah, il semble que c'est une question de versions différentes: "Sybase Adaptive Server Anywhere (mais pas Adaptive Server Enterprise) a cette fonction d'agrégat non soignée, bien que propriétaire, non standard appelée liste ()."



2
votes

Utilisez la fonction de niveau de ligne.

Votre requête: xxx

fonction: xxx


0 commentaires