Нулевое значение, возвращаемое в Count Distinct (pl sql) - PullRequest
1 голос
/ 18 июня 2009

Упреждающие извинения за бессмысленные имена таблиц / столбцов в этих запросах. Если вы когда-либо работали с базой данных Remedy для БД, вы поймете.

У меня проблема с тем, что Count Distinct возвращает нулевое значение, когда я подозреваю, что фактическое значение должно быть где-то в 20-х годах (мне кажется, 23). Ниже приведен ряд запросов и их возвращаемых значений.

SELECT count(distinct t442.c1)
      FROM t442, t658, t631
     WHERE t442.c1 = t658.c536870930
       AND t442.c200000003 = 'Network'
       AND t442.c536871139 < 2
       AND t631.c536870913 = t442.c1
       AND t658.c536870925 = 1
       AND (t442.c7 = 6 OR t442.c7 = 5)
       AND t442.c536870954 > 1141300800
       AND (t442.c240000010 = 0)

Результат = 497.

Добавьте таблицу t649 и убедитесь, что в ней есть записи, связанные с таблицей t442:

 SELECT COUNT (DISTINCT t442.c1)
              FROM t442, t658, t631, t649
             WHERE t442.c1 = t658.c536870930
               AND t442.c200000003 = 'Network'
               AND t442.c536871139 < 2
               AND t631.c536870913 = t442.c1
               AND t658.c536870925 = 1
               AND (t442.c7 = 6 OR t442.c7 = 5)
               AND t442.c536870954 > 1141300800
               AND (t442.c240000010 = 0)
               AND t442.c1 = t649.c536870914

Результат = 263.

Отфильтровать записи в таблице t649, где столбец c536870939 <= ​​1: </p>

SELECT COUNT (DISTINCT t442.c1)
          FROM t442, t658, t631, t649
         WHERE t442.c1 = t658.c536870930
           AND t442.c200000003 = 'Network'
           AND t442.c536871139 < 2
           AND t631.c536870913 = t442.c1
           AND t658.c536870925 = 1
           AND (t442.c7 = 6 OR t442.c7 = 5)
           AND t442.c536870954 > 1141300800
           AND (t442.c240000010 = 0)
           AND t442.c1 = t649.c536870914
           AND t649.c536870939 > 1

Результат = 24.

Фильтр в операторе HAVING:

SELECT COUNT (DISTINCT t442.c1)
          FROM t442, t658, t631, t649
         WHERE t442.c1 = t658.c536870930
           AND t442.c200000003 = 'Network'
           AND t442.c536871139 < 2
           AND t631.c536870913 = t442.c1
           AND t658.c536870925 = 1
           AND (t442.c7 = 6 OR t442.c7 = 5)
           AND t442.c536870954 > 1141300800
           AND (t442.c240000010 = 0)
           AND t442.c1 = t649.c536870914
           AND t649.c536870939 > 1
        HAVING COUNT (DISTINCT t631.c536870922) =
                                              COUNT (DISTINCT t649.c536870931)

Результат = ноль.

Если я запускаю следующий запрос, я не вижу ничего в списке результатов, который объяснил бы, почему я не получаю никакого возвращаемого значения. Это верно, даже если я удаляю DISTINCT из SELECT. (Я получаю 25 и 4265 строк данных обратно соответственно).

SELECT DISTINCT t442.c1, t631.c536870922, t649.c536870931
          FROM t442, t658, t631, t649
         WHERE t442.c1 = t658.c536870930
           AND t442.c200000003 = 'Network'
           AND t442.c536871139 < 2
           AND t631.c536870913 = t442.c1
           AND t658.c536870925 = 1
           AND (t442.c7 = 6 OR t442.c7 = 5)
           AND t442.c536870954 > 1141300800
           AND (t442.c240000010 = 0)
           AND t442.c1 = t649.c536870914
           AND t649.c536870939 > 1

У меня есть несколько других мест, где у меня есть запрос, настроенный точно так же, как и тот, который возвращает нулевое значение, и он прекрасно работает - возвращая используемые числа, которые являются правильными значениями. Я должен предположить, что все, что уникально в этой ситуации, связано с данными, а не с фактическим запросом, но я не уверен, что искать в данных, чтобы объяснить это. Мне не удалось найти нулевые значения в необработанных данных перед агрегацией. Я не знаю, что еще могло бы вызвать это.

Буду признателен за любую помощь.

Ответы [ 5 ]

2 голосов
/ 18 июня 2009

Теперь я понимаю. Ваша проблема в исходном запросе состоит в том, что весьма необычно (если на самом деле не неправильно) использовать предложение HAVING без предложения GROUP BY. Ответ заключается в порядке выполнения различных частей запроса.

В исходном запросе вы делаете это:

SELECT COUNT(DISTINCT t442.c1)
  FROM ...
 WHERE ...
HAVING COUNT(DISTINCT t631.c536870922) = COUNT(DISTINCT t649.c536870931);

База данных будет выполнять ваши объединения и ограничения, после чего она будет выполнять любые операции группировки и объединения. В этом случае вы не группируете, поэтому операции COUNT выполняются для всего набора данных. Основываясь на значениях, которые вы опубликовали выше, COUNT (DISTINCT t631.c536870922) = 25 и COUNT (DISTINCT t649.c536870931) = 24. Теперь применяется условие HAVING, в результате чего ничего не найдено - ваш запрос на случаи, когда подсчитывается общее количество set (даже если есть несколько c1s) равны, а они нет. DISTINCT применяется к пустому набору результатов, и вы ничего не получаете.

