2008-10-23 26 views
5

¿Cómo puedo crear eficientemente un índice único en dos campos en una tabla como esta: crear tabla t (un entero, entero b);Cómo crear un índice único 'a dos caras' en dos campos?

donde cualquier combinación única de dos números diferentes no puede aparecer más de una vez en la misma fila en la tabla.

En otras palabras, si existe una fila tal que a = 1 yb = 2, no puede existir otra fila donde a = 2 yb = 1 o a = 1 yb = 2. En otras palabras, dos números no pueden aparecer juntos más de una vez en cualquier orden.

No tengo idea de lo que se llama una restricción, de ahí el nombre del 'índice único de dos lados' en el título.

Actualización: Si tengo una clave compuesta en las columnas (a, b), y existe una fila (1,2) en la base de datos, es posible insertar otra fila (2,1) sin un error . Lo que estoy buscando es una manera de evitar que el mismo par de números que se utilice más de una vez en cualquier orden ...

Respuesta

5

¿Qué hay de controlar lo que entra en la tabla para que siempre almacene el número más pequeño en la primera columna y el más grande en el segundo? Siempre y cuando 'signifique' lo mismo, por supuesto. Probablemente sea menos costoso hacerlo incluso antes de que llegue a la base de datos.

Si esto es imposible, se puede guardar los campos tal cual, pero los han duplicado en orden numérico en dos otros campos, en el que se crearía la clave principal (pseudo código-ish):

COLUMN A : 2 
COLUMN B : 1 

COLUMN A_PK : 1 (if new.a < new.b then new.a else new.b) 
COLUMN B_PK : 2 (if new.b > new.a then new.b else new.a) 

Esto podría hacerse fácilmente con un desencadenante (como en la respuesta de Ronald) o manejarse más arriba, en la aplicación.

+0

Su primera respuesta es perfecta para mí. Gracias. Esperaba que hubiera una solución más "natural", , pero supongo que esto es todo. –

3

Creo que esto sólo se puede hacer usando un disparador FOR INSERT (en combinación con una restricción única en las dos columnas). No estoy muy fluida en la sintaxis de MySQL (Mi T-SQL es mejor), así que supongo que lo siguiente será contener algunos errores:

Editar: limpiado la sintaxis por lo que funciona para MySQL. Además, tenga en cuenta que probablemente quiera poner esto como un activador BEFORE UPDATE también (con un nombre diferente, por supuesto).

Además, este método se basa en tener una clave primaria o única en los dos campos (es decir, este disparador solo comprueba que el reverso todavía no existe). No parece haber ninguna forma de arrojar un error desde un disparador, así que me atrevo a decir que esto es tan bueno como se pone.

CREATE TRIGGER tr_CheckDuplicates_insert 
BEFORE INSERT ON t 
FOR EACH ROW 
BEGIN 
    DECLARE rowCount INT; 
    SELECT COUNT(*) INTO rowCount 
        FROM t 
        WHERE a = NEW.b AND b = NEW.a; 

    IF rowCount > 0 THEN 
     -- Oops, we need a temporary variable here. Oh well. 
     -- Switch the values so that the key will cause the insert to fail. 
     SET rowCount = NEW.a, NEW.a = NEW.b, NEW.b = rowCount; 
    END IF; 
END; 
+0

Reparada y probada en MySQL.Si alguien sabe si es posible lanzar un error directamente desde un disparador para detener el inserto, sería útil saber ... –

2

En Oracle se puede utilizar un índice basado en función de la siguiente manera:

create unique index mytab_idx on mytab (least(a,b), greatest(a,b)); 

No sé MySQL, pero tal vez es posible algo similar? Por ejemplo, puede agregar 2 nuevas columnas leastab y greatestab a la tabla, con un desencadenador para mantenerlas con los valores de mínimo (a, b) y máximo (a, b), respectivamente, y luego crear un índice único en (leastab , greatestab).

Cuestiones relacionadas