Valor duplicado en una Primary Key o en una Unique Key

1 comentario

Si una columna tiene la restricción Primary Key o la restricción Unique Key entonces no podrás tener valores duplicados en esa columna. Si lo intentas verás un mensaje similar al siguiente:

attempt to store duplicate value <visible to active transactions>in unique index RDB$PRIMARY1051

Este error ocurrirá en dos circunstancias:

  1. Cuando la Primary Key o la Unique Key ya están definidas y quieres insertar un valor que ya existe
  2. Cuando en una columna que tiene valores duplicados quieres definir una Primary Key o una Unique Key

 

.

 

Anuncios

Tratamiento de columnas NULL

8 comentarios

En el lenguaje SQL la palabra NULL significa “desconocida/o”.

Cuando establecemos que una columna de una tabla puede ser de tipo NULL lo que le estamos diciendo al Firebird es: “en esta columna podríamos tener algunos valores desconocidos”.

¿Establecer una columna como NULL podría causarnos problemas?

Pues sí, si es que nuestra aplicación no maneja correctamente a los NULL.

Entender como tratar a las columnas que pueden contener NULL es fundamental para que nuestra aplicación sea exitosa. En general, como una regla mientras no domines SQL:

  • No debes establecer NULL para las columnas numéricas, en lugar de eso ponerles como valor por defecto el número 0 (cero)
  • No debes establecer NULL para las columnas alfanuméricas, en lugar de eso ponerles como valor por defecto un espacio en blanco
  • No debes establecer NULL para las columnas de tipo fecha, en lugar de eso ponerles como valor por defecto una fecha muy antigua y que nunca usarás, como: ’01/01/1900′

¿Por qué es preferible ponerles valores por defecto a las columnas?

Porque el tratamiento de los valores de esas columnas se facilita. Por ejemplo, la función agrupada AVG() ignora a las columnas que tienen NULL para hallar el promedio. Y en general vas a querer que no las ignore. Además, en tu aplicación podrías olvidarte que una columna puede contener NULL y perderías tiempo buscando el problema, problema que nunca habrías tenido si la columna tuviera un valor por defecto.

Dependiendo de nuestro lenguaje de programación podemos hacer asignaciones NULL y también hacer comparaciones para saber si una columna es NULL o no lo es.

En el lenguaje Visual FoxPro escribiríamos:

lcNombre = NULL          && le asigna NULL a la variable lcNombre
IF ISNULL(lcNombre)      && si la variable lcNombre es NULL, entonces ...
IF !ISNULL(lcNombre)     && si la variable lcNombre no es NULL, entonces...

Tener columnas de tipo NULL es muy útil siempre y cuando sepas como usarlas. Si ese no es el caso, es mejor evitarlas y ponerles valores por defecto a todas las columnas, vivirás más tranquilo así.

Si tienes columnas NULL, en tu aplicación a cada momento podrías tener que verificar si una columna es NULL o no lo es. Y eso te hará perder tiempo y complicará tu código fuente.

Una forma de evitar ese problema es que en tu Base de Datos uses la función COALESCE(), como se muestra a continuación:

SELECT
   COALESCE(MiColumnaNumérica, 0) AS MiNumero
FROM
   MiTabla

La función COALESCE() devuelve el primer argumento que no sea NULL. O sea que en este ejemplo si MiColumnaNumérica no es NULL, ese es el valor que devolverá. Y si MiColumnaNumérica es NULL devolverá 0.

.

CAST()

4 comentarios

Descripción: Convierte una expresión de un tipo de datos a otro tipo de datos o dominio. Si la conversión no es posible envía un error.

Tipo de resultado: Elegido por el usuario

Sintaxis:

      CAST(Expresión AS <tipo_datos>)

      tipo_datos = Tipo_de_datos_SQL |
                   [TYPE OF] dominio |
                   TYPE OF COLUMN NombreTabla.NombreColumna

Sintaxis abreviada:

Hay una sintaxis alternativa que puede usarse solamente para convertir a DATE, TIME o TIMESTAMP

      Tipo_de_datos ‘date/timestring’

Ejemplos:

Usando la sintaxis completa:

   SELECT CAST('21' || '-April-' || '2013' AS DATE) FROM RDB$DATABASE

Usando la sintaxis abreviada:

UPDATE
   PRODUCTOS
SET
   FECHA_VENCIMIENTO = CURRENT_DATE + 45        -- la fecha de vencimiento será dentro de 45 días
