2009-06-11 5 views
8

Ho una tabella in SQL Server 2000 che sto cercando di interrogare in modo specifico. Il modo migliore per mostrare questo è con i dati di esempio.query SQL per restituire un singolo record per ogni valore univoco in una colonna

Ecco, [Addresses]:

Name   Street     City   State 
-------------------------------------------------------- 
Bob   123 Fake Street  Peoria  IL 
Bob   234 Other Street  Fargo   ND 
Jim   345 Main Street  St Louis  MO 

Questo è in realtà un esempio semplificato della struttura della tabella effettiva. La struttura del tavolo è completamente fuori dal mio controllo. Ho bisogno di una query che restituirà un singolo indirizzo per nome. Non importa quale indirizzo, solo che ce n'è solo uno. Il risultato potrebbe essere questo:

Name   Street     City   State 
-------------------------------------------------------- 
Bob   123 Fake Street  Peoria  IL 
Jim   345 Main Street  St Louis  MO 

Ho trovato una domanda simile here, ma nessuna delle soluzioni indicate lavoro nel mio caso perché non ho accesso a CROSS APPLY, e chiamando MIN() su ogni colonna mescolare diversi indirizzi insieme e anche se non mi interessa quale record viene restituito, deve essere una riga intatta, non un mix di righe diverse.

Le raccomandazioni per modificare la struttura della tabella non mi saranno d'aiuto. Sono d'accordo che questo tavolo è terribile, (è peggio di quanto mostrato qui) ma questo fa parte di un grande database ERP che non posso cambiare.

ci sono circa 3000 record in questa tabella. Non c'è una chiave primaria.

Qualche idea?

+0

può darvi un'idea il numero di record nella tabella? Ho alcune idee su come farlo ma potrebbe non essere molto veloce se ci sono migliaia/milioni di record. –

+0

Avete delle chiavi primarie su questo tavolo? –

+0

~ 3000 record e nessun PK, abbastanza sorprendentemente. Ho aggiunto queste informazioni alla domanda. – recursive

risposta

4

Bene, questo vi darà piuttosto male le prestazioni, ma penso che funzionerà

SELECT t.Name, t.Street, t.City, t.State 
FROM table t 
INNER JOIN (
    SELECT m.Name, MIN(m.Street + ';' + m.City + ';' + m.State) AS comb 
    FROM table m 
    GROUP BY m.Name 
) x 
    ON x.Name = t.Name 
    AND x.comb = t.Street + ';' + t.City + ';' + t.State 
+0

Sfortunatamente, non esiste un campo ID univoco per questa tabella. Si, lo so. Questo fa schifo. – recursive

+0

Penso che quelli facciano parte del nome della via. Non sembra avere alcuna chiave. –

+0

123, 234, ecc. Non sono ID, fanno parte dell'indirizzo. Il problema come indicato non include un campo ID nella tabella, il che è un peccato perché questa soluzione è ottima altrimenti. –

2
select distinct Name , street,city,state 
from table t1 where street = 
(select min(street) from table t2 where t2.name = t1.name) 
+0

Che non funziona: puoi avere lo stesso indirizzo in più città. – Tadmas

+0

Ci sono in realtà esempi di più righe che contengono lo stesso indirizzo, quindi in questi casi otterrei comunque dei duplicati. – recursive

+0

Lo stesso indirizzo per lo stesso nome? se nome + indirizzo è univoco, questo funzionerà, penso – tekBlues

0

Una leggera modifica di quanto sopra dovrebbe funzionare.

SELECT Name, Street, City, State 
FROM table t 
INNER JOIN (
    SELECT Name, MIN(Street) AS Street 
    FROM table m 
    GROUP BY Name 
) x 
    ON x.Name = t.Name AND x.Street = t.Street 

Ora, questo non funzionerà se avete la stessa strada, ma gli altri pezzi di informazione sono diverse (per esempio con errori di battitura).

