sql >> Databasteknik >  >> RDS >> Sqlserver

Vänsterkoppling med närmaste värde utan dubbletter

Nedan finns en uppsättningsbaserad lösning som använder CTE:er och fönsterfunktioner.

ranked_matches CTE tilldelar en närmast matchningsrankning för varje rad i TableA tillsammans med en närmast matchningsrankning för varje rad i TableB , med hjälp av index värde som oavgjort.

best_matches CTE returnerar rader från ranked_matches som har den bästa rankningen (rankvärde 1) för båda rankningarna.

Slutligen använder den yttre frågan en LEFT JOIN från TableA till till best_matches CTE för att inkludera TableA rader som inte tilldelats en bästa matchning på grund av att den avslutande matchen redan har tilldelats.

Observera att detta inte returnerar en matchning för index 3 TabellA rad som anges i dina exempelresultat. Den avslutande matchningen för den här raden är TabellB index 3, en skillnad på 83. Den tabellB-raden är emellertid en närmare matchning med TabellA-index 2-raden, en skillnad på 14 så den var redan tilldelad. Vänligen förtydliga din fråga om detta inte är vad du vill. Jag tror att den här tekniken kan anpassas därefter.

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

EDIT:

Även om denna metod använder CTE, används inte rekursion och är därför inte begränsad till 32K-rekursioner. Det kan dock finnas utrymme för förbättringar här ur ett prestationsperspektiv.



  1. Det gick inte att lägga till den främmande nyckeln. Saknat index för begränsningsfelkod:1822

  2. Beteende av NOT LIKE med NULL-värden

  3. Hur kan jag ansluta till SQL Server med integrerad säkerhet med JDBC-drivrutinen?

  4. Vackra block av pannplåt