J'ai une requête sur un grand nombre de grandes tables (lignes et colonnes) avec un certain nombre de jointures. Cependant, l'une des tables contient des lignes de données dupliquées, ce qui pose des problèmes pour ma requête. Puisqu'il s'agit d'un flux en temps réel en lecture seule provenant d'un autre département, je ne peux pas corriger ces données. Cependant, j'essaie d'éviter les problèmes dans ma requête.
Compte tenu de cela, je dois ajouter ces données de merde en tant que jointure gauche à ma bonne requête. L'ensemble de données ressemble à:
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
...
(environ 2 douzaines de colonnes et 100 000 lignes)
Mon premier instinct a été de jouer un disque distinct, il m’a donné environ 80 000 lignes:
SELECT DISTINCT P.IDNo
FROM people P
Mais quand j'essaye ce qui suit, je récupère toutes les lignes:
SELECT DISTINCT P.*
FROM people P
OR
SELECT
DISTINCT(P.IDNo) AS IDNoUnq
,P.FirstName
,P.LastName
...etc.
FROM people P
J'ai alors pensé que je ferais une fonction d'agrégat FIRST () sur toutes les colonnes, même si ça ne va pas. Syntaxiquement, est-ce que je fais quelque chose de mal ici?
Mise à jour: Je voulais juste noter: Ces enregistrements sont des doublons basés sur un champ d'ID non clé/non indexé énuméré ci-dessus. L'ID est un champ de texte qui, même s'il a la même valeur, est un cas différent des autres données à l'origine du problème.
Il s'avère que je me suis trompé. Je devais d'abord effectuer une sélection imbriquée des seules colonnes importantes, puis effectuer une sélection distincte pour empêcher les colonnes de la corbeille de données "uniques" de corrompre mes bonnes données. Ce qui suit semble avoir résolu le problème ... mais je vais essayer sur l'ensemble de données complet plus tard.
SELECT DISTINCT P2.*
FROM (
SELECT
IDNo
, FirstName
, LastName
FROM people P
) P2
Voici quelques données de jeu demandées: http://sqlfiddle.com/#!3/050e0d/
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')
;
distinct
est pas une fonction. Il fonctionne toujours sur toutes les colonnes de la liste de sélection.
Votre problème est un problème typique du "plus grand nombre N par groupe" qui peut facilement être résolu en utilisant une fonction de fenêtre:
select ...
from (
select IDNo,
FirstName,
LastName,
....,
row_number() over (partition by lower(idno) order by firstname) as rn
from people
) t
where rn = 1;
En utilisant le order by
vous pouvez sélectionner les doublons à sélectionner.
Ce qui précède peut être utilisé dans une jointure gauche:
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 ...
Ajoutez une colonne d'identité (PeopleID), puis utilisez une sous-requête corrélée pour renvoyer la première valeur pour chaque valeur.
SELECT *
FROM People p
WHERE PeopleID = (
SELECT MIN(PeopleID)
FROM People
WHERE IDNo = p.IDNo
)
En fonction de la nature des lignes en double, il semble que tout ce que vous voulez, c'est que les colonnes respectent la casse. Définir le classement sur ces colonnes devrait être ce que vous cherchez:
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
Après mûre réflexion, ce dillema propose différentes solutions:
Tout regrouper Utilisez un agrégat sur chaque colonne pour obtenir la valeur de champ la plus grande ou la plus petite. C’est ce que je suis en train de faire car il faut 2 enregistrements partiellement remplis et "fusionne" les données.
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
Obtenir le premier (ou le dernier enregistrement)
http://sqlfiddle.com/#!3/59cde/2
-- ------------------------------------------------------
-- 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
Essaye ça
SELECT *
FROM people P
where P.IDNo in (SELECT DISTINCT IDNo
FROM people)