2016-01-15 3 views
8

Пусть у меня есть таблица, состоящая из записей, какSQL автообъединение попарно

ID Arrival Date Arrival City Departure Date Departure City 
1  Jun 27 2015  Berlin   Jun 20 2015  Paris 
1  Jul 1 2015  Rome   Jun 29 2015  Berlin 
1  Jul 30 2015  Vienna   Jul 15 2015  Rome 
2  Jun 28 2015  Prague   Jun 23 2015  Vienna 
2  Jul 1 2015  Rome   Jun 29 2015  Prague 
2  Jul 30 2015  Vienna   Jul 15 2015  Moscow 
... 

и для каждого ID я хочу присоединиться к этим данным о себе, например, что наблюдения с последующим Departure Date и Arrival Date сгруппированы попарно - т.е. отхода сопряжен с предыдущим приходом для каждого идентификатора.

В приведенном выше примере (где наблюдения сортируются для удобства), вторая строка будет добавлена ​​к 1-й, 3-й-2-й, 5-й и 4-й и 6-й - 5-й (таким образом производя 4 строки с поля ID Arrival Date Arrival City Departure Date Departure City Arrival Date2 Arrival City2 Departure Date2 Departure City2).

Для каждого идентификатора может быть более трех отправлений, поэтому необходим общий подход. Также обратите внимание, что могут быть отверстия в данных, где Arrival City и Departure City не совпадают - например. Arrival City 5-й строки не является Departure City 6-й строки, но они все равно должны быть объединены. На самом деле главная цель - лучше понять, сколько отверстий в данных.

+1

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

+0

«Для каждого идентификатора потенциально может быть более трех отправлений, поэтому необходим общий подход». Любой конкретный запрос создает наборы результатов с фиксированной «формой» - числом, именами и типами столбцов. Но похоже, что вы запрашиваете запрос, который производит различное количество столбцов в зависимости от входных данных (а также, что происходит, когда разные идентификаторы имеют разные номера отправлений?). Лучше сделать такую ​​обработку вывода на более высоком уровне, чем SQL. –

+2

Что такое версия SQL Server? Пожалуйста, добавьте соответствующий тег в вопрос вместо 'join'. Кроме того, на основе ваших данных примера, пожалуйста, покажите, как должен выглядеть конечный результат. –

ответ

5

Попробуйте это:

SELECT a.id 
    ,a.arrival_date 
    ,a.arrival_city 
    ,a.departure_date 
    ,a.departure_city 
    ,b.arrival_date arrival_date_2 
    ,b.arrival_city arrival_city_2 
    ,b.departure_date departure_date_2 
    ,b.departure_city departure_city_2 
FROM triptable a 
JOIN triptable b ON a.id = b.id 
    AND a.departure_date = (SELECT min(departure_date) FROM so34815894 x WHERE x.departure_date > b.arrival_date AND x.id = b.id) 

Отредактировано основанное на ваш комментарий к:

  • найти запись с самой ранней датой отправления после даты прибытия предыдущего рекорда, и
  • игнорировать тот факт, что 6-я запись данных выборки имеет другой город отправления , чем город прибытия 5-й записи.
+0

Благодарим вас за ответ. На самом деле я хочу, чтобы 6-я запись добавилась к пятой - ошибка здесь была преднамеренной (поскольку в данных могут быть дыры). Я обновлю ОП с помощью этого разъяснения – user787267

8

Решение состоит в том, чтобы использовать КТР и считают, что разница между двумя последовательными рядами (идентифицируемые Ровно) 1 все время (а также учитывать даты):

;WITH CTE AS (
SELECT 
    rownum = ROW_NUMBER() OVER (ORDER BY t.ID, t.arrivalDate), 
    t.ID, 
    t.arrivalDate, 
    t.arrivalCity, 
    t.departureDate, 
    t.departureCity 
FROM #test t 
) 
SELECT * 
FROM CTE c1 
JOIN CTE c2 
ON c1.ID = c2.ID 
    AND c2.departureDate > c1.arrivalDate 
    AND c2.rownum - c1.rownum = 1 
GO 

-- structure of the #test table 
CREATE TABLE #test (
    ID int, 
    arrivalDate date, 
    arrivalCity varchar(30), 
    departureDate date, 
    departureCity varchar(30) 
) 

SQL манящими здесь: SQLFiddle

+1

И добавление 'WHERE c1.arrivalCity <> c2.departureCity' определенно помогает * лучше понять, сколько дыр в данных есть * :-) – dnoeth

4

Не совсем уверен, какой результат поставил ваш поиск .. но я думал, что дам этот выстрел и посмотрю, поможет ли вам это.

drop table #t1 
create table #t1 (id int, ArrivalDate datetime, ArrivalCity varchar(50), Departuredate datetime, DepartureCity varchar(50)) 

insert into #t1 
values (1, 'Jun 27 2015', 'Berlin', 'Jun 20 2015','Paris'), 
     (1, 'Jul 1 2015', 'Rome','Jun 29 2015','Berlin'), 
     (1, 'Jul 30 2015', 'Vienna','Jul 15 2015','Rome'), 
     (2, 'Jun 28 2015','Prague','Jun 23 2015','Vienna'), 
     (2, 'Jul 1 2015','Rome','Jun 29 2015','Prague'), 
     (2, 'Jul 30 2015','Vienna','Jul 15 2015','Moscow') 

select *, case when lead(departurecity) over (partition by id order by Arrivaldate) = ArrivalCity or lead(departurecity) over (partition by id order by Arrivaldate) is null then 1 else 0 end as PairID into #t2 from #t1 

