2016-12-07 3 views
1

У меня есть вопрос, который казалось простым решением, но теперь я нахожу это неприятным.Идентификация/сравнение наборов строк внутри групп

В упрощении - мне нужно найти способ идентификации уникальных наборов строк в группах, определенных другим столбцом. В базовом примере таблица источников содержит только два столбца:

routeID nodeID nodeName 
    1  1  a 
    1  2  b 
    2  1  a 
    2  2  b 
    3  1  a 
    3  2  b 
    4  1  a 
    4  2  c 
    5  1  a 
    5  2  c 
    6  1  a 
    6  2  b 
    6  3  d 
    7  1  a 
    7  2  b 
    7  3  d 

Таким образом, столбец routeID относится к множеству узлов, которые определяют маршрут.

Мне нужно сделать так, чтобы каким-то образом группировать маршруты, чтобы была только одна уникальная последовательность узлов для одного routeID.

В моем фактическом случае я попытался использовать функцию окна для добавления столбцов, которые помогают идентифицировать последовательность узлов, но я до сих пор не знаю, как получить эти уникальные последовательности и групповые маршруты.

В качестве окончательного эффекта я хочу получить только уникальные маршруты - например, маршруты 1,2 и 3, объединенные по одному маршруту.

У вас есть идеи, как мне помочь?

EDIT:

Другая таблица, в которой я хотел бы присоединиться к одному из примера может выглядеть так:

journeyID nodeID nodeName routeID 
    1  1  a  1 
    1  2  b  1 
    2  1  a  1 
    2  2  b  1 
    3  1  a  4 
    3  2  c  4 
    ........................... 
    ........................... 
+3

пожалуйста показать ваш ожидаемый результат – TheGameiswar

+0

У меня возникли проблемы с пониманием того, что вы хотите. Вы хотите, чтобы routeID появлялся один раз, а затем список разделенных запятыми nodeID в следующем столбце? –

+0

есть ли ограничение на количество узлов для каждого идентификатора маршрута? – MtwStark

ответ

0

Вы можете попробовать эту идею:

DECLARE @DataSource TABLE 
(
    [routeID] TINYINT 
    ,[nodeID] TINYINT 
    ,[nodeName] CHAR(1) 
); 

INSERT INTO @DataSource ([routeID], [nodeID], [nodeName]) 
VALUES ('1', '1', 'a') 
     ,('1', '2', 'b') 
     ,('2', '1', 'a') 
     ,('2', '2', 'b') 
     ,('3', '1', 'a') 
     ,('3', '2', 'b') 
     ,('4', '1', 'a') 
     ,('4', '2', 'c') 
     ,('5', '1', 'a') 
     ,('5', '2', 'c') 
     ,('6', '1', 'a') 
     ,('6', '2', 'b') 
     ,('6', '3', 'd') 
     ,('7', '1', 'a') 
     ,('7', '2', 'b') 
     ,('7', '3', 'd'); 


SELECT DS.[routeID] 
     ,nodes.[value] 
     ,ROW_NUMBER() OVER (PARTITION BY nodes.[value] ORDER BY [routeID]) AS [rowID] 
FROM 
( 
    -- getting unique route ids 
    SELECT DISTINCT [routeID] 
    FROM @DataSource DS 
) DS ([routeID]) 
CROSS APPLY 
(
    -- for each route id creating CSV list with its node ids 
    SELECT STUFF 
    (
     (
      SELECT ',' + [nodeName] 
      FROM @DataSource DSI 
      WHERE DSI.[routeID] = DS.[routeID] 
      ORDER BY [nodeID] 
      FOR XML PATH(''), TYPE 
     ).value('.', 'VARCHAR(MAX)') 
     ,1 
     ,1 
     ,'' 
    ) 
) nodes ([value]); 

Код даст вам этот результат:

enter image description here

Итак, вам просто нужно фильтровать по rowID = 1. Конечно, вы можете изменить код по своему усмотрению, чтобы удовлетворить ваши критерии работы (например, не показывать первый идентификатор маршрута с теми же узлами, кроме последнего).

Кроме того, ROW_NUMBER функция не может быть использована непосредственно в пункте WHERE, так что вам нужно обернуть код перед фильтрацией:

