70

我对具有多个连接的大量大表(行和列)进行了查询,但是其中一个表有一些重复的数据行,导致我的查询出现问题。由于这是来自另一个部门的只读实时提要,因此我无法修复该数据,但是我试图防止我的查询出现问题。

鉴于此,我需要将此废话数据作为左连接添加到我的良好查询中。数据集如下所示:

IDNo    FirstName   LastName    ...
-------------------------------------------
uqx     bob     smith
abc     john        willis
ABC     john        willis
aBc     john        willis
WTF     jeff        bridges
sss     bill        doe
ere     sally       abby
wtf     jeff        bridges
...

(大约 2 打列和 100K 行)

我的第一直觉是执行一个 distinct 给我大约 80K 行:

SELECT DISTINCT P.IDNo
FROM people P

但是当我尝试以下操作时,我得到了所有的行:

SELECT DISTINCT P.*
FROM people P

或者

SELECT 
    DISTINCT(P.IDNo) AS IDNoUnq 
    ,P.FirstName
    ,P.LastName
    ...etc.    
FROM people P

然后我想我会在所有列上做一个 FIRST() 聚合函数,但这也感觉不对。从语法上讲,我在这里做错了吗?

更新: 只是想注意:这些记录是基于上面列出的 ID 的非键/非索引字段的重复项。ID 是一个文本字段,虽然具有相同的值,但它与导致问题的其他数据不同。

4

7 回答 7

78

distinct不是函数。它总是对选择列表的所有列进行操作。

您的问题是典型的“每组最大 N”问题,可以使用窗口函数轻松解决:

select ...
from (
  select IDNo,
         FirstName,
         LastName,
         ....,
         row_number() over (partition by lower(idno) order by firstname) as rn 
  from people 
) t
where rn = 1;

使用该order by子句,您可以选择要选择的重复项。

上面可以在左连接中使用,见下文:

select ...
from x
  left join (
    select IDNo,
           FirstName,
           LastName,
           ....,
           row_number() over (partition by lower(idno) order by firstname) as rn 
    from people 
  ) p on p.idno = x.idno and p.rn = 1
where ...
于 2015-01-06T12:14:30.403 回答
7

添加一个标识列 (PeopleID),然后使用相关子查询返回每个值的第一个值。

SELECT *
FROM People p
WHERE PeopleID = (
    SELECT MIN(PeopleID) 
    FROM People 
    WHERE IDNo = p.IDNo
)
于 2014-02-05T21:17:31.697 回答
3

原来我做错了,我需要首先执行一个嵌套选择,首先是重要的列,然后再做一个不同的选择,以防止“唯一”数据的垃圾列破坏我的好数据。以下似乎已经解决了这个问题......但我稍后会尝试完整的数据集。

SELECT DISTINCT P2.*
FROM (
  SELECT
      IDNo
    , FirstName
    , LastName
  FROM people P
) P2

以下是一些要求的播放数据:http ://sqlfiddle.com/#!3/050e0d/3

CREATE TABLE people
(
       [entry] int
     , [IDNo] varchar(3)
     , [FirstName] varchar(5)
     , [LastName] varchar(7)
);

INSERT INTO people
    (entry,[IDNo], [FirstName], [LastName])
VALUES
    (1,'uqx', 'bob', 'smith'),
    (2,'abc', 'john', 'willis'),
    (3,'ABC', 'john', 'willis'),
    (4,'aBc', 'john', 'willis'),
    (5,'WTF', 'jeff', 'bridges'),
    (6,'Sss', 'bill', 'doe'),
    (7,'sSs', 'bill', 'doe'),
    (8,'ssS', 'bill', 'doe'),
    (9,'ere', 'sally', 'abby'),
    (10,'wtf', 'jeff', 'bridges')
;
于 2013-11-07T17:13:35.313 回答
3

经过仔细考虑,这个困境有几个不同的解决方案:

聚合所有 内容 对每一列使用聚合来获取最大或最小的字段值。这就是我正在做的,因为它需要 2 条部分填写的记录并“合并”数据。

http://sqlfiddle.com/#!3/59cde/1

SELECT
  UPPER(IDNo) AS user_id
, MAX(FirstName) AS name_first
, MAX(LastName) AS name_last
, MAX(entry) AS row_num
FROM people P
GROUP BY 
  IDNo

获取第一条(或最后一条记录)

http://sqlfiddle.com/#!3/59cde/23

-- ------------------------------------------------------
-- Notes
-- entry: Auto-Number primary key some sort of unique PK is required for this method
-- IDNo:  Should be primary key in feed, but is not, we are making an upper case version
-- This gets the first entry to get last entry, change MIN() to MAX()
-- ------------------------------------------------------

SELECT 
   PC.user_id
  ,PData.FirstName
  ,PData.LastName
  ,PData.entry
FROM (
  SELECT 
      P2.user_id
     ,MIN(P2.entry) AS rownum
  FROM (
    SELECT
        UPPER(P.IDNo) AS user_id 
      , P.entry 
    FROM people P
  ) AS P2
  GROUP BY 
    P2.user_id
) AS PC
LEFT JOIN people PData
ON PData.entry = PC.rownum
ORDER BY 
   PData.entry
于 2013-11-07T19:04:45.177 回答
2

尝试这个

 SELECT *
 FROM people P 
 where P.IDNo in (SELECT DISTINCT IDNo
              FROM people)
于 2013-11-07T12:00:15.553 回答
2

根据重复行的性质,您似乎只想对这些列进行区分大小写。在这些列上设置排序规则应该是您所追求的:

SELECT DISTINCT p.IDNO COLLATE SQL_Latin1_General_CP1_CI_AS, p.FirstName COLLATE SQL_Latin1_General_CP1_CI_AS, p.LastName COLLATE SQL_Latin1_General_CP1_CI_AS
FROM people P

http://msdn.microsoft.com/en-us/library/ms184391.aspx

于 2014-11-03T04:40:59.217 回答
1

使用交叉应用或外部应用,通过这种方式,您可以限制要从具有重复项的表连接到第一次命中的数据量。

Select 
    x.*,
    c.*
from 
    x
Cross Apply 
    (
        Select 
            Top (1)
            IDNo,
            FirstName,
            LastName,
            ...., 
        from 
            people As p
        where 
            p.idno = x.idno
        Order By 
            p.idno //unnecessary if you don't need a specific match based on order
    ) As c

交叉应用的行为类似于内部连接,外部应用类似于左连接

SQL Server 交叉应用和外部应用

于 2021-06-03T09:20:52.510 回答