2016-01-15 19 views
8

Załóżmy, że mam tabela składająca się z wpisami jakSQL własny dołączyć parami

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 
... 

i dla każdego ID chcę dołączyć dane o sobie tak, że obserwacje z kolejnym Departure Date i Arrival Date są pogrupowane parami - czyli odejście jest sparowany z poprzednim przyjazdem dla każdego identyfikatora.

W powyższym przykładzie (gdzie obserwacje są posortowane dla wygody), drugi wiersz zostanie dołączony do pierwszego, trzeciego do drugiego, od piątego do czwartego i od szóstego do piątego (w ten sposób tworząc 4 wiersze z pola ID Arrival Date Arrival City Departure Date Departure City Arrival Date2 Arrival City2 Departure Date2 Departure City2).

Dla każdego identyfikatora mogą występować więcej niż trzy odstępstwa, dlatego konieczne jest ogólne podejście. Należy również pamiętać, że w danych mogą być dziury, w których Arrival City i Departure City nie pasuje - np. Arrival City piątego rzędu nie jest numerem Departure City szóstego wiersza, ale powinny one zostać scalone. W rzeczywistości głównym celem jest uzyskanie lepszego obrazu liczby otworów w danych.

+1

mógłbyś pokazać co się oczekiwany wynik powinien wyglądać? –

+0

"Dla każdego identyfikatora może istnieć więcej niż trzy odstępstwa, więc wymagane jest ogólne podejście" każde zapytanie generuje zestawy wyników o ustalonym "kształcie" - liczbę, nazwy i typy kolumn. Ale wygląda na to, że pytasz o zapytanie, które tworzy inną liczbę kolumn w zależności od danych wejściowych (a także, co się dzieje, gdy różne identyfikatory mają różną liczbę odejść?). Lepszym rozwiązaniem może być przetwarzanie danych wyjściowych na wyższym poziomie niż SQL. –

+2

Jaka jest wersja programu SQL Server? Dodaj odpowiedni tag do pytania zamiast "join". Ponadto, na podstawie Twoich przykładowych danych, pokaż, jak powinien wyglądać końcowy wynik. –

Odpowiedz

5

Spróbuj tego:

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) 

edytowany oparte na Twój komentarz do:

  • znaleźć rekord z najwcześniejszą datą wyjazdu, po Poprzedni rekord za datą przyjazdu, a
  • ignorować faktu że 6. rekord danych próbki ma inne miasto odlotów niż miasto przylotu 5. pisma.
+0

Dziękuję za odpowiedź. Właściwie to chcę, aby szósta płyta była dołączona do piątej - błąd tutaj był celowy (ponieważ w danych mogą być dziury). Zaktualizuję OP z tym wyjaśnieniem. – user787267

8

Rozwiązaniem jest użycie CTE i pod uwagę, że różnica między dwoma kolejnymi rzędach (wskazanym przez równo) wynosi 1 cały czas (a także pod daty):

;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 skrzypacz tutaj: SQLFiddle

+1

Dodanie "WHERE c1.arrivalCity <> c2.departureCity" zdecydowanie pomaga * uzyskać lepszy widok liczby dziur w danych * :-) – dnoeth

4

Nie jestem do końca pewny, jaki rezultat jest dla Ciebie ... ale pomyślałem, że dam ci szansę i zobaczę, czy któreś z nich ci pomogą.

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 

Jest to kod do uruchomienia ..

select * from #t2 

spowoduje:

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 

Każde miejsce, w którym para id = 0 ... masz luki/baddata jednak chcesz go umieścić ..

Możesz również:

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 

Spowoduje to dodanie poprzedniego wyjazdu miasto i datę .. i możesz robić co chcesz z null .. będą oznaczać pierwszy lot .. lub przerwę jeśli kolejny para id = 0 ...

Wybrane opcje stają się nieskończone .... jeśli null i lag (pairid) = 0, to masz wiersz z luką .. jeśli null i pair id = id .. i lag (pairid) = id, to masz swoje pierwszy lot..

Znaczy mogę iść dalej .. i daje więcej szczegółów, ale nie jestem pewien, że to, co szukasz .. Mam nadzieję, że to pomogło tak ..

Powodzenia!

PS Nie rozumiem, dlaczego trzeba było dołączyć tabelę do siebie .. może brakowało mi cały point..lol..sorry czy to przypadek ..

3

To powinno działać:

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

Wydaje mi się, że chcesz przestawić wyniki i umieścić wyniki w dodatkowych kolumnach. Użyłem ROW_NUMBER() do zamówienia. Łączyłem kolumny w rzędzie przed obrotem, obracałem, a następnie stosowałem funkcję odwracania łączenia.

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; 

użyciem tej funkcji 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

spróbować,

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