2009-06-03 9 views
3

J'ai quelques donnéesrequête TSQL à concaténer et de supprimer le préfixe commun

id ref 
== ========== 
1 3536757616 
1 3536757617 
1 3536757618 

et que vous souhaitez obtenir le résultat

1 3536757616/7/8 

donc essentiellement les données sont agrégées sur id, avec les refs concaténés, séparés par une barre oblique «/», mais avec un préfixe commun supprimé si les données ont été comme

id ref 
== ========== 
2 3536757628 
2 3536757629 
2 3536757630 

je ne voudrais pour obtenir le résultat

2 3536757629/28/30 

Je sais que je peux simplement concaténer les refs en utilisant

SELECT distinct 
    id, 
    stuff ((SELECT 
        '/ ' + ref 
       FROM 
        tableA tableA_1 
       where tableA_1.id = tableA_2.id 
    FOR XML PATH ('')) , 1 , 2 , '') 
from TableA tableA_2 

pour donner

1 3536757616/ 3536757617/ 3536757618 
2 3536757628/ 3536757629/ 3536757630 

mais il est le bit qui supprime l'élément commun que je suis après .....


Code pour les données de test:

create table tableA (id int, ref varchar(50)) 

insert into tableA 
select 1, 3536757616 
union select 1, 3536757617 
union select 1, 3536757618 
union select 2, 3536757628 
union select 2, 3536757629 
union select 2, 3536757630 

Répondre

3
WITH hier(cnt) AS 
     (
     SELECT 1 
     UNION ALL 
     SELECT cnt + 1 
     FROM hier 
     WHERE cnt <= 100 
     ) 
SELECT CASE WHEN ROW_NUMBER() OVER (ORDER BY id) = 1 THEN ref ELSE '/' + SUBSTRING(ref, mc + 1, LEN(ref)) END 
FROM (
     SELECT MIN(common) AS mc 
     FROM (
       SELECT (
         SELECT MAX(cnt) 
         FROM hier 
         WHERE SUBSTRING(initref, 1, cnt) = SUBSTRING(ref, 1, cnt) 
           AND cnt <= LEN(ref) 
         ) AS common 
       FROM (
         SELECT TOP 1 ref AS initref 
         FROM tableA 
         ) i, 
         tableA 
       ) q 
     ) q2, tableA 
FOR XML PATH('') 

--- 

3536757616/17/18/28/29/30 

Même chose avec les groupes:

WITH hier(cnt) AS 
     (
     SELECT 1 
     UNION ALL 
     SELECT cnt + 1 
     FROM hier 
     WHERE cnt <= 100 
     ) 
SELECT (
     SELECT CASE WHEN ROW_NUMBER() OVER (ORDER BY a2.ref) = 1 THEN ref ELSE '/' + SUBSTRING(ref, mc + 1, LEN(ref)) END 
     FROM tableA a2 
     WHERE a2.id = q2.id 
     FOR XML PATH('') 
     ) 
FROM (
     SELECT id, MIN(common) AS mc 
     FROM (
       SELECT a.id, 
         (
         SELECT MAX(cnt) 
         FROM hier 
         WHERE SUBSTRING(i.initref, 1, cnt) = SUBSTRING(a.ref, 1, cnt) 
           AND cnt <= LEN(ref) 
         ) AS common 
       FROM (
         SELECT id, MIN(ref) AS initref 
         FROM tableA 
         GROUP BY 
           id 
         ) i 
       JOIN tableA a 
       ON  i.id = a.id 
       ) q 
     GROUP BY 
       id 
     ) q2 
--- 
3536757616/7/8 
3536757628/29/30 
+0

+1. J'ai mal à la tête en essayant d'envelopper mon cerveau mais c'est vraiment une très bonne façon de le faire. Beaucoup plus que les deux (actuellement) autres réponses (y compris la mienne). –

+0

@Lieven: sera le sujet de la poste de demain dans mon blog – Quassnoi

+0

il suffit d'ajouter id à la sélection finale et c'est parfait! –

1

Je nomme ma table #T, et en utilisant l'instruction SELECT suivante

select id, number, substring(#t.ref, 1, v.number), count(id) 
from master.dbo.spt_values v 
inner join #t on v.number <= len(#t.ref) 
where v.name is null and v.number > 0 
group by id, number, substring(#t.ref, 1, v.number) 
order by id, count(id) desc, number desc 

vous obtenez un jeu de résultats où le premier enregistrement de chaque ID contient la longueur maximale et la chaîne initiale la plus longue pour chaque identifiant. Ce n'est pas une solution complète, mais un bon point de départ: parcourir les identifiants, émettre un SELECT TOP 1 pour récupérer la chaîne la plus longue et concaténer la chaîne de diff pour chaque enregistrement ayant le même ID.

0
declare @tableA table(id int, ref varchar(50)) 
declare @suffix table(id int, suffix varchar(50)) 
declare @todo table(id int) 

insert into @tableA 
select 1, '3536757616' 
union select 1, '3536757617' 
union select 1, '3536757618' 
union select 2, '3536757628' 
union select 2, '3536757629' 
union select 2, '3536757630' 

insert into @suffix 
select * from @tableA 

insert into @todo 
select s1.id 
from (
    select id, cnt = count(*) 
    from @suffix 
    group by id, substring(suffix, 1, 1) 
) s1 
    inner join (
    select id, cnt = count(*) 
    from @suffix 
    group by id 
) s2 on s2.id = s1.id and s2.cnt = s1.cnt 

while exists (select * from @todo) 
begin 
    update @suffix 
    set suffix = substring(suffix, 2, len(suffix) - 1) 
    from @suffix s 
     inner join @todo t on t.id = s.id 

    delete from @todo 

    insert into @todo 
    select s1.id 
    from (
     select id, cnt = count(*) 
     from @suffix 
     group by id, substring(suffix, 1, 1) 
    ) s1 
    inner join (
     select id, cnt = count(*) 
     from @suffix 
     group by id 
    ) s2 on s2.id = s1.id and s2.cnt = s1.cnt 
end 

select * from @suffix 
Questions connexes