2015-05-27 13 views
6

Sto tentando di ordinare una query specifica prendendo in considerazione i record successivi e precedenti, ma non riesco a ottenere il risultato. Vorrei ordinare per un numero e una lettera, ma se, per esempio, l'ultima lettera del numero 1 è uguale a una delle lettere del numero 2, voglio cambiare l'ordine, in modo che la lettera corrisponda al seguente disco.Ordinamento basato sui record successivi e precedenti in SQL

Create script and SQL fiddle demo

create table Parent (
id [bigint] IDENTITY(1,1), 
number bigint NOT NULL, 
PRIMARY KEY (id) 
) 
GO 

create table Child (
id [bigint] IDENTITY(1,1), 
parentId BIGINT, 
letter VARCHAR(1) NOT NULL, 
PRIMARY KEY (id), 
UNIQUE (parentId, Letter), 
FOREIGN KEY (parentId) REFERENCES Parent(id) 
) 
GO 

INSERT Parent (number) VALUES (1) 
INSERT Parent (number) VALUES (2) 
INSERT Parent (number) VALUES (3) 

INSERT Child (parentId, letter) VALUES (1, 'A') 
INSERT Child (parentId, letter) VALUES (1, 'C') 
INSERT Child (parentId, letter) VALUES (2, 'B') 
INSERT Child (parentId, letter) VALUES (2, 'C') 
INSERT Child (parentId, letter) VALUES (3, 'B') 
INSERT Child (parentId, letter) VALUES (3, 'D') 

interrogazione attuale

Attualmente sto ordinamento con questa query:

SELECT P.number, C.letter 
FROM Child C 
JOIN Parent P ON C.parentId = P.id 
ORDER BY P.number, C.letter 

risultato corrente impostato

0.123.516,41 mila
number    letter 
-------------------- ------ 
1     A 
1     C 
2     B 
2     C 
3     B 
3     D 

Risultato atteso set

Per chiarire quello che realmente voglio fare, qui è il set di risultati attesi (con C e B del numero 2 acceso).

number    letter 
-------------------- ------ 
1     A 
1     C 
2     C --switched 
2     B --switched 
3     B 
3     D 

Altri requisiti e mettere in discussione

  • Si deve lavorare in SQL Server 2005.
  • C'è uno scenario in cui vengono utilizzate 3 lettere per numero, sono contento se utilizza solo la corrispondenza migliore.
  • In realtà mi interessano anche le soluzioni per le versioni successive di SQL Server (per l'apprendimento), ma quelle non rispondono alla mia domanda.

Qualcuno può indicarmi la giusta direzione su come farlo?

+0

Si prega di consultare questa domanda per un nuovo scenario che mi è venuta in mente, dai commenti sulla risposta accettata: http://stackoverflow.com/questions/30480504/complex-sorting-based-on- next-and-previous-records-in-sql –

risposta

6

Si può fare qualcosa di simile.

  1. Identificare prima e l'ultima lettera di ogni padre utilizzando ROW_NUMBER() e PARTITION BY
  2. Partita l'ultimo record della precedente id con il primo record della prossima id.
  3. Verificare se il secondo id genitore ha alcuna lettera che corrisponde con la lettera selezionata sopra
  4. Utilizzare un LEFT JOIN e utilizzare CASE o ISNULL per impostare una priorità più alta per un tale record di id in cui la lettera era abbinato

Query

;WITH CTE AS 
(
SELECT id,ParentID,letter, 
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID) first_element, 
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID DESC) Last_element 
FROM Child 
), CTE2 AS 
(
SELECT c1.id,c1.parentid,c1.letter,c2.parentid as c2parentid 
FROM CTE c1 
INNER JOIN CTE c2 
ON c1.last_element = 1 
AND c2.first_element = 1 
AND c1.id +1 = c2.id 
), CTE3 AS 
(
SELECT C.parentid,C.id 
FROM CTE2 
INNER JOIN child C ON CTE2.c2parentid = C.parentid 
AND C.letter = CTE2.letter 
) 
SELECT P.number, C.letter 
FROM Child C 
JOIN Parent P ON C.parentId = P.id 
LEFT JOIN CTE3 ON CTE3.id = C.id 
ORDER BY P.number, ISNULL(CTE3.id,0) DESC, C.letter 

uscita

number letter 
1 A 
1 C 
2 C 
2 B 
3 B 
3 D 

SQL Fiddle

EDIT

Se il ids non sono sequenziali, è possibile cambiare CTE1 e CTE2 come questo per utilizzare ROW_NUMBER()OVER(ORDER BY ID) seq_id.

;WITH CTE AS 
(
SELECT id,ParentID,letter, 
ROW_NUMBER()OVER(ORDER BY ID) seq_id, 
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID) first_element, 
ROW_NUMBER()OVER(PARTITION BY parentId ORDER BY ID DESC) Last_element 
FROM Child 
), CTE2 AS 
(
SELECT c1.id,c1.parentid,c1.letter,c2.parentid as c2parentid 
FROM CTE c1 
INNER JOIN CTE c2 
ON c1.last_element = 1 
AND c2.first_element = 1 
AND c1.seq_id + 1 = c2.seq_id 
) 

Il resto del codice rimane uguale.

SQL Fiddle

+0

molto bello, stavo cercando di fare qualcosa del genere ma eri waaaaaaaaaay più veloce: p, non ero nemmeno a metà strada –

+0

'AND c1.id +1 = c2.id 'questo mi preoccupa un po ', e se gli id ​​non ci stanno riuscendo? –

+0

potresti fare un 'row_number() over (order by id)' e quindi usare quello – ughai

Problemi correlati