sql查詢消除重複數據
如數據庫有一名為biau的表:
id | name | address |
---|---|---|
1 | 小王 | 重慶 |
2 | 小張 | 北京 |
3 | 小明 | 上海 |
4 | 小紅 | 重慶 |
要求用sql查詢出按不同address(地址)的排列的所有數據(即地址不能重複,如有重複只選取一次,其他列數據可以重複)
查询SQL如下
id | name | address |
---|---|---|
4 | 小紅 | 重慶 |
3 | 小明 | 上海 |
2 | 小張 | 北京 |
1 | select * from biau where id in (select max(id) from biau group by address) order by id desc |
常見的SQL問題:
◆選擇重複,消除重複和選擇出序列
有例表:emp
| emp_no | name | age |
|:————-:|:————-:|:————-:|
| 001 | Tom | 17 |
| 002 | Sun | 14 |
| 003 | Tom | 15 |
| 004 | Tom | 16 |
要求:
列出所有名字重複的人的記錄
(1)最直觀的思路:要知道所有名字有重複人資料,首先必須知道哪個名字重複了:1
select name from emp group by name having count(*)>1
所有名字重複人的記錄是:1
select * from emp where name in(select name from emp group by name having count(*)>1)
(2)稍微再聰明一點,就會想到,如果對每個名字都和原表進行比較,大於2個人名字與這條記錄相同的就是合格的 ,就有1
select * from emp where (select count(*) from emp e where e.name=emp.name)>1
–注意一下這個>1,想下如果是 =1,如果是 =2 如果是>2 如果 e 是另外一張表 而且是=0那結果 就更好玩了:)
這個過程是 在判斷工號為001的人的時候先取得 001的名字(emp.name) 然後和原表的名字進行比較 e.name
注意e是emp的一個別名。
再稍微想得多一點,就會想到,如果有另外一個名字相同的人工號不與她他相同那麼這條記錄符合要求:1
select * from emp where exists (select * from emp e where e.name=emp.name and e.emp_no<>emp.emp_no)
此思路的join寫法:1
select emp.* from emp,emp e where emp.name=e.name and emp.emp_no<>e.emp_no
/* 這個語句較規範的join寫法是1
select emp.* from emp inner join emp e on emp.name=e.name and emp.emp_no<>e.emp_no
但個人比較傾向於前一種寫法,關鍵是更清晰 */
b、有例表:emp
name | age |
---|---|
Tom | 16 |
Sun | 14 |
Tom | 16 |
Tom | 16 |
—————————————————-清除重複—————————————————-
過濾掉所有多餘的重複記錄
(1)我們知道distinct、group by 可以過濾重複,於是就有最直觀的1
select distinct * from emp 或 select name,age from emp group by name,age
獲得需要的數據,如果可以使用臨時表就有解法:1
select distinct * into #tmp from emp delete from emp insert into emp select * from #tmp
(2)但是如果不可以使用臨時表,那該怎麼辦?
我們觀察到我們沒辦法區分數據(物理位置不一樣,對 SQL Server來說沒有任
何區別),思路自然是想辦法把數據區分出來了,既然現在的所有的列都沒辦法區分數據,唯一的辦法就是再加個列讓它區分出來,加什麼列好?最佳選擇是identity列:1
alter table emp add chk int identity(1,1)
表示例:
name | age | chk |
---|---|---|
Tom | 16 | 1 |
Sun | 14 | 2 |
Tom | 16 | 3 |
Tom | 16 | 4 |
重複記錄可以表示為:1
select * from emp where (select count(*) from emp e where e.name=emp.name)>1
要刪除的是:1
delete from emp where (select count(*) from emp e where e.name=emp.name and e.chk>=emp.chk)>1
再把添加的列刪掉,出現結果。1
alter table emp drop column chk
(3)另一個思路:
視圖1
select min(chk) from emp group by name having count(*)>1
獲得有重複的記錄chk最小的值,於是可以1
delete from emp where chk not in (select min(chk) from emp group by name)
寫成join的形式也可以:
(1)有例表:emp
emp_no | name | age |
---|---|---|
001 | Tom | 17 |
002 | Sun | 14 |
003 | Tom | 15 |
004 | Tom | 16 |
◆要求生成序列號
(1)最簡單的方法,根據b問題的解法:1
2alter table emp add chk int identity(1,1)
或 select *,identity(int,1,1) chk into #tmp from emp
◆如果需要控制順序怎麼辦?1
select top 100000 *,identity(int,1,1) chk into #tmp from emp order by age
(2) 假如不可以更改表結構,怎麼辦?
如果不可以唯一區分每條記錄是沒有辦法的,在可以唯一區分每條記錄的時候,可以使用a 中的count的思路解決這個問題1
select emp.*,(select count(*) from emp e where e.emp_no<=emp.emp_no) from emp order by (select count(*) from emp e where e.emp_no<=emp.emp_no)