Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Ляво съединение с най-близката стойност без дубликати

По-долу е базирано на набор решение, използващо CTE и функции за прозорци.

ranked_matches CTE присвоява ранг на най-близкото съвпадение за всеки ред в TableA заедно с ранга на най-близкото съвпадение за всеки ред в TableB , използвайки index стойност като прекъсвач на вратовръзка.

best_matches CTE връща редове от ranked_matches които имат най-добър ранг (стойност на ранг 1) и за двете класации.

И накрая, външната заявка използва LEFT JOIN от TableA към към best_matches CTE за включване на TableA редове, на които не е присвоено най-добро съвпадение поради вече зададеното затварящо съвпадение.

Обърнете внимание, че това не връща съвпадение за реда с индекс 3 TableA, посочен във вашите примерни резултати. Затварящото съвпадение за този ред е TableB индекс 3, разлика от 83. Въпреки това, този ред TableB е по-близко съвпадение на TableA индекс 2, разлика от 14, така че вече е присвоен. Моля, изяснете въпроса си, ако това не е това, което искате. Мисля, че тази техника може да бъде променена съответно.

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

РЕДАКТИРАНЕ:

Въпреки че този метод използва CTE, рекурсията не се използва и следователно не е ограничена до 32K рекурсии. Може обаче да има място за подобрение от гледна точка на производителността.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Въведение в функциите с таблично стойности с множество оператори (MSTVF) в SQL Server

  2. Филтрирайте по изходна клауза sql

  3. Изпълнете SQL съхранена процедура и обработете резултатите

  4. Как мога да увелича стойността за всяка итерация INSERT INTO?

  5. Как да промените периода на задържане на прихващане на промени (CDC) в SQL Server - урок за SQL Server