O un hash più completa dovrebbe includere tutti i campi (ma è probabile che avere troppi per le prestazioni):

SELECT Name, Street, City, State 
FROM table t 
INNER JOIN (
    SELECT Name, MIN(Street + '|' + City + '|' + State) AS key 
    FROM table m 
    GROUP BY Name 
) x 
    ON x.Name = t.Name 
    AND x.key = Street + '|' + City + '|' + State 
+0

Ho controllato questo. Sfortunatamente, ci sono alcuni esempi di record che hanno la stessa strada. – recursive

+0

Dovrebbe essere il nome e la strada perché ci sia un problema. Puoi aggiungere più colonne per ridurre la probabilità di collisione, ma nel complesso non è una grande soluzione al problema. Se è possibile ottenere una chiave primaria nei registri è meglio. –

+0

Chiudi, tranne "tabella" x non ha un campo chiamato Street. Avresti bisogno di qualcosa come "SELECT Name, MIN (Street) a" e "ON x.Name = t.Name AND x.a = t.Street" – mbeckish

0

Non credo che si può fare che, dato i vostri vincoli. Puoi estrarre combinazioni distinte di quei campi. Ma se qualcuno avesse scritto Bob e Bobb con lo stesso indirizzo, avresti finito con due dischi. [GIGO] Hai ragione sul fatto che qualsiasi raggruppamento (a corto di raggruppamento su tutti i campi, equivalente a DISTINCT) mescolerà le righe. È un peccato che tu non abbia un identificativo univoco per ogni cliente.

Potrebbe essere possibile annidare le query insieme in modo da selezionare il primo 1 per ciascun nome e unire tutti quelli insieme.

2

selezionare Nome, via, città, stato FROM ( selezionare Nome, via, città, stato, ROW_NUMBER() OVER (partizione per nome Ordina per nome) rn da tavolo) AS t DOVE rn = 1

+0

SQL Server 2000, senza CTE – recursive

3

Utilizzare una tabella temporanea o una variabile di tabella e selezionare un elenco distinto di nomi in quello. Utilizzare quindi quella struttura per selezionare il primo 1 di ciascun record nella tabella originale per ciascun nome distinto.

+0

Le prestazioni per questo sarebbero molto scarse poichè dovresti usare un cursore per questo, a meno che tu non abbia il codice per questo. –

+0

Guardando la tabella delle fonti non penso che la prestazione sia il problema. – Gratzy

+0

Dovrei aver letto prima tutte le risposte perché è la stessa della mia soluzione. La tabella non è indicizzata e ci sono solo 3000 record, quindi non penso che un cursore sarà significativamente più lento di una soluzione rigorosamente SQL. Anche questo non mi sembra una query che verrà eseguita frequentemente. –

0
SELECT name, 
     (SELECT TOP 1 street, city, state 
      FROM addresses b 
      WHERE a.name = b.name) 
    FROM addresses a 
GROUP BY name 
0
SELECT name, street, address, state 
FROM 
(SELECT name, street, address, state, 
    DENSE_RANK() OVER (PARTITION BY name ORDER BY street DESC) AS r 
FROM tbl) AS t 
WHERE r = 1; 
+0

SQL 2000 non supporta DENSE_RANK – recursive

1

Una soluzione tabella temporanea potrebbe essere il seguente

CREATE Table #Addresses 
(
    MyId int IDENTITY(1,1), 
    [Name] NVARCHAR(50), 
    Street NVARCHAR(50), 
    City NVARCHAR(50), 
    State NVARCHAR(50) 
) 

INSERT INTO #Addresses ([Name], Street, City, State) SELECT [Name], Street, City, State FROM Addresses 

SELECT 
    Addresses1.[Name], 
    Addresses1.Street, 
    Addresses1.City, 
    Addresses1.State 
FROM 
    #Addresses Addresses1 
WHERE 
    Addresses1.MyId = 
