2016-01-15 10 views
8

Supponiamo che ho una tabella composta da voci comeSQL sé unirsi a coppie

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

e per ogni ID voglio partecipare a questo i dati su se stessa in modo tale che le osservazioni con successiva Departure Date e Arrival Date sono raggruppati a coppie - vale a dire un partenza è abbinato all'arrivo precedente per ogni ID.

Nell'esempio sopra (dove le osservazioni sono ordinate per comodità) la seconda riga verrebbe aggiunta al 1 °, al 3 ° al 2 °, al 5 ° al 4 ° e al 6 ° al 5 ° (producendo così 4 righe con campi ID Arrival Date Arrival City Departure Date Departure City Arrival Date2 Arrival City2 Departure Date2 Departure City2).

Potrebbero esserci potenzialmente più di tre partenze per ogni ID, quindi è necessario un approccio generale. Tieni presente che potrebbero esserci buchi nei dati in cui Arrival City e Departure City non corrispondono - ad es. il Arrival City della quinta riga non è il Departure City della sesta riga, ma dovrebbero essere ancora uniti. In effetti, uno degli obiettivi principali è ottenere una visione migliore di quanti buchi ci siano nei dati.

+1

, la prego di mostrare ciò che il vostro risultato atteso dovrebbe essere simile? –

+0

"Potrebbero esserci potenzialmente più di tre partenze per ciascun ID, quindi è necessario un approccio generale" ogni particolare query produce set di risultati con una "forma" fissa: il numero, i nomi e i tipi delle colonne. Ma sembra che tu stia chiedendo una query che produce un numero diverso di colonne a seconda dei dati di input (e anche, cosa succede quando ID diversi hanno numeri diversi di partenze?). Potrebbe essere preferibile eseguire questo tipo di elaborazione di output a un livello superiore rispetto a SQL. –

+2

Qual è la versione di SQL Server? Si prega di aggiungere il tag corrispondente alla domanda invece di 'join'. Inoltre, in base ai dati di esempio, mostra come dovrebbe apparire il risultato finale. –

risposta

5

Prova questo:

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) 

cura basato sul tuo commento:

  • trovare il record con la data di partenza più presto dopo il precedente record data di arrivo, e
  • ignorare il fatto che il sesto record dei dati di esempio ha una diversa città di partenza rispetto alla città di arrivo del 5 ° record.
+0

Grazie per la risposta. In effetti, voglio che il sesto record sia aggiunto al 5 °: l'errore qui è stato intenzionale (dato che possono esserci dei buchi nei dati). Aggiornerò l'OP con questo chiarimento – user787267

8

Una soluzione è quella di utilizzare un CTE e considerare che la differenza tra due righe consecutive (identificato dal RowNo) è 1 tutto il tempo (e anche considerare le date):

;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 violino qui: SQLFiddle

+1

E aggiungendo 'WHERE c1.arrivalCity <> c2.departureCity' sicuramente aiuta * ad avere una visione migliore di quanti buchi ci sono nei dati * :-) – dnoeth

4

Non del tutto sicuro di quale risultato ha impostato la tua ricerca .. ma ho pensato di dare una possibilità e vedere se qualcuno di questi ti aiuta.

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 

Questo è il codice per avviare ..

select * from #t2 

si tradurrà in:

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 

Luoghi dove la coppia id = 0 ... Hai un gap/baddata comunque vuoi metterlo ..

Puoi anche:

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 

Questo aggiungerà precedente città di partenza e la data .. e si può fare quello che vuoi con i valori nulli .. essi significare il primo volo .. o una lacuna, se la successiva coppia di id = 0 ...

Le opzioni di selezione diventano infinite .... se null e lag (pairid) = 0, allora hai la riga con il divario .. se nullo e coppia id = id .. e lag (pairid) = id allora hai il tuo primo volo..

voglio dire che posso andare avanti .. e vi darà maggiori dettagli, ma io non sono sicuro che questo è quello che cercate .. Speranza ha aiutato comunque ..

Buona fortuna!

PS Non vedere il motivo per cui avevi bisogno di unire la tabella con se stessa .. forse ho perso tutta la point..lol..sorry se questo è il caso ..

3

questo dovrebbe funzionare:

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

Mi sembra che tu voglia ruotare i risultati e inserire i risultati in colonne aggiuntive. Ho usato ROW_NUMBER() per l'ordine. Ho concatenato le colonne in una riga prima del pivot, imperniato, quindi ho usato una funzione per invertire la concatenazione.

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; 

di utilizzare questa funzione 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

provare questo,

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