Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Unisci a sinistra con il valore più vicino senza duplicati

Di seguito è riportata una soluzione basata su set che utilizza CTE e funzioni di windowing.

Le ranked_matches CTE assegna un grado di corrispondenza più vicino per ogni riga in TableA insieme a un grado di corrispondenza più vicino per ogni riga in TableB , utilizzando l'index valore come spareggio.

I best_matches CTE restituisce le righe da ranked_matches che hanno il miglior rank (valore 1) per entrambe le classifiche.

Infine, la query esterna utilizza un LEFT JOIN da TableA al best_matches CTE per includere la TableA righe a cui non è stata assegnata una corrispondenza migliore a causa della corrispondenza di chiusura già assegnata.

Si noti che questo non restituisce una corrispondenza per la riga dell'indice 3 TableA indicata nei risultati di esempio. La corrispondenza di chiusura per questa riga è l'indice TableB 3, una differenza di 83. Tuttavia, quella riga TableB è una corrispondenza più vicina alla riga dell'indice TableA 2, una differenza di 14, quindi era già stata assegnata. Per favore chiarisci la tua domanda se questo non è quello che vuoi. Penso che questa tecnica possa essere modificata di conseguenza.

CREATE TABLE dbo.TableA(
      [index] int NOT NULL
        CONSTRAINT PK_TableA PRIMARY KEY
    , value int
    );
CREATE TABLE dbo.TableB(
      [index] int NOT NULL
        CONSTRAINT PK_TableB PRIMARY KEY
    , value int
    );
INSERT  INTO dbo.TableA
        ( [index], value )
VALUES  ( 1, 123 ),
        ( 2, 245 ),
        ( 3, 342 ),
        ( 4, 456 ),
        ( 5, 608 );

INSERT  INTO dbo.TableB
        ( [index], value )
VALUES  ( 1, 152 ),
        ( 2, 159 ),
        ( 3, 259 );

WITH 
      ranked_matches AS (
        SELECT 
              a.[index] AS a_index
            , a.value AS a_value
            , b.[index] b_index
            , b.value AS b_value
            , RANK() OVER(PARTITION BY a.[index] ORDER BY ABS(a.Value - b.value), b.[index]) AS a_match_rank
            , RANK() OVER(PARTITION BY b.[index] ORDER BY ABS(a.Value - b.value), a.[index]) AS b_match_rank
        FROM dbo.TableA AS a
        CROSS JOIN dbo.TableB AS b
    )
    , best_matches AS (
        SELECT
              a_index
            , a_value
            , b_index
            , b_value
        FROM ranked_matches
        WHERE
                a_match_rank = 1
            AND b_match_rank= 1
    )
SELECT
      TableA.[index] AS a_index
    , TableA.value AS a_value
    , best_matches.b_index
    , best_matches.b_value
FROM dbo.TableA
LEFT JOIN best_matches ON
    best_matches.a_index = TableA.[index]
ORDER BY
    TableA.[index];

MODIFICA:

Sebbene questo metodo utilizzi CTE, la ricorsione non viene utilizzata e pertanto non è limitata alle ricorsioni a 32 KB. Tuttavia, potrebbe esserci spazio per miglioramenti qui dal punto di vista delle prestazioni.