【问题标题】:Joining tables on a non-key column and eliminating duplicates?在非键列上连接表并消除重复项?
【发布时间】:2018-07-28 14:07:21
【问题描述】:

我继承了一个结构很差的数据库,我的任务是从中获取有用的信息。我有两个表,Transactions 和 CodeDefinitions。

这是事务表的简化示例

ID (primary key)
CodeName (varchar(50))
Description(varchar(255)
TranDate (datetime)
Amount (money)

CodeDefinitions 表的简化结构

ID (primary key)
Name (varchar(50))
Description (varchar(255))

两个表中的示例数据

事务表(包含 500,000 行事务)

ID      CodeName    Description             TranDate    Amount
510348  HRCUT       Haircut                 2017-11-02  30.00
510349  RINSE       Rinse - Extra Deluxe    2017-11-02  45.00
510350  PERM        Luxury Perm             2017-11-02  80.00
510351  HRCUT       Haircut                 2017-11-02  30.00

CodeDefinitions 表(2000 行只有 SKU)

ID      Name        Description
684     RINSE       Rinse - Extra Deluxe
686     HRCUT       Haircut
730     PERM        Luxury Perm
2203    HRCUT       Haircut (note the duplicate name and description!)

以前的数据库开发人员没有费心在 CodeDefinitions.Code 上设置 UNIQUE 约束,甚至对 Transactions.Code 设置 FK 约束。所以这意味着用户可以输入相同的 CodeDefinitions.Name,唯一的区别是 CodeDefinitions.ID。由于 Transactions 表仅包含特定 CodeDefinition 的名称和描述,而不包含 ID,因此尝试过滤掉重复项非常麻烦

我想要做的是能够通过在 Transactions.CodeID 上加入以消除重复,准确地计算给定时间日期已售出多少理发的金额。如果两个 SKU 具有相同的名称,请选择最高的 CodeDefinitions.ID。如果可能的话,我想避免使用 DISTINCT、GROUP BY 或 MAX,因为它们将查询执行时间从 5 秒缩短到 5 分钟(除非我编写查询的方式有问题,否则我不太熟悉MySQL 优化器)

我曾尝试Left JOIN on t.CodeName = cd.Name,但这会将所有CodeDefinitions.ID 返回为NULL。我需要能够SELECT the CodeDefinitions.ID进行过滤。

我的查询:

SELECT
t.ID   AS TranID
, t.CodeName AS ProcedureCode
, cd.ID  AS CodeID
, Description
, t.TranDate
, t.Amount
FROM transactions t
LEFT OUTER JOIN CodeDefinitions cd ON (T.Description = cd.Description)
WHERE TranDate BETWEEN '2017-11-1' AND '2017-11-31'

预期结果:

TranID  ProcedureCode CodeID Description            TranDate    TranAmt
510348  HRCUT         2203   Haircut                2017-11-02  30.00
510349  RINSE         684    Rinse - Extra Deluxe   2017-11-02  45.00
510350  PERM          730    Luxury Perm            2017-11-02  80.00
510351  HRCUT         2203   Haircut                2017-11-02  30.00

实际结果:

TranID  ProcedureCode CodeID Description          TranDate   TranAmt
510348  HRCUT         686    Haircut              2017-11-02 30.00
510348  HRCUT         2203   Haircut              2017-11-02 30.00
510349  RINSE         684    Rinse - Extra Deluxe 2017-11-02 45.00
510350  PERM          730    Luxury Perm          2017-11-02 80.00
510351  HRCUT         686    Haircut              2017-11-02 30.00
510351  HRCUT         2203   Haircut              2017-11-02 30.00

你如何摆脱这些重复并在每组重复中选择最高的 CodeID?

【问题讨论】:

    标签: mysql sql database duplicates left-join


    【解决方案1】:

    由于 CodeDefinitions 表的数据较少,因此对其应用排名以从组中选择具有最大 ID 的行的成本不会很高。所以我们可以先找出 CodeDefinitions 表中每个 Name 的最大 ID。然后我们可以在左外连接中使用它和事务表。

    SELECT
    t.ID   AS TranID
    , t.CodeName AS ProcedureCode
    , cd.ID  AS CodeID
    , Description
    , t.TranDate
    , t.Amount
    FROM transactions t
    LEFT OUTER JOIN (select * from 
    (select ID,Name,rank() over (parition by name order by ID desc) as rnum from CodeDefinitions) 
    where rnum=1) cd 
    ON (T.Description = cd.Name)
    WHERE TranDate BETWEEN '2017-11-1' AND '2017-11-31'
    

    我所做的只是用没有重复的 Codedefinitions 视图替换了您的 Codedefinitions 表,并且只包含每个不同名称的最大 ID 行。

    【讨论】:

      【解决方案2】:

      您可以使用分组子选择来查找具有最高 ID 的不同代码,因为您的 ID 似乎是数字 -

      SELECT t.ID AS TranID, t.CodeName AS ProcedureCode, cd.ID AS CodeID,
          cd.Description, t.TranDate, t.Amount
      FROM (SELECT MAX(ID) AS ID, Name FROM CodeDefinitions GROUP BY Name) ss
      JOIN CodeDefinitions cd 
      ON cd.name = ss.name AND cd.ID = ss.ID
      RIGHT OUTER JOIN
      transactions t
      ON (T.CodeName = cd.Name)
      WHERE TranDate BETWEEN '2017-11-1' AND '2017-11-31'
      

      SQLFiddle

      这将从您的选择中删除与另一个名称相同的代码定义。如果你想真正从数据库中删除它们 -

      DELETE FROM CodeDefinitions
      WHERE ID NOT IN(SELECT MAX(ID) AS ID FROM CodeDefinitions GROUP BY Name) ss
      

      【讨论】:

        猜你喜欢
        • 2014-12-31
        • 1970-01-01
        • 1970-01-01
        • 2020-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-03
        相关资源
        最近更新 更多