2008-12-31 5 views
2

J'ai une table, avec 2 colonnes importantes DocEntry, webidTrouver les valeurs manquantes

données de l'échantillon est comme

DocEntry WebId 
1   S001 
2   S002 
3   S003 
4   S005 

Maintenant que nous pouvons remarquer ici, dans la colonne webid, S004 est manquante. Comment pouvons-nous localiser ces numéros manquants, avec une requête.

Des explications complémentaires:

L'id Web devrait être en ordre croissant comme, S001, S002, S003, S004, S005 si un nombre quelconque entre manque, que c'est le nombre manquant. Je n'ai pas de tableau séparé pour les entrées possibles, car ce n'est pas pratique. Je dois trouver les numéros manquants d'un mois à l'autre, en prenant la valeur de début et de fin de chaque mois comme limites et en trouvant les nombres manquants, le cas échéant.

Répondre

4

Une approche très simple :)

mysql> select * from test; 
+----------+-------+ 
| DocEntry | WebId | 
+----------+-------+ 
| 1  | S001 | 
| 2  | S002 | 
| 3  | S003 | 
| 4  | S005 | 
| 5  | S006 | 
| 6  | S007 | 
| 7  | S008 | 
| 8  | S010 | 
+----------+-------+ 
8 rows in set (0,00 sec) 

mysql> SELECT right(t1.webid,3) +1 as missing_WebId FROM test t1 left join test t2 on right(t1.webid,3)+1 = right(t2.webid,3) where t2.webid is null; 
+---------------+ 
| missing_WebId | 
+---------------+ 
| 4    | 
| 9    | 
| 11   | 
+---------------+ 
3 rows in set (0,01 sec) 

bonne chance, Maurice

0

Vous devez définir ce que vous voulez dire par "manquant". Vous ne pouvez pas attendre que votre serveur de base de données comprenne ce concept abstrait. Peut-être qu'une procédure stockée est la meilleure façon de procéder, car alors vous pouvez définir votre logique plus précisément.

+0

Votre commentaire était valide. La question a été clarifiée. Je suggère de supprimer cette réponse pour éviter les votes négatifs. –

1

Sauf si vous avez une disposition de nombre spécifique déjà défini (il semble que vous êtes), une table avec toutes les possibilités (pas de temps très efficace cependant) et vous pouvez faire quelque chose comme ceci:

Obtenir un table avec toutes les possiblités de PossibleEntries nom et faites ceci:

SELECT pe.WebID de PossibleEntries pe OÙ pe.WebID Not In (Sélectionner CodeWeb de SampleData)

Je pense qu'il faut travailler, mais je ne Je ne sais pas à quel point c'est efficace. Je suis d'accord avec ci-dessus. Si les numéros ne sont pas séquentiels, vous ne pourrez pas le faire.

1

Personnellement, je le ferais en PHP ou n'importe quel langage de programmation que vous utilisez avec SQL. Si vous n'êtes pas en mesure d'avoir une table séparée avec toutes les valeurs possibles (pourquoi pas, d'ailleurs?), L'approche que je prendrais serait de faire une simple requête pour obtenir les valeurs qui sont dans le tableau:

select WebID from table order by WebID; 

puis d'utiliser une simple boucle pour trouver ceux qui manquent. Par exemple, en php:

$values = Array(); 
$query = "select WebID from table order by WebID;"; 
$dataset = mysql_query ($query) or die (mysql_error()); 
while ($data = mysql_fetch_assoc($dataset)) 
{ 
    $values[$data['WebID'] = 1; 
} 

$last_line = $data['WebID']; 
$matches = Array(); 
ereg("S([0-9]+)", $last_line, $matches)) 

$max_value = $matches[0]; 
$missing = Array(); 

