Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Junção à esquerda com o valor mais próximo sem duplicatas


Abaixo está uma solução baseada em conjunto usando CTEs e funções de janelas.

Os ranked_matches CTE atribui uma classificação de correspondência mais próxima para cada linha em TableA juntamente com uma classificação de correspondência mais próxima para cada linha em TableB , usando o index valor como um desempate.

Os best_matches CTE retorna linhas de ranked_matches que têm a melhor classificação (valor de classificação 1) para ambas as classificações.

Finalmente, a consulta externa usa um LEFT JOIN da TableA para as best_matches CTE para incluir a TableA linhas que não receberam uma melhor correspondência devido à correspondência de fechamento já estar atribuída.

Observe que isso não retorna uma correspondência para a linha TableA do índice 3 indicada nos resultados da amostra. A correspondência de fechamento para essa linha é o índice TableB 3, uma diferença de 83. No entanto, essa linha TableB é uma correspondência mais próxima da linha do índice TableA 2, uma diferença de 14, portanto, já foi atribuída. Por favor, esclareça sua pergunta se não é isso que você deseja. Eu acho que esta técnica pode ser ajustada de acordo.
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:

Embora esse método use CTEs, a recursão não é usada e, portanto, não está limitada a recursões de 32K. No entanto, pode haver espaço para melhorias aqui do ponto de vista do desempenho.