2010-09-24 31 views
9

Recibí la siguiente pregunta en una entrevista: Dada una tabla de números naturales con algunos que faltan, proporcione la salida de dos tablas, comenzando la brecha numérica en la primera tabla y terminando en la segunda. Ejemplo:Pregunta de entrevista SQL

 
____ ________ 
| | | | | 
| 1 | | 3 | 3 | 
| 2 | | 6 | 7 | 
| 4 | | 10| 12| 
| 5 | |___|___| 
| 8 | 
| 9 | 
| 13 | 
|____| 
+1

Hmm ... probablemente podría hacerlo fácilmente con funciones analíticas como ' lag' y 'lead' (tal vez cuando tengo tiempo en el almuerzo) ... pero eso sería específico de Oracle (u otros que admitan esas funciones). ¿Fue esta una solución genérica que puede ejecutarse en * cualquier * RDBMS, o se le permite asumir una implementación específica? – FrustratedWithFormsDesigner

+0

Supongo que tenía que ser algo genérico, porque otras preguntas, que eran sobre programación, eran independientes del lenguaje. – Krns

+0

Creo que debería poder hacer esto con dos sentencias existentes y una autocompansión para rellenar una tabla temporal (existe una para ver cuándo el siguiente número no está en la tabla, otro para cuando el número anterior no está). – JNK

Respuesta

5

Si bien esto es más o menos lo mismo que la respuesta de Phil Sandler, esto debería devolver dos tablas separadas (y creo que se ve más limpio) (funciona en SQL Server, en menos):

 
DECLARE @temp TABLE (num int) 
INSERT INTO @temp VALUES (1),(2),(4),(5),(8),(9),(13) 

DECLARE @min INT, @max INT 
SELECT @min = MIN(num), @max = MAX(num) FROM @temp 

SELECT t.num + 1 AS range_start 
    FROM @temp t 
    LEFT JOIN @temp t2 ON t.num + 1 = t2.num 
    WHERE t.num < @max AND t2.num IS NULL 

SELECT t.num - 1 AS range_end 
    FROM @temp t 
    LEFT JOIN @temp t2 ON t.num - 1 = t2.num 
    WHERE t.num > @min AND t2.num IS NULL 
+0

Gracias, supongo que tengo mucho que aprender sobre sql :) – Krns

1

algo como esto:

SELECT col1, col2 FROM 
(
    SELECT x + 1 as col1, 
     ROW_NUMBER() OVER(ORDER BY x) AS 'rownum' 
    FROM tbl y 
    WHERE NOT EXISTS (SELECT x FROM tbl z WHERE z.x = y.x + 1) 
     AND x <> (SELECT MAX(x) FROM tbl) 
) a 
INNER JOIN 
(
    SELECT x - 1 as col2, 
     ROW_NUMBER() OVER(ORDER BY x) AS 'rownum' 
    FROM tbl y 
    WHERE NOT EXISTS (SELECT x FROM tbl z WHERE z.x = y.x - 1) 
     AND x <> (SELECT MIN(x) FROM tbl) 
) b 
ON a.rownum = b.rownum 

La sintaxis "rownum" será diferente para diferentes DBMS. Lo anterior podría funcionar para SQL Server, pero no lo he probado.

Como se señala en uno de los comentarios, muchos DBMS tienen análisis que lo harán más fácil.

+0

Para el primero y el último, podría agregar algo para los valores MAX y MIN de la columna. – JNK

+0

@JNK: dos mentes con un solo pensamiento :) – egrunin

+0

bueno, en realidad aprendí mucho sobre SQL de esto :) –

1

Ésta es la sintaxis SQL Server:

CREATE TABLE #temp (columnA int) 

INSERT INTO #temp VALUES(1) 
INSERT INTO #temp VALUES(2) 
INSERT INTO #temp VALUES(4) 
INSERT INTO #temp VALUES(5) 
INSERT INTO #temp VALUES(8) 
INSERT INTO #temp VALUES(9) 
INSERT INTO #temp VALUES(13) 

SELECT 
    t1.columnA - 1 
FROM 
    #temp t1 
    LEFT JOIN #temp t2 ON t1.columnA = t2.ColumnA + 1 
WHERE 
    t2.ColumnA IS NULL 
    AND t1.ColumnA != (SELECT MIN(ColumnA) from #temp) 

SELECT 
    t1.columnA + 1 
FROM 
    #temp t1 
    LEFT JOIN #temp t2 ON t1.columnA = t2.ColumnA - 1 
WHERE 
    t2.ColumnA IS NULL 
    AND t1.ColumnA != (SELECT MAX(ColumnA) from #temp) 

DROP table #temp 
+0

Um, ¿cómo se produce esto el resultado de dos columnas ...? – egrunin

+0

Guau, recibo votos atrasados ​​mientras que una consulta que produce el resultado incorrecto es la obtención de votos ascendentes. –

+0

Se nos ocurrió el mismo concepto (pero una implementación ligeramente diferente), así que obtienes un voto positivo de mi parte. – stack

1

Itzik Ben Gan escribe mucho sobre estos problemas "las lagunas y las islas". Su solución a esto es row_number

WITH C AS 
(
SELECT N, ROW_NUMBER() OVER (ORDER BY N) AS RN 
FROM t 
) 
SELECT Cur.N+1,Nxt.N-1 
FROM C AS Cur 
JOIN C AS Nxt ON Nxt.RN = Cur.RN+1 
WHERE Nxt.N-Cur.N>1 

y una solución sin row_number de la misma fuente.

SELECT N+1 AS start_range, 
(SELECT MIN(B.N) FROM t AS B WHERE B.N > A.N)-1 AS end_range 
FROM t AS A 
WHERE NOT EXISTS(SELECT * FROM t AS B WHERE B.N = A.N+1) 
AND N< (SELECT MAX(N) FROM t) 
2

Esto funciona sin DB de SQL específico y es probable que se podría hacer un poco más limpio pero funciona

EDIT: Esto se puede ver trabajando en este Query en StackExchange Data Explorer

SELECT low,high FROM 

(

SELECT col1, low 

FROM 
(Select n1.col1 col1, min(n2.col1) + 1 low 
from numbers n1 
inner join numbers n2 
on n1.col1 < n2.col1 

Group by n1.col1) t 
WHERE t.low not in (SELECT col1 FROM NUMBERS) 
and t.low < (Select MAX(col1) from numbers) 
) t 

INNER JOIN 
(

SELECT col1 - 1 col1, high 
FROM 
(Select n1.col1 col1 , min(n2.col1) - 1 high 
from numbers n1 
inner join numbers n2 
on n1.col1 < n2.col1 

Group by n1.col1) t 
WHERE t.high not in (SELECT col1 FROM NUMBERS) 
) t2 
ON t.col1 = t2.col1 
+0

¡¡No sabía nada sobre Data Explorer !! – egrunin

+0

Se enteró aquí http://herdingcode.com/?p=263 –

0

Puede utilizar la función Lag acceder a la fila anterior:

create table #a (n int) 

insert #a values(1) 
insert #a values(2) 
insert #a values(4) 
insert #a values(5) 
insert #a values(8) 
insert #a values(9) 
insert #a values(13) 

select prev + 1, n - 1 from 
(select lag(n) over(order by n) as prev, n 
from #a) a 
where prev < n - 1 

Resultado:

|3 |3 | 

|6 |7 | 

|10 |12 | 
Cuestiones relacionadas