【问题标题】:How can I match a row in SQL Server only once?如何只匹配 SQL Server 中的一行一次?
【发布时间】:2022-11-23 02:24:45
【问题描述】:

在 SQL Server 2016 (v13) 中连接两个表时,我遇到以下问题,恳请您的帮助。

我有 2 个表,RevenuesCashins

Revenues:

RevenueID ProductID InvoiceNo Amount
123 456 987 1000
234 456 987 1000

Cashins:

CashinID ProductID InoviceNo Amount
ABC 456 987 1000
CDE 456 987 1000

目标是自动将现金与收入匹配(但只有一次!)。

两个表都有其唯一 ID,但用于连接这些表的列是

  • 产品编号
  • 发票号
  • 数量

对于具有这些条件的每个表中只有一行的条目,一切正常。

但有时,这些列中有几行具有相同的值(如上)但具有唯一 ID(这不是错误,但应该是这样)。

它的问题是,加入它会产生笛卡尔积。

要重新创建表,请使用以下语句:

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)

期望的输出:

RevenueID ProductID InvoiceNo Amount CashinID
123 456 987 1000 ABC
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

结果:

RevenueID ProductID InvoiceNo Amount CashinID
123 456 987 1000 ABC
123 456 987 1000 CDE
234 456 987 1000 ABC
234 456 987 1000 CDE

这在理论上是有道理的,但我似乎无法找到每行只使用一次的解决方案。

我发现并尝试过的两件事是窗口函数和带有TOP(1)选择的OUTER APPLY函数。两者都得出相同的结果:

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

它从 Revenues 表中返回所需的列,但仅匹配 Cashins 表中的第一个外观:

RevenueID ProductID InvoiceNo Amount CashinID
123 456 987 1000 ABC
234 456 987 1000 ABC

我还考虑过更新 Revenues 表,以便匹配的 CashinID 位于一行的旁边,然后每次检查 CashinID 是否尚未在该表中使用,但我做不到工作...

非常感谢您在正确方向上提供的任何帮助或提示!

【问题讨论】:

  • 您的数据关系存在根本问题。您通过非唯一标识符加入。我看到的唯一可能的解决方案是对 Revenues 中的行进行排序,对 Cashins 中的行进行排序,然后匹配 row_number。

标签: sql-server join unique window-functions cartesian-product


【解决方案1】:

正如我在评论中所说,您的数据关系存在根本问题。您需要在您的一个表中引用另一个表的唯一标识符。如果您不这样做,那么您只能在两个表中对您的交易进行排序并按行号加入它们。您正在使用希望和祈祷来加入您的数据,而不是信誉不佳的标识符。

WITH RevPrelim AS (
    SELECT *
      , ROW_NUMBER() OVER(PARTITION BY ProductID, InvoiceNo, Amount ORDER BY RevenueID) as row_num
    FROM [Revenues] R
) CashinsPrelim AS (
    SELECT *
        , ROW_NUMBER() OVER(PARTITION BY ProductID, InvoiceNo, 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
        ) OR c.row_num = r.row_num

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    • 2011-02-07
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多