for ($count = 0; $count < $max_value; $count ++) 
{ 
    if (!isset($values[$count]) 
    { 
    echo "value $count is missing\n"; 
    $missing[$count] = true; 
    } 
} 

Je ne l'ai pas testé, mais si vous n'arrive d'utiliser PHP, alors que peut faire ce que vous voulez.

Ben

1

Il y a un truc standard pour générer des nombres entiers, vous devez créer une table utilitaire 10 rangée à savoir:

create table Pivot (i int) 

insert into Pivot values (0) 
insert into Pivot values (1) 
insert into Pivot values (2) 

/* ... down to */ 

insert into Pivot values (9) 

Une fois que vous avez fait cela, alors, par exemple

select u.i + 10*t.i + 100*h.i from Pivot u, Pivot t, Pivot h 

vous obtiendrez tous les numéros 0 à 999.

utilisez pour vous restreindre entre une plage et certaines fonctions de chaîne vous amèneront au tableau PossibleEntries dans Robs answer ci-dessus.

+0

C'est un travail modérément difficile. –

1

(A part: Pourquoi les gens en général (Rahul est pas le seul, loin de l'imagination) omettent le nom de leur table de la question?)

Il est très difficile à faire d'une manière relationnelle car elle repose intrinsèquement sur l'ordonnancement des données et l'algèbre relationnelle fonctionne sur des ensembles (non ordonnés). Je suppose que nous devons supposer qu'il n'y a pas de signification pour la colonne DocID et qu'elle ne peut pas être utilisée pour résoudre le problème.

Dans l'exemple, vous avez S003 et S005 et il manque S004. Comment dit-on qu'il y a une valeur manquante? Vraisemblablement, parce qu'il y a une opération de comparaison qui nous dit «inférieur à», «égal», «supérieur à», et aussi parce qu'il y a une fonction de différence qui nous dit que l'écart entre S003 et S005 est de 2. Supposons que ' > 'et les amis font la comparaison (fonctionne ici pour les chaînes de caractères), et que vous pouvez produire une procédure stockée webid_diff() qui prend deux valeurs WebID et renvoie la différence.

Ensuite, vous pouvez écrire une requête telle que:

SELECT a.webid, MIN(b.webid) AS min_next 
    FROM AnonymousTable AS a, AnonymousTable AS b 
    WHERE a.webid < b.webid 
    GROUP BY a.webid; 

Il utilise un non-équijointure entre la table elle-même et de trouver le minimum successeur valeur CodeWeb pour chaque élément. Avec cela comme le noyau, nous pouvons ensuite filtrer le résultat pour sélectionner uniquement les lignes pour lesquelles l'écart entre WebID et Min_Next est supérieur à un. Donc, je pense que nous obtenons (1ère tentative):

SELECT x.webid, y.min_next, webid_diff(x.webid, y.min_next) AS gap 
    FROM AnonymousTable AS x, 
     (SELECT a.webid, MIN(b.webid) AS min_next 
      FROM AnonymousTable AS a, AnonymousTable AS b 
      WHERE a.webid < b.webid 
      GROUP BY a.webid 
     ) AS y 
    WHERE x.webid = y.webid 
     AND webid_diff(x.webid, y.min_next) > 1; 

Est-ce la jonction au niveau extérieur nous avoir fait quelque chose d'utile? Je ne pense pas, afin que nous puissions l'enlever, ce qui conduit à (2ème tentative):

SELECT y.webid, y.min_next, webid_diff(y.webid, y.min_next) AS gap 
    FROM (SELECT a.webid, MIN(b.webid) AS min_next 
      FROM AnonymousTable AS a, AnonymousTable AS b 
      WHERE a.webid < b.webid 
      GROUP BY a.webid 
     ) AS y 
    WHERE webid_diff(y.webid, y.min_next) > 1; 

Cela ne fonctionne. Essayer de placer la fonction webid_diff() dans la requête interne me pose des problèmes - au moins l'expression de GAP devrait être incluse dans la clause GROUP BY, mais cela donnera une mauvaise réponse.

La clause HAVING est utilisé pour appliquer des conditions de filtre aux agrégats, il semble un peu comme si la requête pourrait être réductible à:

SELECT a.webid, MIN(b.webid) AS min_next, webid_diff(a.webid, b.webid) AS gap 
    FROM AnonymousTable AS a, AnonymousTable AS b 
    WHERE a.webid < b.webid 
    GROUP BY a.webid 
    HAVING webid_diff(a.webid, b.webid) > 1; 

Cependant, cela ne fonctionne pas (pour moi, avec mon SGBD - IBM Informix Dynamic Server) car webid_diff() n'est pas un agrégat.

Voici le code que j'utilisé pour la fonction webid_diff() (vous auriez à ajuster en fonction de la syntaxe de votre SGBD), et la fonction auxilliaire de webid_num():

CREATE FUNCTION webid_num(a CHAR(4)) RETURNING INTEGER; 
    DEFINE i INTEGER; 
    LET i = substr(a, 2, 3); 
    RETURN i; 
END FUNCTION; 

CREATE FUNCTION webid_diff(a CHAR(4), b CHAR(4)) RETURNING INTEGER; 
    DEFINE i, j INTEGER; 
    LET i = webid_num(a); 
    LET j = webid_num(b); 
    RETURN (j - i); 
END FUNCTION; 
0

Je pense que votre base de données a un défaut de conception sérieux, car il semble que votre WebID soit réellement composé d'au moins deux colonnes que vous avez combinées. La partie numérique a évidemment un sens, puisque vous voulez qu'elle soit séquentielle, mais si c'est le cas, que signifie le "S"? En raison de cette faille de conception, la solution à votre problème sera plus complexe qu'elle ne devrait l'être. Aussi, que vous déclarez que ce n'est pas "pratique" de stocker des données qui sont importantes pour la base de données est un grand drapeau rouge.

Paramètre qui part, la requête suivante devrait vous donner toutes les valeurs manquantes:

SELECT 
    (
      SELECT 
       SUBSTRING(MAX(T4.WebID), 1, 1) + 
       RIGHT('000' + CAST(CAST(SUBSTRING(MAX(T4.WebID), 2, 3) AS INT) + 1 AS VARCHAR), 3) 
      FROM My_Table T4 
      WHERE T4.WebID < T1.WebID 
    ) AS min_range, 
    SUBSTRING(T1.WebID, 1, 1) + RIGHT('000' + CAST(CAST(SUBSTRING(T1.WebID, 2, 3) AS INT) - 1 AS VARCHAR), 3) AS max_range 
FROM 
    My_Table T1 
LEFT OUTER JOIN My_Table T2 ON 
    T2.WebID = SUBSTRING(T1.WebID, 1, 1) + 
       RIGHT('000' + CAST(CAST(SUBSTRING(T1.WebID, 2, 3) AS INT) - 1 AS VARCHAR), 3) 

WHERE 
    T2.WebID IS NULL AND 
    T1.WebID <> (SELECT MIN(WebID) FROM My_Table) 

Il vous donne un début et de fin pour chaque gamme de valeurs manquantes, plutôt qu'une liste distincte de chacun. Pour obtenir cela, vous aurez besoin d'une table de chiffres que cindi et Rob ont couverts.

Questions connexes