【问题标题】:How do I filter out duplicates?如何过滤掉重复项?
【发布时间】:2014-12-19 07:29:27
【问题描述】:

我有两个设计不佳的表格 Form 和 FormDetails 我正在尝试清理。

Form 表包含有关我的应用程序中表单状态的信息:

+-----------------------------------------+
| form_id | status_id   | form_created_by |
+-----------------------------------------+
| 1       | 1           | abc             |
+-----------------------------------------+
| 2       | 3           | def             |
+-----------------------------------------+

form_id,在 Form 表中,是主键

FormDetails 表包含有关表单的其他信息:

+-----------------------------------------+
| form_id | status_id   | process_id      |
+-----------------------------------------+
| 1       | 1           | 1               |
+-----------------------------------------+
| 2       | 2           | 1               |
+-----------------------------------------+
| 2       | 3           | 1               |
+-----------------------------------------+
| 2       | 3           | 1               |
+-----------------------------------------+

form_id,在 Form 表中,是 NOT 主键或外键。没有限制。该表设计不当,随着时间的推移,重复数据已添加到该表中。

我想通过将唯一数据复制到新的 FormDetails 表中并将 form_id 设置为 Form 表的外键来清理此表。

为了实现这一点,我尝试了以下查询:

select * 
from FormDetails fd
right join Form f on f.form_id = fd.form_id and f.status_id = fd.status_id

不幸的是,我仍然收到带有重复 form_id 的行,因为 form_id=2 有两行 status_id=3。

+-----------------------------------------+
| form_id | status_id   | process_id      |
+-----------------------------------------+
| 1       | 1           | 1               |
+-----------------------------------------+
| 2       | 3           | 1               |
+-----------------------------------------+
| 2       | 3           | 1               |
+-----------------------------------------+

我要写的是一个查询:选择 FormDetails 中与当前表单相对应的所有行。如果有重复项,只需选择其中一个即可。

知道如何编写这样的查询吗?

我想看到的是:

+-----------------------------------------+
| form_id | status_id   | process_id      |
+-----------------------------------------+
| 1       | 1           | 1               |
+-----------------------------------------+
| 2       | 3           | 1               |
+-----------------------------------------+

【问题讨论】:

  • 你用的是什么数据库?
  • @GordonLinoff SQL Server 2005。我现在添加了 sql-server 作为标签...
  • 我只是凭记忆,但我认为 SELECT DISTINCT for MSSQL 应该可以解决问题。

标签: sql sql-server select duplicates


【解决方案1】:

您可以使用 ANSI SQL 函数 row_number() 为每个 form_id 枚举 FormDetails 中的行。你不关心特定的顺序是什么,所以下面应该做你想做的:

select form_id, status_id, process_id
from (select fd.*, row_number() over (partition by form_id order by form_id) as seqnum
      from formdetails fd
     ) fd
where exists (select 1 from forms f where fd.form_id = f.form_id) and
      seqnum = 1;

【讨论】:

  • 这似乎是朝着正确方向迈出的一步,但有些地方不太对劲。当我select count(*) from forms 时,我得到 21607。当我计算您的查询返回的行数时,我得到 21581。这让我知道您的查询没有选择 26 个表单。
  • 我认为这个查询的问题在于它没有为表单详细信息提供一个序列 1,该表单详细信息的状态与表单表中的状态匹配。知道如何调整您的查询以允许这样做吗?
  • @mezoid 。 . .您的问题从“选择 FormDetails 中的所有行”开始。 Forms 中似乎有 26 个表格不在 FormDetails 中。如果你想要这些,只需删除 exists 子句。
  • 我设法解决了这个问题。您的回答肯定为我指明了正确的方向。我已经用我最终使用的解决方案发布了我自己的答案。感谢您对此的帮助。
【解决方案2】:

使用SELECT DISTINCT 语句

SELECT DISTINCT * 
FROM FormDetails fd
RIGHT JOIN Form f ON f.form_id = fd.form_id AND f.status_id = fd.status_id

