2009-06-11 13 views
8

Tengo una tabla en SQL Server 2000 que estoy tratando de consultar de una manera específica. La mejor manera de mostrar esto es con datos de ejemplo.Consulta SQL para devolver un solo registro para cada valor único en una columna

He aquí, [Addresses]:

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

Esto es en realidad un ejemplo simplificado de la estructura de la mesa real. La estructura de la mesa está completamente fuera de mi control. Necesito una consulta que me devuelva una sola dirección por nombre. No importa qué dirección, solo que hay una sola. El resultado podría ser la siguiente:

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

me encontré con una pregunta similar here, pero ninguna de las soluciones dadas a trabajar en mi caso, porque no tengo acceso a CROSS APPLY, y llamando MIN() en cada columna a mezclar diferentes direcciones juntas , y aunque no me importa qué registro se devuelve, debe ser una fila intacta, no una combinación de filas diferentes.

Las recomendaciones para cambiar la estructura de la tabla no me ayudarán. Estoy de acuerdo en que esta tabla es terrible, (es peor que la que se muestra aquí), pero esto es parte de una importante base de datos de ERP que no puedo cambiar.

Hay alrededor de 3000 registros en esta tabla. No hay clave principal.

¿Alguna idea?

+0

¿Puede dar una idea de cuántos registros hay en su tabla? Tengo algunas ideas para hacer esto, pero puede que no sea muy rápido si hay miles/millones de registros. –

+0

¿Tiene alguna clave principal en esta tabla? –

+0

~ 3000 registros, y no PK, sorprendentemente. Agregué esta información a la pregunta. – recursive

Respuesta

4

Bueno, esto le dará un rendimiento bastante malo, pero creo que va a trabajar

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

Desafortunadamente, no hay un campo de identificación único para esta tabla. Sí, lo sé. Esto apesta. – recursive

+0

Creo que esos son parte del nombre de la calle. No parece tener ninguna clave. –

+0

123, 234, etc. no son ID, son parte de la dirección de la calle. El problema dado no incluye un campo ID en la tabla, lo cual es una lástima porque de lo contrario esta solución es excelente. –

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

Eso no funcionará: puede tener la misma dirección en varias ciudades. – Tadmas

+0

En realidad, hay ejemplos de varias filas que contienen la misma dirección, por lo que en esos casos, aún obtendría duplicados. – recursive

+0

¿La misma dirección para el mismo nombre? si nombre + dirección es único, esto funcionará, creo que – tekBlues

0

Una ligera modificación en lo anterior debería funcionar.

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 

Ahora bien, esto no va a funcionar si tiene la misma calle, pero las otras piezas de información son diferentes (por ejemplo, con errores ortográficos).

O un hash más completo incluiría todos los campos (pero es probable que tenga demasiados para un rendimiento):

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

He comprobado esto. Desafortunadamente, hay algunos ejemplos de registros que tienen la misma calle. – recursive

+0

Tendría que ser el nombre y la calle para que haya un problema. Puede agregar más columnas para reducir la probabilidad de colisión, pero en general no es una gran solución al problema. Si es posible, obtener una clave primaria en los registros es mejor. –

+0

Cerrar, excepto que "tabla" x no tiene un campo llamado Calle. Necesitaría algo como "SELECT Name, MIN (Street) a" y "ON x.Name = t.Name AND x.a = t.Street" – mbeckish

0

No creo que se puede hacer eso, teniendo en cuenta sus limitaciones. Puede sacar distintas combinaciones de esos campos. Pero si alguien deletreara Bob y Bobb con la misma dirección terminarías con dos registros. [GIGO] Tiene razón en que cualquier agrupación (sin agrupar en todos los campos-equivalente a DISTINCT) mezclará filas. Es una lástima que no tenga un identificador único para cada cliente.

Es posible que pueda anidar consultas juntas de forma tal que se seleccione el top 1 para cada nombre y se unan todas juntas.

2

seleccione Nombre, calle, ciudad, estado DESDE ( seleccione Nombre, calle, ciudad, estado, ROW_NUMBER() OVER (partición por nombre Orden por su nombre) AS rn de la tabla) como T que RN = 1

+0

SQL Server 2000, no CTE – recursive

3

Utilice una tabla temporal o variable de tabla y seleccione una lista distinta de nombres en eso. Use esa estructura y luego seleccione la parte superior 1 de cada registro en la tabla original para cada nombre distinto.

+0

El rendimiento de esto sería muy pobre ya que tendría que usar un cursor para esto, a menos que tenga un código para ello. –

+0

Al mirar la tabla fuente, no creo que el rendimiento sea el problema. – Gratzy

+0

Debería haber leído todas las respuestas primero porque esta es la misma que mi solución. La tabla no está indexada y solo hay 3000 registros, así que no creo que un cursor sea significativamente más lento que una solución estrictamente SQL. Esto tampoco me parece una consulta que se ejecutará con frecuencia. –

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 no es compatible con DENSE_RANK – recursive

1

Una solución tabla temporal sería la siguiente

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

Esto no funciona porque los componentes de dirección no siempre aumentan o disminuyen al mismo tiempo. Por ejemplo, 123 <234, pero Peoria> Fargo. – recursive

+0

Ok, lo intenté con su conjunto de datos y, de hecho, parece que mi lógica fue muy incorrecta. Han dejado solo la solución de tabla temporal, que funciona bien para mí. –

1

Esto es feo como el infierno, pero suena como su situación es fea, también ... así que aquí va ...

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

Estaba escribiendo esta solución en mi cabeza mientras leía las respuestas anteriores. No es una respuesta resbaladiza o bonita, pero debería funcionar. Otra opción aquí sería cambiar la subconsulta FROM a GROUP BY para mejorar el rendimiento ligeramente sobre DISTINCT. –

+0

¿Está garantizado que no se mezclarán las direcciones? – recursive

+0

necesita y PEDIDO POR Creo que es seguro – Brimstedt

3

Si puede utilizar una tabla temporal:

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) 

Esta solución no sería recomendable para tablas mucho más grandes.

+1

++ 1. Esta es una excelente respuesta. Corto y dulce. No es necesario que enumere todos los nombres de campo individualmente, y completa evita el problema de la comparación de campos que admite nulos. Una subconsulta correlacionada, sin agregados, sin uniones. Para 3000 filas, esta es a) la menos codificación, b) buen rendimiento, yc) resultados infalibles. ¡Hermosa! –

1

Creo que este es un buen candidato para una solución basada en cursor. Ha pasado tanto tiempo desde que he usado un cursor que no voy a intentar escribir el T-SQL, pero aquí está la idea:

  1. Crear tabla temporal con el mismo esquema que las direcciones
  2. Seleccionar nombres distintos en el cursor
  3. bucle a través del cursor seleccionar la parte superior 1 de direcciones en la tabla de temperatura para cada nombre distinto
  4. Retorno seleccionar de tabla temporal
0

Y todavía otra forma:

-- 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 
Cuestiones relacionadas