(
    SELECT 
     MIN(MyId) 
    FROM 
     #Addresses Addresses2 
    WHERE 
     Addresses2.[Name] = Addresses1.[Name] 
) 

DROP TABLE #Addresses 
+0

Ciò non funziona perché i componenti di indirizzo non sempre aumentano o diminuiscono insieme. Ad esempio, 123 <234, ma Peoria> Fargo. – recursive

+0

Ok, l'ho provato con il set di dati e, in effetti, sembra che la mia logica fosse molto sbagliata. Ho lasciato solo la soluzione temporanea del tavolo, anche se ciò funziona bene per me. –

1

Questo è brutto come l'inferno, ma suona come la vostra situazione è brutta, troppo ... quindi ecco qui ...

select name, 
    (select top 1 street from [Addresses] a1 where a1.name = a0.name) as street, 
    (select top 1 city from [Addresses] a2 where a2.name = a0.name) as city, 
    (select top 1 state from [Addresses] a3 where a3.name = a0.name) as state 
from (select distinct name from [Addresses]) as a0 
+0

Stavo scrivendo questa soluzione nella mia testa mentre stavo leggendo le risposte precedenti. Non è una risposta lucida o carina, ma dovrebbe funzionare. Un'altra opzione potrebbe essere quella di modificare la subquery FROM in GROUP BY per migliorare leggermente le prestazioni rispetto a DISTINCT. –

+0

È garantito non mescolare gli indirizzi? – recursive

+0

esigenze e ORDINA DA Penso di essere al sicuro – Brimstedt

3

Se è possibile utilizzare una tabella temporanea:

select * -- Create and populate temp table 
into #Addresses 
from Addresses 

alter table #Addresses add PK int identity(1, 1) primary key 

select Name, Street, City, State 
-- Explicitly name columns here to not return the PK 
from #Addresses A 
where not exists 
    (select * 
    from #Addresses B 
    where B.Name = A.Name 
    and A.PK > B.PK) 

Questa soluzione non sarebbe consigliabile per tabelle molto più grandi.

+1

++ 1. Questa è una risposta eccellente. Breve e dolce. Non richiede di elencare tutti i nomi dei campi singolarmente e questo completa evita il problema del confronto dei campi con valori nulli. Una subquery correlata, nessun aggregato, nessun join. Per 3000 righe, questa è a) la meno codificante, b) buone prestazioni e c) risultati infallibili. Bellissimo! –

1

Penso che questo sia un buon candidato per una soluzione basata su cursore. E 'passato tanto tempo da quando ho usato un cursore che non tenterò di scrivere il T-SQL ma ecco l'idea:

  1. Creare tabella temporanea con lo stesso schema come indirizzi
  2. selezionare i nomi distinti in cursore
  3. loop tramite cursore selezionando superiore 1 da indirizzi nella tabella temporanea per ciascun nome distinto
  4. ritorno selezionare dalla tabella temporanea
0

E ancora un altro modo:

-- build a sample table 
DECLARE @T TABLE (Name VARCHAR(50),Street VARCHAR(50),City VARCHAR(50),State VARCHAR(50)) 
INSERT INTO @T 
SELECT 'Bob','123 Fake Street','Peoria','IL' UNION 
SELECT 'Bob','234 Other Street','Fargo','ND' UNION 
SELECT 'Jim','345 Main Street','St Louis','MO' UNION 
SELECT 'Fred','234 Other Street','Fargo','ND' 

-- here is all you do to get the unique record 
SELECT * FROM @T a WHERE (SELECT COUNT(*) FROM @T b WHERE a.Name = b.name and a.street <= b.street) = 1 
0
select c.*, b.* from companies c left outer join 
(SELECT *, 
    ROW_NUMBER() 
     OVER(PARTITION BY FKID ORDER BY PKId) AS Seq 
FROM Contacts) b on b.FKID = c.PKID and b.Seq = 1