2010-09-24 13 views
9

J'ai eu la question suivante sur une interview: Étant donné une table de nombres naturels avec quelques-uns manquant, fournir la sortie de deux tables, début de l'écart de nombre dans le premier tableau et se terminant en deuxième. Exemple:question d'entrevue SQL

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

Hmm ... Vous pourriez probablement faire facilement avec des fonctions analytiques comme ' lag' et 'lead' (peut-être quand j'ai le temps au déjeuner) ... mais ce serait spécifique à Oracle (ou à d'autres qui supportent ces fonctions). S'agit-il d'une solution générique pouvant fonctionner sur * n'importe quel * SGBDR, ou êtes-vous autorisé à assumer une implémentation spécifique? – FrustratedWithFormsDesigner

+0

Je suppose que ça devait être quelque chose de générique, parce que les autres questions, qui étaient sur la programmation, étaient indépendantes des langages. – Krns

+0

Je pense que vous devriez être capable de le faire avec deux instructions exist et une auto-comparaison pour remplir une table temporaire (une existe pour voir quand le prochain numéro n'est pas dans la table, une autre pour quand le nombre précédent ne l'est pas). – JNK

Répondre

5

Bien que ce soit à peu près la même chose que la réponse de Phil Sandler, cela devrait retourner deux tables distinctes (et je pense que cela semble plus propre) (cela fonctionne dans SQL Server, à moins):

 
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

Merci, je suppose que j'ai beaucoup à apprendre sur sql :) – Krns

1

Quelque chose comme ceci:

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 syntaxe "rownum" sera différent pour différents SGBD. Ce qui précède pourrait fonctionner pour SQL Server, mais je ne l'ai pas testé.

Comme l'un des commentaires l'a souligné, de nombreux SGBD ont des analyses qui faciliteront cette tâche.

+0

Pour le premier et le dernier, vous pouvez ajouter quelque chose pour les valeurs MAX et MIN de la colonne. – JNK

+0

@JNK: deux esprits avec une seule pensée :) – egrunin

+0

nice, je viens d'apprendre beaucoup sur SQL à partir de ce :) –

1

Ceci est la syntaxe 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, comment cela produit-il le résultat à deux colonnes ...? – egrunin

+0

Wow, je reçois downvotes alors qu'une requête qui produit le mauvais résultat obtient des upvotes. –

+0

Nous avons eu le même concept (mais avec une implémentation légèrement différente), donc vous obtenez un upvote de ma part. – stack

1

Itzik Ben Gan écrit beaucoup sur ces problèmes "des lacunes et des îles". Sa solution à ce row_number est

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 

Et une solution sans row_number de la même source.

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

Cela fonctionne sans DB SQL spécifique et il pourrait probablement être un peu plus propre mais il ne fonctionne

EDIT: Vous pouvez le voir travailler à ce Query à 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

Je ne connaissais pas Data Explorer !! – egrunin

+0

Entendu à ce sujet ici http://herdingcode.com/?p=263 –

0

Vous pouvez utiliser Lag fonction pour accéder à la ligne précédente:

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 

Résultat:

|3 |3 | 

|6 |7 | 

|10 |12 |