it-swarm.com.de

SQL Left Join nur erste Übereinstimmung

Ich habe eine Abfrage für eine große Anzahl von großen Tabellen (Zeilen und Spalten) mit einer Anzahl von Verknüpfungen. Eine der Tabellen weist jedoch einige doppelte Datenzeilen auf, die Probleme für meine Abfrage verursachen. Da dies ein schreibgeschützter Echtzeit-Feed einer anderen Abteilung ist, kann ich diese Daten nicht korrigieren. Ich versuche jedoch, Probleme bei meiner Abfrage zu vermeiden.

Angesichts dessen muss ich diese Mistdaten als Links-Join zu meiner guten Abfrage hinzufügen. Der Datensatz sieht folgendermaßen aus:

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

(ca. 2 Dutzend Spalten und 100.000 Zeilen)

Mein erster Instinkt war, ein bestimmtes Ergebnis zu erzielen, das mir ungefähr 80.000 Zeilen bescherte:

SELECT DISTINCT P.IDNo
FROM people P

Aber wenn ich Folgendes versuche, bekomme ich alle Zeilen zurück:

SELECT DISTINCT P.*
FROM people P

OR

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

Ich dachte dann, ich würde eine FIRST () - Aggregatfunktion für alle Spalten ausführen, aber das fühlt sich auch falsch an. Mache ich hier syntaktisch etwas falsch?

pdate: Nur zur Kenntnis genommen: Diese Datensätze sind Duplikate, die auf einem oben aufgelisteten Nichtschlüssel-/Nichtindex-ID-Feld basieren. Die ID ist ein Textfeld, das zwar denselben Wert hat, sich jedoch von den anderen Daten unterscheidet, die das Problem verursachen.

37
Dave

Es stellte sich heraus, dass ich es falsch gemacht habe. Ich musste zuerst eine verschachtelte Auswahl von nur den wichtigen Spalten durchführen und eine eindeutige Auswahl vornehmen, um zu verhindern, dass Mülleimerspalten mit "eindeutigen" Daten meine guten Daten beschädigen. Das folgende scheint das Problem behoben zu haben ... aber ich werde den vollständigen Datensatz später erneut testen.

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

Hier sind einige angeforderte Spieldaten: 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')
;
2
Dave

distinct ist nicht eine Funktion. Es wird immer auf alle Spalten der Auswahlliste angewendet.

Ihr Problem ist ein typisches "größtes N pro Gruppe" -Problem, das leicht mit einer Fensterfunktion gelöst werden kann:

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

Verwendung der order by-Klausel können Sie auswählen, welche der Duplikate Sie auswählen möchten.

Das obige kann in einem Link-Join verwendet werden:

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

Fügen Sie eine Identitätsspalte (PeopleID) hinzu und verwenden Sie dann eine korrelierte Unterabfrage, um den ersten Wert für jeden Wert zurückzugeben.

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

Abhängig von der Art der doppelten Zeilen möchten Sie lediglich die Groß- und Kleinschreibung dieser Spalten berücksichtigen. Das Festlegen der Sortierung für diese Spalten sollte Ihren Wünschen entsprechen:

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

2
Fiddles

Nach sorgfältiger Überlegung hat dieses Dillema einige unterschiedliche Lösungen:

Alles aggregieren Verwenden Sie für jede Spalte ein Aggregat, um den größten oder kleinsten Feldwert zu erhalten. Dies ist, was ich mache, da es 2 teilweise ausgefüllte Datensätze nimmt und die Daten "zusammenführt".

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

Erste (oder letzte) Aufzeichnung abrufen

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
1
Dave

Versuche dies

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