Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Left join z najbliższą wartością bez duplikatów

Poniżej znajduje się rozwiązanie oparte na zbiorach, wykorzystujące CTE i funkcje okienkowe.

ranked_matches CTE przypisuje najbliższą rangę dopasowania dla każdego wiersza w TableA wraz z najbliższą rangą dopasowania dla każdego wiersza w TableB , używając index wartość jako rozstrzygający remis.

best_matches CTE zwraca wiersze z ranked_matches które mają najlepszą rangę (wartość rangi 1) dla obu rankingów.

Wreszcie zapytanie zewnętrzne używa LEFT JOIN z TableA do best_matches CTE, aby uwzględnić TableA wiersze, do których nie przypisano najlepszego dopasowania, ponieważ dopasowanie zamknięcia jest już przypisane.

Zauważ, że nie zwraca to dopasowania dla wiersza tabeli indeksu 3 wskazanego w przykładowych wynikach. Dopasowanie zamykające dla tego wiersza to indeks 3 tabeli B, różnica 83. Jednak ten wiersz tabeli B jest bliższy wierszowi indeksu 2 tabeli A, różnica 14, więc został już przypisany. Proszę wyjaśnić swoje pytanie, jeśli nie tego chcesz. Myślę, że tę technikę można odpowiednio dostosować.

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

EDYTUJ:

Chociaż ta metoda używa CTE, rekursja nie jest używana i dlatego nie jest ograniczona do rekurencji 32K. Jednak może być tutaj miejsce na ulepszenia z perspektywy wydajności.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Instrukcja CASE w klauzuli WHERE w SQL Server 2008

  2. Jak utworzyć domyślną inną kolumnę zależną od ograniczeń w serwerze sql?

  3. Zarządzaj współbieżnością transakcji za pomocą blokad w SQL Server

  4. Czy istnieje sposób na zmianę wartości FixedLenNullInSource i TrimTrailingBlanks?

  5. Jak usunąć nadmiarową przestrzeń nazw w zagnieżdżonej kwerendzie podczas korzystania z FOR XML PATH?