Jak znaleźć duplikaty w wielu kolumnach?

98

Więc chcę zrobić coś takiego jak ten kod sql poniżej:

select s.id, s.name,s.city 
from stuff s
group by s.name having count(where city and name are identical) > 1

Aby otrzymać następujące dane (ale zignoruj, jeśli pasuje tylko nazwa lub tylko miasto, musi znajdować się w obu kolumnach):

id      name  city   
904834  jim   London  
904835  jim   London  
90145   Fred  Paris   
90132   Fred  Paris
90133   Fred  Paris
NimChimpsky
źródło

Odpowiedzi:

137

Powielone iddla par namei city:

select s.id, t.* 
from [stuff] s
join (
    select name, city, count(*) as qty
    from [stuff]
    group by name, city
    having count(*) > 1
) t on s.name = t.name and s.city = t.city
Michał Powaga
źródło
Zauważ, że jeśli jedna namelub cityzawierać null, wtedy nie należy zgłaszać w zapytaniu zewnętrznym, ale będą dopasowane do zapytania wewnętrznej.
Adam Parkin
3
Jeśli wartości mogą ewentualnie zawierać nullto (chyba że czegoś mi brakuje), należy zmienić to na CROSS JOIN(pełny iloczyn kartezjański), a następnie dodać WHEREklauzulę taką jak:WHERE ((s.name = t.name) OR (s.name is null and t.name is null)) AND ((s.city = t.city) OR (s.city is null and t.city is null))
Adam Parkin
56
 SELECT name, city, count(*) as qty 
 FROM stuff 
 GROUP BY name, city HAVING count(*)> 1
Sunnny
źródło
10

Coś takiego wystarczy. Nie wiem o wydajności, więc zrób kilka testów.

select
  id, name, city
from
  [stuff] s
where
1 < (select count(*) from [stuff] i where i.city = s.city and i.name = s.name)
ssarabando
źródło
6

Użycie count(*) over(partition by...)zapewnia prosty i skuteczny sposób znajdowania niechcianych powtórzeń, a także wyświetla listę wszystkich dotkniętych wierszy i wszystkich żądanych kolumn:

SELECT
    t.*
FROM (
    SELECT
        s.*
      , COUNT(*) OVER (PARTITION BY s.name, s.city) AS qty
    FROM stuff s
    ) t
WHERE t.qty > 1
ORDER BY t.name, t.city

Podczas gdy najnowsze wersje RDBMS obsługują count(*) over(partition by...) MySQL V 8.0 wprowadzono „funkcje okna”, jak widać poniżej (w MySQL 8.0)

CREATE TABLE stuff(
   id   INTEGER  NOT NULL
  ,name VARCHAR(60) NOT NULL
  ,city VARCHAR(60) NOT NULL
);
INSERT INTO stuff(id,name,city) VALUES 
  (904834,'jim','London')
, (904835,'jim','London')
, (90145,'Fred','Paris')
, (90132,'Fred','Paris')
, (90133,'Fred','Paris')

, (923457,'Barney','New York') # not expected in result
;
SELECT
    t.*
FROM (
    SELECT
        s.*
      , COUNT(*) OVER (PARTITION BY s.name, s.city) AS qty
    FROM stuff s
    ) t
WHERE t.qty > 1
ORDER BY t.name, t.city
    id | nazwa | miasto | szt
-----: | : --- | : ----- | -:
 90145 | Fred | Paryż | 3
 90132 | Fred | Paryż | 3
 90133 | Fred | Paryż | 3
904834 | jim | Londyn | 2
904835 | jim | Londyn | 2

db <> skrzypce tutaj

Funkcje okna. MySQL obsługuje teraz funkcje okna, które dla każdego wiersza zapytania wykonują obliczenia przy użyciu wierszy powiązanych z tym wierszem. Należą do nich funkcje takie jak RANK (), LAG () i NTILE (). Ponadto kilka istniejących funkcji agregujących może być teraz używanych jako funkcje okna; na przykład SUMA () i ŚREDNIA (). Aby uzyskać więcej informacji, zobacz Sekcja 12.21, „Funkcje okna” .

Used_By_Already
źródło
3

Trochę późno do gry w tym poście, ale uznałem ten sposób za dość elastyczny / wydajny

select 
    s1.id
    ,s1.name
    ,s1.city 
from 
    stuff s1
    ,stuff s2
Where
    s1.id <> s2.id
    and s1.name = s2.name
    and s1.city = s2.city
MattD
źródło
2

Musisz dołączyć do rzeczy i dopasować nazwę i miasto. Następnie grupuj według liczby.

select 
   s.id, s.name, s.city 
from stuff s join stuff p ON (
   s.name = p.city OR s.city = p.name
)
group by s.name having count(s.name) > 1
Anja
źródło
Niepowodzenia w SQL Server: wszystkie
nieagregowane
0

Biorąc pod uwagę tabelę pomostową z 70 kolumnami i tylko 4 reprezentującymi duplikaty, ten kod zwróci niewłaściwe kolumny:

SELECT 
    COUNT(*)
    ,LTRIM(RTRIM(S.TransactionDate)) 
    ,LTRIM(RTRIM(S.TransactionTime))
    ,LTRIM(RTRIM(S.TransactionTicketNumber)) 
    ,LTRIM(RTRIM(GrossCost)) 
FROM Staging.dbo.Stage S
GROUP BY 
    LTRIM(RTRIM(S.TransactionDate)) 
    ,LTRIM(RTRIM(S.TransactionTime))
    ,LTRIM(RTRIM(S.TransactionTicketNumber)) 
    ,LTRIM(RTRIM(GrossCost)) 
HAVING COUNT(*) > 1

.

Don G.
źródło