Как я могу сопоставить строку в SQL Server только один раз?

У меня возникла следующая проблема, и я прошу вашей помощи при объединении двух таблиц в SQL Server 2016 (v13).

У меня есть 2 стола, Revenues и Cashins.

Revenues:

Идентификатор дохода Идантификационный номер продукта Счет № Количество 123 456 987 1000 234 456 987 1000

Cashins:

CashinID Идантификационный номер продукта InoviceНет Количество азбука 456 987 1000 CDE 456 987 1000

Цель состоит в том, чтобы автоматически сопоставлять денежные суммы с доходами (но только один раз!).

Обе таблицы имеют свои уникальные идентификаторы, но столбцы, используемые для объединения этих таблиц,

  • Идантификационный номер продукта
  • Счет №
  • Количество

Для записей только с одной строкой в ​​каждой таблице с такими критериями все работает нормально.

Однако иногда в этих столбцах есть несколько строк с одинаковым значением (как указано выше), но с уникальным идентификатором (это не ошибка, а так и должно быть).

Проблема в том, что при объединении получается декартово произведение.

Чтобы воссоздать таблицы, здесь операторы:

DROP TABLE IF EXISTS Revenues
GO

CREATE TABLE Revenues 
(
    RevenueID [nvarchar](10) NULL,  
    ProductID [nvarchar](10) NULL,  
    InvoiceNo [nvarchar](10) NULL,      
    Amount money NULL
)
GO

DROP TABLE IF EXISTS CashIns
GO

CREATE TABLE CashIns 
(
    CashinID [nvarchar](10) NULL,
    ProductID [nvarchar](10) NULL,  
    InvoiceNo [nvarchar](10) NULL,      
    Amount money NULL
)
GO

INSERT INTO [Revenues] VALUES ('123', '456', '987', 1000)
INSERT INTO [Revenues] VALUES ('234', '456', '987', 1000)

INSERT INTO [CashIns] VALUES ('ABC', '456', '987', 1000)
INSERT INTO [CashIns] VALUES ('BCD', '456', '987', 1000)

Желаемый результат:

Идентификатор дохода Идантификационный номер продукта Счет № Количество CashinID 123 456 987 1000 азбука 234 456 987 1000 CDE
SELECT 
    R.RevenueID,
    R.ProductID,
    R.InvoiceNo,
    R.Amount,
    C.CashinID,
FROM 
    [Revenues] R
LEFT JOIN 
    [CashIns] C ON R.ProductID = C.ProductID
                AND R.InvoiceNo = C.InvoiceNo
                AND R.Amount = C.Amount

Полученные результаты:

Идентификатор дохода Идантификационный номер продукта Счет № Количество CashinID 123 456 987 1000 азбука 123 456 987 1000 CDE 234 456 987 1000 азбука 234 456 987 1000 CDE

Что теоретически имеет смысл, но я просто не могу найти решение, в котором каждая строка используется только один раз.

Две вещи, которые я нашел и попробовал, — это оконные функции и функция OUTER APPLY с выбором TOP(1). Оба пришли к одному и тому же результату:

SELECT
    *
FROM 
    [Revenues] R
OUTER APPLY 
    (SELECT TOP(1) *
     FROM [CashIns] C) C

Который возвращает нужные столбцы из таблицы Revenues, но соответствует только первому появлению из таблицы Cashins:

Идентификатор дохода Идантификационный номер продукта Счет № Количество CashinID 123 456 987 1000 азбука 234 456 987 1000 азбука

Я также подумал о чем-то вроде обновления таблицы Revenues, чтобы совпавший CashinID находился рядом со строкой, а затем каждый раз проверял, что CashinID еще не используется в этой таблице, но я не мог заставить это работать...

Заранее большое спасибо за любую помощь или подсказку в правильном направлении!

У вас есть фундаментальная проблема с вашими отношениями данных. Вы присоединяетесь по неуникальным идентификаторам. Единственное возможное решение, которое я вижу, это упорядочить строки в Revenues и упорядочить строки в Cashins и сопоставить row_number.

Tim Jarosz 22.11.2022 19:09

Технически ваш 4-строчный вывод означает: «Цель состоит в том, чтобы автоматически сопоставлять наличные деньги с доходами (но только один раз!»). поскольку нет никакой связи между RevenueID и CashinId

Mark Schultheiss 22.11.2022 19:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как я уже сказал в своем комментарии, у вас есть фундаментальная проблема с вашими отношениями данных. Вам нужно сослаться на уникальный идентификатор другой таблицы в одной из ваших таблиц. Если вы этого не сделаете, то сможете только упорядочить свои транзакции в обеих таблицах и соединить их по номеру строки. Вы используете надежду и молитву, чтобы объединить свои данные вместо ненадежных идентификаторов.

--This example orders the transactions in each transaction table and uses
--the order number to join them.
WITH RevPrelim AS (
    SELECT *
      , ROW_NUMBER() OVER(PARTITION BY InvoiceNo, ProductID, Amount ORDER BY RevenueID) AS row_num
    FROM [Revenues] R
), CashinsPrelim AS (
    SELECT *
        , ROW_NUMBER() OVER(PARTITION BY InvoiceNo, ProductID, Amount ORDER BY CashinID) AS row_num
    FROM [CashIns] AS C
)
SELECT *
FROM RevPrlim AS r
    LEFT OUTER JOIN CashinsPrelim AS c
        ON c.ProductID = r.ProductID
            AND c.InvoiceNo = r.InvoiceNo
            AND c.Amount = r.Amount
            AND c.row_num = r.row_num

Большое спасибо за ваш ответ, @TimJarosz! Вы правы, это не лучшая база данных для работы, но это то, что у меня есть. По крайней мере, я могу положиться на тот факт, что не может быть никаких ложных значений с этими тремя условиями соединения - проблема была только в дублировании. Для тестовых данных ваше решение работает, большое спасибо! Я собираюсь проверить это на реальных данных. Еще раз спасибо и всего наилучшего.

rasi_kg 23.11.2022 11:10

Другие вопросы по теме