Найти пропущенные значения - PullRequest
1 голос
/ 31 декабря 2008

У меня есть таблица с двумя важными столбцами DocEntry, WebId

Пример данных как

DocEntry  WebId
1         S001
2         S002
3         S003
4         S005

Теперь, как мы можем заметить, в столбце WebId S004 отсутствует. Как мы можем найти такие пропущенные числа, с помощью запроса.

Дальнейшее объяснение:

Идентификатор сети должен быть в порядке возрастания, например, S001, S002, S003, S004, S005, если пропущено какое-либо число между ними, чем пропущенное число. У меня нет отдельной таблицы для возможных записей, так как это не практично. Я должен найти пропущенные числа по месяцам, взяв начальное и конечное значение каждого месяца в качестве границ, а затем найти пропущенные числа, если они есть.

Ответы [ 7 ]

5 голосов
/ 06 января 2009

Очень простой подход:)

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)

удачи, Maurice

1 голос
/ 31 декабря 2008

( В сторону: почему люди вообще (Рахул, не из-за разного воображения) опускают имя своего стола из вопроса? )

Это очень сложно сделать реляционным способом, потому что он по своей сути опирается на упорядочение данных, а реляционная алгебра работает с (неупорядоченными) множествами. Я полагаю, что мы должны предположить, что столбец DocID не имеет значения, и его нельзя использовать для решения проблемы.

В этом примере у вас есть S003 и S005, и вам не хватает S004. Как мы можем сказать, что отсутствует значение? Предположительно, потому что есть операция сравнения, которая говорит нам «меньше», «равно», «больше чем», а также потому, что есть функция разности, которая говорит нам, что разрыв между S003 и S005 равен 2. Давайте предположим, что « > 'и друзья проводят сравнение (работает здесь для символьных строк), и вы можете создать хранимую процедуру webid_diff (), которая принимает два значения WebID и возвращает разницу.

Затем вы можете написать запрос, такой как:

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;

При этом используется неравенство между таблицей и самой собой, чтобы найти минимальное значение WebID преемника для каждого элемента.

Имея это в качестве ядра, мы можем затем отфильтровать результат, чтобы выбрать только те строки, для которых разрыв между WebID и Min_Next больше одного. Итак, я думаю, что мы получаем ( 1-я попытка) :

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;

Действительно ли объединение на внешнем уровне приносит нам что-нибудь полезное? Я так не думаю, поэтому мы можем удалить его, что приведет к ( 2-я попытка) :

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;

Это работает . Попытка поместить функцию webid_diff () во внутренний запрос вызывает у меня проблемы - по крайней мере, выражение GAP должно быть включено в предложение GROUP BY, но тогда это даст неправильный ответ.

Предложение HAVING используется для применения условий фильтрации к агрегатам, поэтому выглядит так, будто запрос может быть приведен к:

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;

Однако, это не работает (для меня, с моей СУБД - IBM Informix Dynamic Server), потому что webid_diff () не является агрегатом.

Вот код, который я использовал для функции webid_diff () (которую нужно настроить в соответствии с синтаксисом вашей СУБД) и вспомогательной функции 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;
1 голос
/ 31 декабря 2008

Существует стандартная хитрость для генерации целых чисел, которая требует от вас создать таблицу из 10 строк, а именно:

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)

Как только вы это сделаете, например,

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

получит все цифры от 0 до 999.

Добавьте предложение where, чтобы ограничить вас между диапазонами, и некоторые строковые функции приведут вас к таблице возможной записи в ответе Робса выше.

0 голосов
/ 31 декабря 2008

Я предполагаю, что у вашей базы данных есть серьезный недостаток в дизайне, поскольку похоже, что ваш WebID - это как минимум два столбца, которые вы объединили вместе. Числовая часть, очевидно, имеет какое-то значение, поскольку вы хотите, чтобы она была последовательной, но если это так, то что означает «S»? В результате этого конструктивного недостатка решение вашей проблемы будет более сложным, чем необходимо. Кроме того, если вы заявляете, что хранение данных, важных для базы данных, «не практично», это большой красный флаг.

Если оставить в стороне, следующий запрос должен дать вам пропущенные значения:

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)

Это дает вам начало и конец для каждого диапазона пропущенных значений, а не отдельный список каждого из них. Для этого вам понадобится таблица чисел, которую покрыли Синди и Роб.

0 голосов
/ 31 декабря 2008

Лично я бы сделал это на PHP или на любом другом языке программирования, который вы используете с SQL. Если вы не можете иметь отдельную таблицу со всеми возможными значениями (почему бы и нет?), Тогда я бы выбрал простой запрос, чтобы получить значения, которые находятся в таблице: *

select WebID from table order by WebID;

, а затем используйте простой цикл, чтобы найти, какие из них отсутствуют. Например, в 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;
  }
}

Я не проверял это, но если вы действительно используете PHP, то это может сделать то, что вы хотите.

Ben

0 голосов
/ 31 декабря 2008

Если вы не определили определенную раскладку чисел (похоже, что вы), у вас есть таблица со всеми возможностями (хотя и не очень эффективная по времени), и вы можете сделать что-то вроде этого:

Получить одну таблицу со всеми возможностями имени возможного входа и затем сделать это:

SELECT pe.WebID from PossibleEntries pe 
WHERE pe.WebID Not In (Select WebID from SampleData)

Я думаю, что это должно сработать, но я не знаю, насколько это эффективно. Я согласен с вышеизложенным. Если числа не последовательные, вы не сможете это сделать.

0 голосов
/ 31 декабря 2008

Вам нужно определить, что вы подразумеваете под «отсутствующим». Вы не можете ожидать, что ваш сервер базы данных поймет это абстрактное понятие. Возможно, лучше всего использовать хранимую процедуру, поскольку тогда вы сможете более точно определить свою логику.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...