只有 SQL 左连接第一次匹配

我有一个针对大量大型表(行和列)以及大量联接的查询,但是其中一个表有一些重复的数据行,从而导致我的查询出现问题。因为这是来自另一个部门的只读实时提要,所以我无法修复这些数据,但是我试图防止我的查询出现问题。

鉴于此,我需要将这些垃圾数据作为左连接添加到我的好查询中。数据集看起来是这样的:

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
...

(大约24列,100K 行)

我的第一反应是执行一个独特的给了我大约8万行:

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 是一个文本字段,虽然具有相同的值,但它与引起问题的其他数据的情况不同。

206593 次浏览

试试这个

 SELECT *
FROM people P
where P.IDNo in (SELECT DISTINCT IDNo
FROM people)

事实证明我的做法是错误的,我需要首先执行一个嵌套选择,只选择重要的列,然后做一个明确的选择,以防止“唯一”数据的垃圾列损坏我的好数据。下面的内容似乎已经解决了这个问题... ... 不过我稍后将尝试使用完整的数据集。

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')
;

经过仔细考虑,这种困境有几种不同的解决办法:

聚合所有东西 在每列上使用聚合可获得最大或最小字段值。这是我所做的,因为它需要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

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

SELECT *
FROM People p
WHERE PeopleID = (
SELECT MIN(PeopleID)
FROM People
WHERE IDNo = p.IDNo
)

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

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

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 ...

使用“交叉应用”或“外部应用”,这样就可以将从表中连接的数据量限制在第一次命中时的重复数据量。

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

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

SQLServer 交叉应用程序和外部应用程序