WHERE
   FECHA_ULTIMA_COMPRA = DATE '21-JAN-2013'     -- para todos los productos que se compraron el 21 de enero de 2013

En algunos casos no es necesario siquiera usar la sintaxis abreviada porque el Firebird entenderá que debe convertir la cadena a fecha. El ejemplo anterior podría ser reescrito así:

UPDATE
   PRODUCTOS
SET
   FECHA_VENCIMIENTO = CURRENT_DATE + 45   -- la fecha de vencimiento será dentro de 45 días
WHERE
   FECHA_ULTIMA_COMPRA = '21-JAN-2013'     -- para todos los productos que se compraron el 21 de enero de 2013

en el cual eliminamos la palabra DATE.

Pero la forma abreviada no siempre es posible, el siguiente caso conducirá a un error porque el Firebird creerá que quieres restarle un entero a una cadena alfanumérica.

SELECT
   'TODAY' - 7
FROM
   RDB$DATABASE

La forma correcta de escribirlo es la siguiente, en la cual la palabra DATE le indica al Firebird que debe convertir la palabra ‘TODAY’ a la fecha de hoy:

SELECT
   DATE 'TODAY' - 7     -- DATE 'TODAY' devuelve la fecha de hoy. Este es un cast abreviado.
FROM
   RDB$DATABASE

Conversiones posibles:

La siguiente tabla muestra las conversiones que pueden realizarse con la función CAST()

-------------------------------------
|Desde              Hacia           |
-------------------------------------
|Tipos numéricos    Tipos numéricos |
|                   [VAR]CHAR       |
|                   BLOB            |
-------------------------------------
|[VAR]CHAR          [VAR]CHAR       |
|BLOB               BLOB            |
|                   Tipos numéricos |
|                   DATE            |
|                   TIME            |
|                   TIMESTAMP       |
-------------------------------------
|DATE               [VAR]CHAR       |
|TIME               BLOB            |
|                   TIMESTAMP       |
-------------------------------------

Recuerda que algunas veces la información puede perderse, por ejemplo cuando moldeas (o sea, haces el cast) de un TIMESTAMP a un DATE. En este caso se perderá la parte de la hora y te quedará solamente la parte de la fecha. Además, el hecho de que según la tabla de arriba pueda convertirse no es garantía de que tal conversión se realice con éxito. Por ejemplo: CAST(‘123456789’ AS SMALLINT) causará un error: “Arithmetic exception, numeric overflow, or string truncation.” ¿Por qué eso? porque el número más grande del tipo SMALLINT que puedes tener es 32767 y el número 123456789 es mayor que 32767.

También escribir algo como CAST(‘New Year’ AS DATE) causará un error.

Haciendo el cast a un dominio o su tipo:

Cuando se moldea (se hace el cast) a un dominio o a su tipo se deben satisfacer las restricciones (NOT NULL y/o CHECK) del dominio o el cast fallará. Un CHECK pasará si evalúa a TRUE o a NULL. Cuidado con esto último: un NULL pasará.

CREATE DOMAIN D_PRECIO_VENTA AS INTEGER CHECK (VALUE >= 1000)
SELECT CAST( 500 AS D_PRECIO_VENTA) FROM RDB$DATABASE     -- (1)
SELECT CAST(3000 AS D_PRECIO_VENTA) FROM RDB$DATABASE     -- (2)
SELECT CAST(NULL AS D_PRECIO_VENTA) FROM RDB$DATABASE     -- (3)

Solamente el primer cast (el número 1) resultará en un error, los otros dos casts pasarán. Cuando el modificador TYPE OF es usado la expresión es moldeada (cast) al tipo base del dominio, ignorando por lo tanto cualquier restricción que tenga ese dominio.

SELECT CAST(400 AS TYPE OF D_PRECIO_VENTA) FROM RDB$DATABASE
SELECT CAST(400 AS INTEGER)                FROM RDB$DATABASE

Los dos SELECT de arriba son equivalentes y ambos funcionarán bien. Recuerda que TYPE OF D_PRECIO_VENTA es lo mismo que escribir INTEGER ya que el tipo de D_PRECIO_VENTA es INTEGER.

Haciendo el cast a un tipo de columna:

También es posible hacer el cast al tipo de columna de una tabla o de una vista. En este caso, solamente el tipo de la columna es usado, ni las restricciones ni los valores por defecto se usan.

CREATE TABLE MiTabla (
    MiColumna INTEGER);

COMMIT;

SELECT CAST(12345 AS TYPE OF COLUMN MiTabla.MiColumna) FROM RDB$DATABASE;

.