WITH DataSource AS 
(
    SELECT DS.[routeID] 
      ,nodes.[value] 
      ,ROW_NUMBER() OVER (PARTITION BY nodes.[value] ORDER BY [routeID]) AS [rowID] 
    FROM 
    ( 
     -- getting unique route ids 
     SELECT DISTINCT [routeID] 
     FROM @DataSource DS 
    ) DS ([routeID]) 
    CROSS APPLY 
    (
     -- for each route id creating CSV list with its node ids 
     SELECT STUFF 
     (
      (
       SELECT ',' + [nodeName] 
       FROM @DataSource DSI 
       WHERE DSI.[routeID] = DS.[routeID] 
       ORDER BY [nodeID] 
       FOR XML PATH(''), TYPE 
      ).value('.', 'VARCHAR(MAX)') 
      ,1 
      ,1 
      ,'' 
     ) 
    ) nodes ([value]) 
) 
SELECT DS2.* 
FROM DataSource DS1 
INNER JOIN @DataSource DS2 
    ON DS1.[routeID] = DS2.[routeID] 
WHERE DS1.[rowID] = 1; 

enter image description here

0

хорошо, давайте использовать некоторые рекурсии, чтобы создать полный узел список для каждого routeID

Прежде всего давайте заполнять таблицу источника и journeyes рассказ

-- your source 
declare @r as table (routeID int, nodeID int, nodeName char(1)) 

-- your other table 
declare @j as table (journeyID int, nodeID int, nodeName char(1), routeID int) 

-- temp results table 
declare @routes as table (routeID int primary key, nodeNames varchar(1000)) 

;with 
s as (
    select * 
    from (
     values 
     (1,  1,  'a'), 
     (1,  2,  'b'), 
     (2,  1,  'a'), 
     (2,  2,  'b'), 
     (3,  1,  'a'), 
     (3,  2,  'b'), 
     (4,  1,  'a'), 
     (4,  2,  'c'), 
     (5,  1,  'a'), 
     (5,  2,  'c'), 
     (6,  1,  'a'), 
     (6,  2,  'b'), 
     (6,  3,  'd'), 
     (7,  1,  'a'), 
     (7,  2,  'b'), 
     (7,  3,  'd') 
    ) s (routeID, nodeID, nodeName) 
) 
insert into @r 
select * 
from s 

;with 
s as (
    select * 
    from (
     values 
     (1,  1,  'a',  1), 
     (1,  2,  'b',  1), 
     (2,  1,  'a',  1), 
     (2,  2,  'b',  1), 
     (3,  1,  'a',  4), 
     (3,  2,  'c',  4) 
    ) s (journeyID, routeID, nodeID, nodeName) 
) 
insert into @j 
select * 
from s 

теперь давайте exctract маршруты:

;with 
d as (
    select *, row_number() over (partition by r.routeID order by r.nodeID desc) n2 
    from @r r 
), 
r as (
    select d.*, cast(nodeName as varchar(1000)) Names, cast(0 as bigint) i2 
    from d 
    where nodeId=1 
    union all 
    select d.*, cast(r.names + ',' + d.nodeName as varchar(1000)), r.n2 
    from d 
    join r on r.routeID = d.routeID and r.nodeId=d.nodeId-1 
) 
insert into @routes 
select routeID, Names 
from r 
where n2=1 

стол @routes будет выглядеть следующим образом:

routeID nodeNames 
1  'a,b' 
2  'a,b' 
3  'a,b' 
4  'a,c' 
5  'a,c' 
6  'a,b,d' 
7  'a,b,d' 

теперь окончательный вывод:

-- the unique routes 
select MIN(r.routeID) routeID, nodeNames 
from @routes r 
group by nodeNames 

-- the unique journyes 
select MIN(journeyID) journeyID, r.nodeNames 
from @j j 
inner join @routes r on j.routeID = r.routeID 
group by nodeNames 

выход:

routeID nodeNames 
1  'a,b' 
4  'a,c' 
6  'a,b,d' 

и

journeyID nodeNames 
1   'a,b' 
3   'a,c' 
Смежные вопросы