sql >> Base de Datos >  >> RDS >> Sqlserver

Unión izquierda con el valor más cercano sin duplicados

A continuación se muestra una solución basada en conjuntos que utiliza CTE y funciones de ventanas.

Los ranked_matches CTE asigna un rango de coincidencia más cercano para cada fila en TableA junto con una clasificación de coincidencia más cercana para cada fila en TableB , utilizando el index valor como desempate.

Las best_matches CTE devuelve filas de ranked_matches que tienen la mejor clasificación (valor de clasificación 1) para ambas clasificaciones.

Finalmente, la consulta externa usa un LEFT JOIN de TableA a las best_matches CTE para incluir la TableA filas a las que no se les asignó una mejor coincidencia debido a que ya se asignó la coincidencia más cercana.

Tenga en cuenta que esto no devuelve una coincidencia para la fila del índice 3 TableA indicada en los resultados de su muestra. La coincidencia más cercana para esta fila es el índice 3 de TableB, una diferencia de 83. Sin embargo, esa fila de TableB es una coincidencia más cercana a la fila de índice 2 de TableA, una diferencia de 14, por lo que ya estaba asignada. Por favor aclare su pregunta si esto no es lo que quiere. Creo que esta técnica se puede modificar en consecuencia.

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];

EDITAR:

Aunque este método usa CTE, la recursión no se usa y, por lo tanto, no se limita a recursiones de 32K. Sin embargo, puede haber margen de mejora aquí desde una perspectiva de rendimiento.