sql >> Database >  >> RDS >> Sqlserver

Linker join met dichtstbijzijnde waarde zonder duplicaten

Hieronder vindt u een set-gebaseerde oplossing die gebruikmaakt van CTE's en vensterfuncties.

De ranked_matches CTE wijst voor elke rij in TableA een rangorde toe die het meest overeenkomt met samen met een rangorde die het meest overeenkomt met elke rij in TableB , met behulp van de index waarde als tiebreak.

De best_matches CTE retourneert rijen van ranked_matches die de beste rang (rangwaarde 1) hebben voor beide ranglijsten.

Ten slotte gebruikt de buitenste query een LEFT JOIN van TableA naar de naar de best_matches CTE om de TableA . op te nemen rijen waaraan geen beste overeenkomst is toegewezen omdat de sluitovereenkomst al is toegewezen.

Houd er rekening mee dat dit geen overeenkomst oplevert voor de rij met index 3 TabelA die is aangegeven in uw voorbeeldresultaten. De sluitovereenkomst voor deze rij is TabelB-index 3, een verschil van 83. Die tabelB-rij komt echter beter overeen met de tabelA-index 2-rij, een verschil van 14, dus deze was al toegewezen. Gelieve uw vraag te verduidelijken als dit niet is wat u wilt. Ik denk dat deze techniek dienovereenkomstig kan worden aangepast.

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

BEWERKEN:

Hoewel deze methode gebruik maakt van CTE's, wordt recursie niet gebruikt en is daarom niet beperkt tot 32K-recursies. Er kan hier echter ruimte zijn voor verbetering vanuit een prestatieperspectief.



  1. Moet ik een try..catch-blok en expliciete rollback gebruiken in een SQL Server-procedure?

  2. Hoe Adminer op zijn eigen app te installeren

  3. 'SQLSTATE[HY093]:Ongeldig parameternummer:aantal gebonden variabelen komt niet overeen met aantal tokens'

  4. Berekening van SQL Server ROW_NUMBER() OVER() voor een afgeleide tabel