2015-11-30 13 views
7

ho questa seguente scenario, una tabella con queste colonne:Confronta diversi ordini della stessa tabella

table_id|user_id|os_number|inclusion_date 

Nel sistema, l'os_number è sequenziale per gli utenti, ma a causa di un errore di sistema alcuni utenti inserita Sistemi operativi nell'ordine sbagliato. Qualcosa di simile a questo:

table_id | user_id | os_number | inclusion_date 
----------------------------------------------- 
1  | 1  | 1   | 2015-11-01 
2  | 1  | 2   | 2015-11-02 
3  | 1  | 3   | 2015-11-01 
  • Nota numero os 3 inserito prima del numero os 2

Che cosa ho bisogno:

recuperare il table_id delle righe 2 e 3, che è guasto.

Ho questi due di selezione che mi mostra la table_id in due ordini diversi:

select table_id from table order by user_id, os_number 

select table_id from table order by user_id, inclusion_date 

io non riesco a capire come posso confrontare questi due seleziona e vedere quali utenti sono interessati da questo bug del sistema .

+3

l'intersezione delle due istruzioni select risulterà in quelle table_id che non sono state interessate dal bug. quindi una query sql che esclude questo specifico table_id ti darà l'elenco di table_id che è stato colpito. Potrei sbagliarmi comunque. – beingsrv

+0

La vera domanda è: perché hai il 'os_number' del tutto? Sembra ridondante se può essere "calcolato" dall'ordine del 'inclusion_date' –

+1

@a_horse_with_no_name potrebbe essere una versione semplificata della sua query. Ad esempio, os_number potrebbe essere una chiave esterna che indirettamente punta a un numero di versione. –

risposta

0

userei WINDOW FUNCTIONS per ottenere row numbers degli ordini in questione e poi confrontarli:

SELECT 
    sub.table_id, 
    sub.user_id, 
    sub.os_number, 
    sub.inclusion_date, 
    number_order_1, number_order_2 
FROM (
    SELECT 
     table_id, 
     user_id, 
     os_number, 
     inclusion_date, 
     row_number() OVER (PARTITION BY user_id 
          ORDER BY os_number 
            ROWS BETWEEN UNBOUNDED PRECEDING 
            AND UNBOUNDED FOLLOWING 
     ) AS number_order_1, 

     row_number() OVER (PARTITION BY user_id 
          ORDER BY inclusion_date 
            ROWS BETWEEN UNBOUNDED PRECEDING 
            AND UNBOUNDED FOLLOWING 
     ) AS number_order_2 
    FROM 
     table 
) sub  
WHERE 
    number_order_1 <> number_order_1 
; 

EDIT:

A causa di a_horse_with_no_name fatto buon punto per la mia risposta definitiva. Sono tornato alla mia prima risposta (guarda nella cronologia delle modifiche) che funziona anche se os_number non è privo di spazio.

0

Entrambi gli esempi di query di seguito riportati semplicemente controllare una mancata corrispondenza tra data inclusione e os_number:

Questa prima query dovrebbe restituire la riga all'origine di errore (quella cui os_number è fuori dalla sua data di inclusione) - nel caso della esempio fila 3.

select table.table_id, table.user_id, table.os_number from table 
where EXISTS(select * from table t 
where t.user_id = table.user_id and 
     t.inclusion_date > table.inclusion_date and 
     t.os_number < table.os_number); 

Questa seconda query restituirà i numeri di tabella e gli utenti di due file che sono corrispondenti:

select first_table.table_id, second_table.table_id, first_table.user_id from 
    table first_table 
    JOIN table second_table 
    ON (first_table.user_id = second_table.user_id and 
     first_table.inclusion_date > second_table.inclusion_date and 
     first_table.os_number < second_table.os_number); 
1

Usa row_number() oltre due ordini:

select * 
from (
    select *, 
     row_number() over (order by os_number) rnn, 
     row_number() over (order by inclusion_date) rnd 
    from a_table 
    ) s 
where rnn <> rnd; 

table_id | user_id | os_number | inclusion_date | rnn | rnd 
----------+---------+-----------+----------------+-----+----- 
     3 |  1 |   3 | 2015-11-01  | 3 | 2 
     2 |  1 |   2 | 2015-11-02  | 2 | 3 
(2 rows) 
+0

Questo è sbagliato dal momento che ha scritto in questione "' il numero_sum è sequenziale per gli utenti "quindi è necessario definire la partizione per la funzione della finestra. Quindi, fondamentalmente, finisce con esattamente la stessa risposta che ho postato prima. –

+2

Stackoverflow non è una gara. Non dovresti downvotare una risposta corretta per il solo motivo che il tuo era precedente. Non è giusto. – klin

+0

@ Gabriel'sMessanger: "sequenziale" da solo non è sufficiente. 'os_number' deve anche essere ** gapless ** affinché la soluzione funzioni. –

1

Non del tutto sicuro circa le prestazioni su questo, ma è possibile utilizzare una croce applicare allo stesso tavolo per ottenere i risultati in una query. Ciò farà apparire le coppie di table_id che sono errate.

select 
    a.table_id as InsertedAfterTableId, 
    c.table_id as InsertedBeforeTableId 
from table a 
cross apply 
(
    select b.table_id 
    from table b 
    where b.inclusion_date < a.inclusion_date and b.os_number > a.os_number 
) c 
+1

In Postgres non è presente alcuna applicazione incrociata: l'equivalente in SQL standard (e Postgres) sarebbe un "cross join lateral" –

2

La tua domanda è un po 'difficile perché non esiste un ordine corretto (come presentato) - perché le date possono avere legami. Quindi, utilizzare la funzione rank() o dense_rank() per confrontare i due valori e restituire quelli che non sono nell'ordine corretto:

select t.* 
from (select t.*, 
      rank() over (partition by user_id order by inclusion_date) as seqnum_d, 
      rank() over (partition by user_id order by os_number) as seqnum_o 
     from t 
    ) t 
where seqnum_d <> seqnum_o; 
+0

Bene, puoi sempre interrompere i legami con 'ROW_NUMBER() OVER (ORDER BY inclusion_date, os_number) seqnum_d'. In caso contrario, la soluzione si trova anche nel problema dei legami, poiché restituirà sempre le righe quando "os_numbers" diversi si trovano nello stesso 'inclusion_date'. –

+0

@JulienBlanchard. . . Il problema non è "rompere i legami". Per i dati di esempio, sia 1-3-2 che 3-1-2 soddisfano i requisiti degli OP. I "legami" potrebbero comportare che gli ordini corretti siano descritti come errati. –

+0

da quello che ho capito, il problema è che il risultato di 'row_number()' varierà in base all'ordine della tabella che gli viene dato quando ci sono vincoli nella sua clausola. Non è così irrilevante se ti assicuri che non ci siano cravatte? –

0
select * 
from (
    select a_table.*, 
    lag(inclusion_date) over (partition by user_id order by os_number) as last_date 
    from a_table 
) result 
where last_date is not null AND last_date>inclusion_date; 

Questo dovrebbe coprire le lacune, così come i legami.Fondamentalmente, ho semplicemente controllato la data di inclusione dell'ultimo numero_os, e mi assicuro che non sia strettamente superiore alla data corrente (quindi 2 versione alla stessa data va bene).

Problemi correlati