То, что вы действительно хотите сделать, это просто версия того, что вы опубликовали в примере, который подсчитывает количество строк:

SELECT count(*)
  FROM (SELECT t442.c1     
          FROM t442
             , t658
             , t631
             , t649
         WHERE t442.c1 = t658.c536870930
           AND t442.c200000003 = 'Network'
           AND t442.c536871139 < 2
           AND t631.c536870913 = t442.c1
           AND t658.c536870925 = 1
           AND (   t442.c7 = 6
                OR t442.c7 = 5)
           AND t442.c536870954 > 1141300800
           AND (t442.c240000010 = 0)
           AND t442.c1 = t649.c536870914
           AND t649.c536870939 > 1
         GROUP BY t442.c1
        HAVING COUNT(DISTINCT t631.c536870922) = COUNT(DISTINCT t649.c536870931)
       );

Это даст вам список столбцов c1, которые имеют равные номера записей таблицы 631 и 649. Примечание: вы должны быть очень осторожны с использованием DISTINCT в ваших запросах. Например, в случае, если вы разместили результаты выше, это совершенно не нужно; часто он действует как своего рода фоновый рисунок для покрытия ошибок в запросах, которые не возвращают результаты так, как вы хотите, из-за пропущенного ограничения в предложении WHERE («Хм, мой запрос возвращает дубликаты для всех этих значений. Что ж, DISTINCT исправит эту проблему ").

1 голос
/ 18 июня 2009

Что является результатом:

SELECT COUNT (DISTINCT t631.c536870922),
       COUNT (DISTINCT t649.c536870931)
          FROM t442, t658, t631, t649
         WHERE t442.c1 = t658.c536870930
           AND t442.c200000003 = 'Network'
           AND t442.c536871139 < 2
           AND t631.c536870913 = t442.c1
           AND t658.c536870925 = 1
           AND (t442.c7 = 6 OR t442.c7 = 5)
           AND t442.c536870954 > 1141300800
           AND (t442.c240000010 = 0)
           AND t442.c1 = t649.c536870914
           AND t649.c536870939 > 1

Если два столбца там никогда не имеют одинаковых значений, то имеет смысл, что добавление предложения HAVING исключит все строки из набора результатов.

0 голосов
/ 18 июня 2009

Если я сделаю это:

SELECT distinct t442.c1, count(distinct t631.c536870922), 
    count (distinct t649.c536870931)
          FROM t442, t658, t631, t649
         WHERE t442.c1 = t658.c536870930
           AND t442.c200000003 = 'Network'
           AND t442.c536871139 < 2
           AND t631.c536870913 = t442.c1
           AND t658.c536870925 = 1
           AND (t442.c7 = 6 OR t442.c7 = 5)
           AND t442.c536870954 > 1141300800
           AND (t442.c240000010 = 0)
           AND t442.c1 = t649.c536870914
           AND t649.c536870939 > 1
           group by t442.c1
           having count(distinct t631.c536870922)= 
                         count (distinct t649.c536870931)

Я вижу 23 строки, которые нужно посчитать. Удаление оператора HAVING возвращает 24 строки, лишнюю из которых не соответствует критерию HAVING.

EDIT: Результаты запроса согласно запросу Стива Броберга:

row | t442.c1         | cnt t631 | cnt 649
-------------------------------------------
1   | CHG000000230378 |    2     |    1
2   | CHG000000230846 |    1     |    1
3   | CHG000000232562 |    1     |    1
4   | CHG000000232955 |    1     |    1
5   | CHG000000232956 |    1     |    1
6   | CHG000000232958 |    1     |    1
7   | CHG000000233027 |    1     |    1
8   | CHG000000233933 |    1     |    1
9   | CHG000000233934 |    1     |    1
10  | CHG000000233997 |    1     |    1
11  | CHG000000233998 |    1     |    1
12  | CHG000000233999 |    1     |    1
13  | CHG000000234001 |    1     |    1
14  | CHG000000234005 |    1     |    1
15  | CHG000000234009 |    1     |    1
16  | CHG000000234012 |    1     |    1
17  | CHG000000234693 |    1     |    1
18  | CHG000000234696 |    1     |    1
19  | CHG000000234730 |    1     |    1
20  | CHG000000234839 |    1     |    1
21  | CHG000000235115 |    1     |    1
22  | CHG000000235224 |    1     |    1
23  | CHG000000235488 |    1     |    1
24  | CHG000000235847 |    1     |    1 

Первая строка будет отфильтрована должным образом, если я включу предложение HAVING.

0 голосов
/ 18 июня 2009

Вместо этого я бы попытался поместить условия условия HAVING в предложение WHERE. Есть ли причина, по которой вы выбрали ХАЙВИНГ? Просто FYI, HAVING - это фильтр, который выполняется после возврата набора результатов, что может привести к неожиданным результатам. Также он не используется при оптимизации запроса. Если вам не нужно использовать HAVING, я бы посоветовал вам не использовать его.

Я бы предложил добавить счетчики к предложению SELECT, а затем объединить их в предложении WHERE.

0 голосов
/ 18 июня 2009

COUNT(DISTINCT column) не считается NULL значения:

SELECT  COUNT(DISTINCT val1)
FROM    (
        SELECT  NULL AS val1
        FROM    dual
        )

---
0

Может быть, дело?

...