Убедитесь, что значения во всех столбцах различны в sql - Oracle

У меня есть таблица с 14 столбцами в Oracle. Все столбцы имеют целочисленный тип. мне нужно проверить, что два столбца в одной строке не имеют в них одинакового целочисленного значения. Как я могу сделать это с помощью SQL. Или это можно сделать только с помощью PL/SQL?


person Arc    schedule 08.07.2010    source источник
comment
У меня уже есть набор данных. Мне нужно проверить данные для таких проблем.   -  person Arc    schedule 08.07.2010


Ответы (7)


Проверьте следующие примеры. Сначала я группирую значения в коллекцию. Я использовал встроенный SYS.DBMS_DEBUG_VC2COLL, но лучше, если вы создадите свой выиграл. Затем я беру эту коллекцию, а затем делаю MULTISET UNION DISTINCT с пустой коллекцией того же типа. Это удалит повторяющиеся записи.

select * from
 (select sys.dbms_debug_vc2coll(1,2,3) a, 
         sys.dbms_debug_vc2coll(1,2,3) multiset union distinct sys.dbms_debug_vc2coll() b
 from dual)
where a=b;

select * from
 (select sys.dbms_debug_vc2coll(1,2,3,1) a, 
         sys.dbms_debug_vc2coll(1,2,3,1) multiset union distinct sys.dbms_debug_vc2coll() b
 from dual)
where a=b

Наконец, я сравниваю эту коллекцию DISTINCT с оригинальной коллекцией. Если они совпадают, то в коллекции уже есть только уникальные значения.

person Gary Myers    schedule 08.07.2010
comment
Поддержка IIRC для MULTISET UNION была новой функцией в 10g, а в OP указан 9i. Но +1 за самое гладкое решение на сегодняшний день. - person APC; 09.07.2010

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

person Andy Lester    schedule 08.07.2010
comment
На самом деле это довольно большой набор данных. - person Arc; 08.07.2010
comment
хороший комментарий, я предположил, что в моем ответе требовались усилия по очистке данных. Правильная нормализованная структура всегда лучше. :) - person Randy; 08.07.2010
comment
Составной уникальный индекс будет улавливать только дубликаты одного и того же набора значений столбца. IE: 14 из 14 должны совпадать, 13 из 14 не вызовут уникальной ошибки ограничения. - person OMG Ponies; 08.07.2010
comment
Что-то подобное можно сделать... можно создать таблицу (RowNumber, ColumnNumber, Value), а затем создать уникальное ограничение (RowNumber, Value). - person Brian Hooper; 08.07.2010

если это ограничение данных - тогда я бы поместил его в триггер - и сравнил каждое значение в PLSQL. (я думаю, что это рекомендуется на основе вашего описания)

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

редактировать:

что-то типа:

select * from mytable where
( col1=col2 or col1=col3 or col1=col4 or col1=col5 ... )
or
( col2=col3 or col2=col4 or col2=col5 ... )
or
( col3=col4 or col3=col5 ... )
etc...
person Randy    schedule 08.07.2010
comment
Нельзя ли это сделать с помощью SQL-запроса, в котором я выбираю все строки, в которых есть столбцы с похожими данными. - person Arc; 08.07.2010
comment
Если я не ошибаюсь, это довольно большая проверка в триггере или довольно большое предложение WHERE в запросе... Я согласен с Энди в этом вопросе: факторизовать столбцы в другой таблице. - person Justin K; 08.07.2010
comment
Длинный запрос точно сработает. Нет ли меньшего и оптимизированного запроса, чтобы сделать то же самое. - person Arc; 08.07.2010
comment
Это не транзакционные данные. это данные, основанные на аналитике, из которых невозможно исключить столбцы. - person Arc; 08.07.2010
comment
Я склонен думать, что нельзя. Это решение выглядит ужасно, но все тесты должны быть сделаны, поэтому запрос должен их сделать. - person Brian Hooper; 08.07.2010

Вот еще один способ сделать это в SQL. Этот пример для трех столбцов. Вы просто добавляете больше предложений union all select ..., чтобы добавить больше столбцов в чек. Он вернет ROWID любой строки, для которой общее количество столбцов больше, чем количество различных значений.

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