update #t2 
set PairID = id 
where pairid != id 
and pairid != 0 

Это код для запуска ..

select * from #t2 

приведет:

id ArrivalDate ArrivalCity Departuredate DepartureCity PairID 
1 2015-06-27 Berlin  2015-06-20  Paris   1 
1 2015-07-01 Rome  2015-06-29  Berlin   1 
1 2015-07-30 Vienna  2015-07-15  Rome   1 
2 2015-06-28 Prague  2015-06-23  Vienna   2 
2 2015-07-01 Rome  2015-06-29  Prague   0 
2 2015-07-30 Vienna  2015-07-15  Moscow   2 

Любое место, где пара ID = 0 ... у вас есть пробел/baddata, но вы хотите это сделать.

Вы также можете:

select *, lead(departurecity) over (partition by ID order by ArrivalDate) as PreviousDepartureCity, lead(Departuredate) over (partition by ID order by ArrivalDate) as PreviousDepartureDate from #t2 

Это добавит предыдущий город отправления и дату .. и вы можете делать то, что вы хотите с нулями .. они будут означать первый рейс .. или пробел, если последующая пара ID = 0 ...

Опции выбора становятся бесконечными .... если null и lag (pairid) = 0, то у вас есть строка с пробелом .. если null и пара id = id .. и lag (pairid) = id, то у вас есть первый полет..

Я имею в виду, я могу продолжать идти .. и дать вам больше информации, но я не уверен, что это то, что ищете .. Надеюсь, что это помогает в любом случае ..

удачи!

PS Не понимаю, почему вам необходимо соединить таблицу к себе .. возможно я пропустил весь point..lol..sorry, если это так ..

3

Это должно работать:

with cte as(select *, row_number() over(partition by id order by date) rn from table) 
select * from cte c1 
join cte c2 on c1.id = c2.id and c1.rn = c2.rn - 1 
4

Это звучит так, как будто вы хотите изменить результаты и поместить результаты в дополнительные столбцы. Я использовал ROW_NUMBER() для заказа. Я конкатенировал столбцы в строке до поворота, поворачивал, а затем использовал функцию для отмены конкатенации.

SELECT 
    p.ID, 
    dbo.SplitString(p.[1], CHAR(13), 1) AS arrivalDate1, 
    dbo.SplitString(p.[1], CHAR(13), 2) AS arrivalCity1, 
    dbo.SplitString(p.[1], CHAR(13), 3) AS departureDate1, 
    dbo.SplitString(p.[1], CHAR(13), 4) AS departureCity1, 
    * 
FROM 
    (
     SELECT * 
     FROM 
     (
      SELECT 
       ID, 
       ROW_NUMBER() OVER (PARTITION BY ID ORDER BY arrivalDate) RowNum, 
       CAST(arrivalDate AS VARCHAR(MAX)) + CHAR(13) 
       + arrivalCity + CHAR(13) 
       + CAST(departureDate AS VARCHAR(MAX)) + CHAR(13) 
       + departureCity TripDetails 
      FROM trip t 
     ) t 
     PIVOT (MIN(t.TripDetails) FOR t.RowNum IN ([1], [2], [3], [4], [5] /* , ... */)) p 
    ) p; 

С помощью этой функции SplitString

CREATE FUNCTION dbo.SplitString ( 
    @stringToSplit VARCHAR(MAX), 
    @delim VARCHAR(255), 
    @occurence INT) 
RETURNS VARCHAR(MAX) AS 
BEGIN 

DECLARE @name NVARCHAR(255); 

DECLARE @pos INT; 

DECLARE @orderNum INT; 

SET @orderNum=0; 

WHILE CHARINDEX(@delim, @stringToSplit) > 0 

BEGIN 
    SELECT @[email protected]+1; 
    SELECT @pos = CHARINDEX(@delim, @stringToSplit) ; 
    SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1); 

    IF @orderNum = @occurence 
    BEGIN 
    RETURN @name; 
    END 

    SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)[email protected]) 
END 

    SELECT @[email protected]+1; 

    IF @orderNum = @occurence 
    BEGIN 
    RETURN @stringToSplit; 
    END 

    RETURN NULL; 
END 
3

попробовать это,

declare @t table(ID int,ArrivalDate datetime, ArrivalCity varchar(50) 
,DepartureDate datetime,DepartureCity varchar(50)) 
insert into @t values 
(1,  'Jun 27 2015',  'Berlin',   'Jun 20 2015',  'Paris ') 
,(1,  'Jul 1 2015 ',  'Rome ',   'Jun 29 2015',  'Berlin ') 
,(1,  'Jul 30 2015',  'Vienna',   'Jul 15 2015',  'Rome ') 
,(2,  'Jun 28 2015',  'Prague',   'Jun 23 2015',  'Vienna ') 
,(2,  'Jul 1 2015 ',  'Rome ',   'Jun 29 2015',  'Prague ') 
,(2 , 'Jul 30 2015',  'Vienna',   'Jul 15 2015',  'Moscow ') 

;WITH CTE 
AS (
    SELECT * 
     ,ROW_NUMBER() OVER (
      ORDER BY id 
       ,arrivaldate 
      ) rn 
    FROM @t 
    ) 
SELECT A.arrivaldate 
    ,a.arrivalcity 
    ,a.DepartureDate 
    ,a.DepartureCity 
    ,b.arrivaldate 
    ,b.arrivalcity 
    ,b.DepartureDate 
    ,b.DepartureCity 
FROM CTE A 
LEFT JOIN CTE b ON a.rn + 1 = b.rn 
Смежные вопросы