【讨论】:

    【解决方案3】:

    Form 表中找到Distinct rows,然后将Right Outer joinFormdetails 表一起使用

    SELECT f.form_id,
           f.status_id,
           f.process_id,
    FROM   FormDetails fd
           RIGHT JOIN (SELECT DISTINCT form_id,
                                       status_id,
                                       process_id,
                       FROM   Form) f
                   ON f.form_id = fd.form_id
                      AND f.status_id = fd.status_id 
    

    【讨论】:

      【解决方案4】:
      CREATE TABLE #Form
      (
      formId INT,
      StatusID INT,
      FormCreate VARCHAR(30)
      )
      
      INSERT INTO #Form VALUES(1,1,'abc')
      INSERT INTO #Form VALUES(2,3,'def')
      
      CREATE TABLE #Form1
      (
      formId INT,
      StatusID INT,
      ProcessID INT
      )
      
      INSERT INTO #Form1 VALUES(1,1,1)
      INSERT INTO #Form1 VALUES(2,2,1)
      INSERT INTO #Form1 VALUES(2,3,1)
      INSERT INTO #Form1 VALUES(2,3,1)
      
      SELECT DISTINCT * FROM #Form fd
      LEFT JOIN #Form1 f ON f.formId = fd.formId AND f.StatusID = fd.StatusID
      

      【讨论】:

      • 您的解决方案工作正常,但不要更改 formDetails 表。但是从那里,可以完全重新创建 formDetail 表。所以它也可以工作。
      【解决方案5】:

      我会这样做来解决您的问题

      1- 创建一个新字段,作为您在 formDetails 表中的唯一键。

      Alter Table formDetails Add formDetail_id int Not Null Identity(1,1) Constraint PKFormDetail Primary Key
      

      2- 然后我将创建一个临时字段,用于对现有数据进行记录评估。我的意思是创建的临时字段(我将其命名为 Eval)可以包含来自您的 3 个字段(form_id、status_id 和 process_id)的计算值。

      Alter Table formDetails Add Eval int
      
      Update formDetails Set Eval = form_id * 100000 + status_id * 1000 + process_id 
      

      无论您采取什么解决方案,您都必须注意结果不会包含“冲突”(取决于表中包含的数据。我的意思是在这种情况下 process_id 必须低于 1000 并且 status_id * 1000 必须低于 100000 )。

      3- 像这样删除不需要的行:

        Delete x
        FROM ( select *, rn=row_number() over (partition by Eval order by formDetail_id) 
               from [dbo].[formDetails]) x
        Where rn > 1;
      

      (您可以在执行删除操作之前测试它执行选择而不是删除以确保结果)。

      4- 从表中删除临时字段。

      Alter Table formDetails Drop Column Eval
      

      5- 您现在可以直接在两个表的 form_id 字段之间设置外键。

      Alter Table formDetails Add Constraint FKFormId Foreign Key (form_id) references forms(form_id)
      

      【讨论】:

        【解决方案6】:

        在一些朋友的帮助下,我设法找到了这个问题的答案。我的问题最接近的答案是@Gordon Linoff,但它仍然不是我想要的。

        为了确定 Form 表中有多少行在 FormDetails 表中没有任何对应记录,我们编写了以下查询:

        SELECT *
        FROM Form
        LEFT OUTER JOIN FormDetails ON Form.form_id = FormDetails.form_id 
                   AND Form.status_id = FormDetails.status_id
        WHERE
              FormDetails.form_id IS NULL
        

        当我对我的表运行它时,它返回了 38 行。这个数字帮助我们确定我们编写的查询是否返回了正确的行数。 @Gordon Linoff 的答案相差 26 行,所以我认为它返回的行数比预期的多,但它比所有其他答案更接近,并且朝着正确的方向迈出了一步。

        然后我们将这个使用CROSS APPLY(我以前从未见过的sql)的查询放在一起。

        查询如下所示:

        SELECT ad.*
        FROM Form f
        CROSS APPLY (SELECT TOP 1 * FROM FormDetails fd WHERE 
                     f.form_id = fd.form_id AND f.status_id = fd.status_id) AS ad
        

        当我将 Form 表中的行数与此查询返回的行数进行比较时,我得到了 38 的差异。这似乎表明这是解决我的问题的好方法。

        我从没想过使用 CROSS APPLY 来解决这个问题……但我想我会在此处记录解决方案,以防将来对某人有所帮助。

        【讨论】:

          猜你喜欢
          • 2018-11-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-12
          • 2019-10-16
          相关资源
          最近更新 更多