select rowid,count(*),count(distinct col)
from (
select rowid,col1 col from the_table
  union all
select rowid,col2 col from the_table
  union all
select rowid,col3 col from the_table
)
group by rowid
having count(distinct col) < count(*)
person Dave Costa    schedule 08.07.2010
comment
+1 за то, что добрался туда почти на 30 минут быстрее, чем мне понадобилось, чтобы доказать метод - person Mark Baker; 08.07.2010

Очень грязный, неуклюжий метод.... но он должен работать с 9i

SELECT *
  FROM ( SELECT keyIdentifierField,
                COUNT(testField) AS fieldCount,
                COUNT(DISTINCT testField) AS distinctFieldCount
           FROM ( SELECT keyIdentifierField,
                         col1 AS testField
                    FROM myTable
                  UNION ALL
                  SELECT keyIdentifierField,
                         col2 AS testField
                    FROM myTable
                  UNION ALL
                  SELECT keyIdentifierField,
                         col3 AS testField
                    FROM myTable
                  ...
                  UNION ALL
                  SELECT keyIdentifierField,
                         col14 AS testField
                    FROM myTable
                )
          GROUP BY keyIdentifierField
       )
 WHERE fieldCount <> distinctFieldCount
person Mark Baker    schedule 08.07.2010
comment
Похоже, Дэйв Коста опубликовал очень похожее решение, пока я работал над этим.... но я не могу удалить свой ответ после того, как набрал все это. - person Mark Baker; 08.07.2010

Другой подход, который будет работать в 9i. Я не гарантирую производительность, только то, что это чисто SQL-подход, который работает на 9i, но выполнение 10 000 строк с 5 столбцами заняло доли секунды, так что это разумно.

create table test (
   uniqueKey number, 
   c1 number,
   c2 number,
   c3 number,
   c4 number,
   c5 number) 

Создайте несколько тестовых примеров — каждая 4-я строка плохая — мы используем псевдотаблицу целых чисел

    insert into test 
    select  r,1,2,3,4,CASE WHEN MOD(r,4)=0 THEN 4 ELSE 5 END
    FROM (SELECT rownum r from dual connect by rownum <= 10000);

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

SELECT uniqueKey,r from
(
    SELECT rownum r from dual connect by rownum <= 100 
/* 100 is the max value in any of our columns */
) numbers,
  test
WHERE r in (c1,c2,c3,c4,c5)

Это дает набор уникальных чисел для каждой строки.

Чтобы найти недопустимые строки, просто проверьте, меньше ли количество столбцов.

SELECT uniqueKey  from
(
    SELECT rownum r from dual connect by rownum <= 100 /* Our max potential value */
) numbers,
  test
WHERE r in (c1,c2,c3,c4,c5)
GROUP BY uniqueKey
HAVING COUNT(r) < 5

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

person JulesLt    schedule 09.07.2010

Если у вас есть только ОДИН возможный повторяющийся номер, возможны решения с использованием битовых операторов. Если вы используете Oracle 11, вы можете UNPIVOT данные столбца в строки,

Предполагая, что ваша таблица имеет уникальный ключ с именем UniqueKey

SELECT  UniqueKey,cvalue
FROM
(SELECT *
FROM yourTable
   UNPIVOT INCLUDE NULLS (cvalue FOR check_values IN (col1, col2, col3, col4))
)
GROUP BY UniqueKey,cvalue HAVING count(cvalue) > 1

Это может быть синтаксически немного неправильно, так как у меня нет под рукой тестовой базы данных Ora 11.

До Oracle 11 единственным способом сделать что-то подобное было использование типов объектов и pl/sql (функция, которая получает идентификатор, возвращает список значений столбца в этой строке в виде коллекции, а затем присоединяется к этой функции к идентификатору).

Вероятно, проще просто сделать это в pl/sql (создать массив, проиндексированный Integer, для каждого столбца проверить .exists(value) перед установкой - если он существует, у вас есть дубликат). Вызывайте функцию один раз для каждого столбца в строке и сбрасывайте массив между строками.

В качестве альтернативы - если это разовая проверка целостности - создайте временную таблицу для пар ключ/значение, уникальный ключ для пары, затем вставьте каждое значение в каждую строку против ключа и позвольте Oracle выбросить ваши ошибки.

person JulesLt    schedule 08.07.2010