varlena
varlena
PostgreSQL Training,
Consulting & Support
General Bits
By A. Elein Mustain


5-Jan-2004 Edición: 56

Archives | General Tidbits | Google General Bits | Docs | Castellano | Português | Subscriptions | Notifications

General Bits General Bits es una publicación periódica escrita con información desde la lista de PostgreSQL, pgsql-general.
Para saber más sobre pgsql-general y PostgreSQL, visite www.PostgreSQL.org.

La hora de la Encuesta
Encuesta sobre PostgreSQL 05-Jan-2004

Es hora de una nueva encuesta. Esta encuesta trata de cómo usted usa PostgreSQL. Por favor corte y pegue las preguntas en su editor de texto, contéstelas y envíe las respuestas a generalbits@varlena.com. Las respuestas serán recopiladas y republicadas anónimamente.

1. ¿Qué interfaz (API) para cliente de PostgreSQL es la que más usa?

2. ¿En qué medida usted o su compañía usa PostgreSQL debido a sus características de objetos u objeto-relacionales? Por ejemplo, funciones o tipos de datos definidos por el usuario, herencia, tipos de datos predefinidos no tradicionales como los tipos geométricos, los métodos de indexación GiST, RTREE, tsearch2.

3. ¿Cuál es su característica favorita nueva en PostgreSQL 7.4?

4. ¿Debería haber un curso de entrenamiento de PostgreSQL mundialmente estándar?

5. Su compañía, ¿contrata los servicios de una compañía de soporte de PostgreSQL? Si es así, ¿cuál?

6. ¿Qué versión de PostgreSQL usa actualmente? Si no usa 7.4 aún, ¿cuáles son las razones técnicas o de negocios por las que aún no ha actualizado?

7. ¿Qué características harían a PostgreSQL más competitivo frente a Oracle o DB2?

8. ¿Contrataría usted o su compañía un servicio especializado en hosting PostgreSQL?

9. Su compañía, ¿está usando (PostgreSQL con) Open Office?

10. ¿Qué lenguaje procedural usa más con PostgreSQL?

11. ¿Qué tan importante es la certificación en PostgreSQL para usted o su compañía?

12. ¿Qué sistema de bases de datos usó antes de PostgreSQL? ¿Qué otros sistemas de bases de datos usa actualmente?

13. ¿Usa usted o su compañía MySQL debido a que fue diseñado para alto rendimiento?

14. ¿Qué característica de PostgreSQL aprovecha más?

  • confiabilidad
  • extensibilidad/diseño objeto-relacional
  • conformancia ACID
  • capacidad de manejar grandes volúmenes de datos
  • costo

15. ¿En qué plataforma corre PostgreSQL pricipalmente?

16. ¿Qué cree que atraería la mayor cantidad de usuarios nuevos de PostgreSQL?

17. ¿Cuál es su herramienta preferida de administración de PostgreSQL?

18. ¿Cuál es su herramienta preferida de visualización o diseño con PostgreSQL?

19. ¿Cuál es su estrategia de respaldos en producción?

20. ¿Cuál es su estrategia de manejo de fallas (fail over) en producción?

Contribuyó: elein at varlena.com
Fundamentos de Union, Intersect y Except
Mezcla, Intersección y Exclusión de Registros en Tablas 02-Jan-2004

Usar un UNION es como juntar dos (o más) tablas o selecciones. Debe haber la misma cantidad de columnas en cada selección y deben tener tipos compatibles.

Tabla 1                  
           
           
Tabla 2            
           

Estos tablas de ejemplo de empleados y contratistas tienen la misma estructura. En los datos, note que hay un contratista 'Dave' y un empleado 'Dave' que comenzaron el mismo día (en una situación real habría otros datos que permitirían diferenciar a los Daves, pero mantendremos este ejemplo para mostrar las variaciones de UNION).

   -- drop table employees; drop table contractors;
   CREATE TABLE employees (
      name text,
      startdate date,
      enddate date );

   CREATE TABLE contractors (
      name text,
      startdate date,
      enddate date );
   
   insert into employees values ('Maxine', '1/1/2002',NULL);
   insert into employees values ('Dave', '1/1/2002',NULL);
   insert into employees values ('Edmund', '3/14/2002',NULL);
   insert into employees values ('Maggie', '4/27/2002',NULL);
   insert into employees values ('Andrew', '10/27/2002',NULL);
   insert into contractors values ( 'Andrew', '1/1/2002', '10/25/2002');
   insert into contractors values ( 'Marcel', '2/1/2002', NULL);
   insert into contractors values ( 'Carrie', '3/14/2002', '12/15/2002');
   insert into contractors values ( 'Jacob', '4/27/2002', NULL);
   insert into contractors values ( 'Dave', '1/1/2002', NULL);

Un simple UNION de empleados y contratistas elimina uno de los Daves. Esto es así porque, por definición, una UNION de conjuntos elimina los duplicados.

   SELECT name, startdate, enddate FROM employees
   UNION 
   SELECT name, startdate, enddate FROM contractors
   
     name  | startdate  |  enddate   
   --------+------------+------------
    Andrew | 2002-01-01 | 2002-10-25
    Andrew | 2002-10-27 | 
    Carrie | 2002-03-14 | 2002-12-15
    Dave   | 2002-01-01 | 
    Edmund | 2002-03-14 | 
    Jacob  | 2002-04-27 | 
    Maggie | 2002-04-27 | 
    Marcel | 2002-02-01 | 
    Maxine | 2002-01-01 | 

Note que Andrew comenzó como contratista y luego se convirtió en empleado. No se presentó la anomalía del duplicado porque los dos registros no son exactamente duplicados: tienen diferentes fechas de inicio y término. Si la consulta involucrara sólo los nombres, un Andrew también se habría eliminado como un duplicado.

Para poder ver todos los registros en ambas tablas, use UNION ALL. Esto concatena las selecciones en lugar de hacer una unión formal.

   SELECT name, startdate, enddate FROM employees
   UNION ALL
   SELECT name, startdate, enddate FROM contractors;
   
     name  | startdate  |  enddate   
   --------+------------+------------
    Maxine | 2002-01-01 | 
    Dave   | 2002-01-01 | 
    Edmund | 2002-03-14 | 
    Maggie | 2002-04-27 | 
    Andrew | 2002-10-27 | 
    Andrew | 2002-01-01 | 2002-10-25
    Marcel | 2002-02-01 | 
    Carrie | 2002-03-14 | 2002-12-15
    Jacob  | 2002-04-27 | 
    Dave   | 2002-01-01 | 
   (10 rows)

Resulta que era un error tener dos Daves; Dave es un empleado y no un contratista. Entonces ahora queremos saber qué registros en la tabla de empleados tiene registros equivalentes en la tabla de contratistas, para poder verificar y corregir el error, y para ver si hay más como ese. Para hacer esto, usamos INTERSECT en lugar de JOIN.

   SELECT name, startdate, enddate FROM employees
   INTERSECT
   SELECT name, startdate, enddate FROM contractors;

    name | startdate  | enddate 
   ------+------------+---------
    Dave | 2002-01-01 | 
   (1 row)

Para eliminar este conjunto de datos podemos usar la cláusula INTERSECT anidada en la cláusula WHERE IN de un DELETE. ¿Por qué estamos usando la subconsulta anidada? SELECT name FROM (SELECT ... INTERSECT ...) Pruebe sin el nivel extra para entenderlo.

   DELETE FROM contractors 
   WHERE name IN ( 
      SELECT name FROM (
         SELECT name, startdate, enddate FROM employees
         INTERSECT
         SELECT name, startdate, enddate FROM contractors
      ) foo
   );

Volviendo a la tabla original (excepto por el Dave extra), suponga que queremos ver todos los empleados que en algún momento fueron contratistas. INTERSECT haría eso. ¿Y sobre los empleados que nunca fueron contratistas? Para esto puede usar EXCEPT. Recuerde que esperamos ver a todos los empleados, excepto Andrew.

   SELECT name FROM employees
   EXCEPT
   SELECT name FROM contractors;

     name  
   --------
    Dave
    Edmund
    Maggie
    Maxine
   (4 rows)

INTERSECT y EXCEPT también eliminan registros duplicados del resultado, si los hay. Si quiere conservar los duplicados, use el calificador ALL en INTERSECT y EXCEPT, tal como haría con UNION.

Contribuyó: elein@varlena.com
Fundamentos de Join
Cómo Unir Consultas y Tablas 02-Jan-2004

Un JOIN es una manera de seleccionar columnas de dos o más tablas en una misma lista de resultados, poniendo sus registros lado a lado en lugar de apilarlos como se hace con UNION. Usualmente, hay columnas traslapadas en cada tabla que son importantes, puesto que especifican el criterio de JOIN, es decir, la manera en que se construyen los registros. Necesita conocer cómo especificar los criterios de JOIN para poder usar JOIN apropiadamente.

Tabla 1   Tabla 2
              
              
              
              
              

El JOIN más simple es el más simple de escribir por error, y rara vez es útil.

   CREATE TABLE numbers ( one integer, two integer);
   CREATE TABLE letters ( a text, b text, c text);
   INSERT INTO numbers VALUES (1,2);
   INSERT INTO numbers VALUES (3,4);
   INSERT INTO letters VALUES ('a','b');
   INSERT INTO letters VALUES ('c','d');
   INSERT INTO letters VALUES ('e','f');
   
   SELECT numbers.one, numbers.two, letters.a, letters.b, letters.c
   FROM numbers, letters;

No especificando un JOIN en la clásula WHERE ni en la cláusula FROM, se crea un producto Cartesiano. Si la tabla numbers tiene 2 registros y la tabla letters tiene 3, entonces el resultado de esta consulta contiene 2 * 3 registros. Cada uno de los dos registros de numbers creará 3 registros en el resultado.

    one | two | a | b | c 
   -----+-----+---+---+---
      1 |   2 | a | b | 
      1 |   2 | c | d | 
      1 |   2 | e | f | 
      3 |   4 | a | b | 
      3 |   4 | c | d | 
      3 |   4 | e | f | 
   (6 rows)

Si realmente quiere usar este tipo de join, puede desear escribirlo usando la sintaxis explícita CROSS JOIN para documentar esta intención; de lo contrario, alguien puede arreglarlo por usted.

   SELECT numbers.one, numbers.two, letters.a, letters.b, letters.c
   FROM numbers CROSS JOIN letters;

Usualmente, los registros en una tabla se ligan a uno o más registros en la otra tabla designando una columna en cada una que sean iguales, o una condición que sea verdadera.

Creemos primero algunos datos de ejemplo para examinar algunos ejemplos simples de los otros tipos de JOINs. Tenemos una tabla de eventos de rugby (events), una tabla de equipos (teams) y una tabla de partidos (matches) jugados en cada evento. Note que estamos agregando un partido para el cual no existe un evento, y un equipo sin partidos. Generalmente para este esquema definiríamos llaves foráneas para asegurar que todos los partidos tuvieran equipos y eventos.

   -- DROP TABLE events; DROP TABLE teams; DROP TABLE matches;
   CREATE TABLE events ( e_id INTEGER, e_name TEXT, e_date DATE);
   CREATE TABLE teams ( t_id INTEGER, t_name TEXT, t_loc TEXT);
   CREATE TABLE matches (
      t1_id TEXT,       
      t2_id TEXT,
      t1_score INTEGER,
      t2_score INTEGER,
      e_id INTEGER);
   
   INSERT INTO teams VALUES (1, 'Twin Cities Amazons', 'Minnesota');
   INSERT INTO teams VALUES (2, 'Mudhens', 'Washington');
   INSERT INTO teams VALUES (3, 'Blues', 'California');
   INSERT INTO teams VALUES (4, 'Stingers', 'Maryland');
   INSERT INTO teams VALUES (5, 'Valkaries', 'Minnesota');
   INSERT INTO events VALUES (1, '2002 Division 1', '10/15/02');
   INSERT INTO events VALUES (2, '2003 Division 1', '10/20/03');
   INSERT INTO events VALUES (3, '2004 Division 1', '10/20/04');
   INSERT INTO matches VALUES (1,2, 12, 10, 1);
   INSERT INTO matches VALUES (3,4, 35, 16, 1);
   INSERT INTO matches VALUES (1,3, 11, 25, 1);
   INSERT INTO matches VALUES (1,3, 18, 30, 2);
   INSERT INTO matches VALUES (2,4, 1, 14, 2);
   INSERT INTO matches VALUES (3,4, 45, 2, 2);
   INSERT INTO matches VALUES (3,1, 10, 3, 4);

Hay varias clases de JOINs. Hemos visto el CROSS JOIN. Ahora veamos los otros: INNER y OUTER. Todos estos tipos de join son tanto conceptos como comandos SQL. Esto es, se pueden implementar los JOINs con cláusulas WHERE, subconsultas y UNIONs, o bien como sentencias JOIN explícitas.

Con la forma explícita de JOINs use las cláusulas NATURAL, ON o USING para indicar cómo deben juntarse los registros en ambas tablas.

Un INNER JOIN muestra todos los registros en la primera tabla que tienen registros correspondientes en la segunda tabla. Si hay un registro en la primera tabla sin un equivalente en la segunda, no se despliega. Lo mismo se cumple para la segunda tabla.

En OUTER JOIN, si una tabla tiene un registro sin un calce en la columna respectiva de la otra tabla, se ponen valores NULL en todas las columnas de la segunda tabla. Hay tres variantes: LEFT, RIGHT y FULL. En un LEFT OUTER JOIN, se muestran todos los registros de la tabla izquierda, y se ponen en NULL las columnas de la tabla derecha. En un RIGHT OUTER JOIN es lo mismo, excepto que se muestran los registros de la tabla derecha, y las columnas de la tabla izquierda pueden ser NULL. Un FULL OUTER JOIN es una combinación de ambos: se muestran todas las columnas de ambas tablas, y cuando no haya una entrada correspondiente en la otra tabla, se rellenan con NULL.
Inner JoinLeft Outer JoinRight Outer JoinFull Outer Join
Tabla 1   Tabla 2
              
              
              
              
              
Tabla 1   Tabla 2
              
              
              
              
              
Tabla 1   Tabla 2
              
              
              
              
              
Tabla 1   Tabla 2
              
              
              
              
              
LEYENDA: Tabla 1Columna de Join Tabla 2Columna NULL
Las siguientes sentencias muestran todas las maneras diferentes de hacer un INNER JOIN. Muestran cómo hacer un INNER JOIN usando la cláusula WHERE, y usando ON, USING y NATURAL. El JOIN enlaza el ID de evento de los partidos con el ID de evento de los eventos, con lo que se puede encontrar el nombre y la fecha de un partido. Note que el partido del evento 3 no se muestra. Un INNER JOIN sólo muestra aquellos registros para los que hay un calce en la columna de correspondencia.

Con la cláusula WHERE, simplemente especifique la igualdad de las columnas con respecto a las cuales quiere enlazar los registros. Con JOIN USING, especifique nombres nombres de las columnas comunes de ambas tablas. Un JOIN NATURAL es una abreviación de JOIN USING con todas las columnas que tengan los mismos nombres. Un JOIN ON permite especificar una expresión como criterio de correspondencia; esto permite establecer equivalencias entre columnas que tienen distintos nombres en las dos tablas, o usar lógica más compleja de correspondencia.

   select m.t1_id, m.t2_id, m.t1_score, m.t2_score, e.e_name, e.e_date
   from matches m, events e
   where m.e_id = e.e_id;
   
   select m.t1_id, m.t2_id, m.t1_score, m.t2_score, e.e_name, e.e_date
   from matches m JOIN events e USING (e_id) ; 
   
   select m.t1_id, m.t2_id, m.t1_score, m.t2_score, e.e_name, e.e_date
   from matches m NATURAL JOIN events e;
   
   select m.t1_id, m.t2_id, m.t1_score, m.t2_score, e.e_name, e.e_date
   from matches m JOIN events e ON (m.e_id = e.e_id) ; 
   
    t1_id | t2_id | t1_score | t2_score |     e_name      |   e_date   
   -------+-------+----------+----------+-----------------+------------
    1     | 2     |       12 |       10 | 2002 Division 1 | 2002-10-15
    3     | 4     |       35 |       16 | 2002 Division 1 | 2002-10-15
    1     | 3     |       11 |       25 | 2002 Division 1 | 2002-10-15
    1     | 3     |       18 |       30 | 2003 Division 1 | 2003-10-20
    2     | 4     |        1 |       14 | 2003 Division 1 | 2003-10-20
    3     | 4     |       45 |        2 | 2003 Division 1 | 2003-10-20
   (6 rows)
Puede unir más de dos tablas con cualquiera de los tipos de JOIN (y por supuesto, cada tabla podría ser una subconsulta, pero eso es otro artículo). Debido a que los nombres de columna son diferentes para los IDs de equipo en la tabla de partidos y en la tabla de equipos, usamos una condición JOIN ON. Esto podría hacerse con una condición WHERE, pero no podría hacerse con un JOIN USING ni con un JOIN NATURAL.

   select t1.t_name as team_one, t2.t_name as team_two,
   m.t1_score, m.t2_score, e.e_name
   from matches m JOIN events e USING (e_id) 
      JOIN teams t1 ON (m.t1_id = t1.t_id)
      JOIN teams t2 ON (m.t2_id = t2.t_id); 
   
         team_one       | team_two | t1_score | t2_score |     e_name      
   ---------------------+----------+----------+----------+-----------------
    Twin Cities Amazons | Mudhens  |       12 |       10 | 2002 Division 1
    Twin Cities Amazons | Blues    |       11 |       25 | 2002 Division 1
    Blues               | Stingers |       35 |       16 | 2002 Division 1
    Twin Cities Amazons | Blues    |       18 |       30 | 2003 Division 1
    Mudhens             | Stingers |        1 |       14 | 2003 Division 1
    Blues               | Stingers |       45 |        2 | 2003 Division 1
   (6 rows)

Un ejemplo de un LEFT OUTER JOIN sería mostrar todos los partidos para todos los eventos. Estamos usando la misma consulta que antes, pero con un LEFT JOIN entre partidos y eventos. Esto debería mostrarnos todos los partidos, incluyendo aquellos que no tienen un evento correspondiente. En este caso, veremos el partido entre las Blues y las Amazons con el ID de evento 4.

   select t1.t_name as team_one, t2.t_name as team_two,
      m.t1_score, m.t2_score, e.e_name
   from matches m LEFT OUTER JOIN events e USING (e_id)
      JOIN teams t1 ON (m.t1_id = t1.t_id)
      JOIN teams t2 ON (m.t2_id = t2.t_id);

         team_one       |      team_two       | t1_score | t2_score |     e_name      
   ---------------------+---------------------+----------+----------+-----------------
    Blues               | Twin Cities Amazons |       10 |        3 | 
    Twin Cities Amazons | Mudhens             |       12 |       10 | 2002 Division 1
    Twin Cities Amazons | Blues               |       11 |       25 | 2002 Division 1
    Twin Cities Amazons | Blues               |       18 |       30 | 2003 Division 1
    Mudhens             | Stingers            |        1 |       14 | 2003 Division 1
    Blues               | Stingers            |       35 |       16 | 2002 Division 1
    Blues               | Stingers            |       45 |        2 | 2003 Division 1
   (7 rows)

Un RIGHT OUTER JOIN selecciona todos los registros en la segunda tabla y pone las columnas de la primera tabla a NULL cuando no hay un calce. Los JOIN son conmutativos, por lo que la consulta de abajo debería dar los mismos resultados que la de arriba, cambiando LEFT por RIGHT y partidos por eventos. Pruébelo.

   select t1.t_name as team_one, t2.t_name as team_two,
      m.t1_score, m.t2_score, e.e_name
      from events e RIGHT OUTER JOIN matches m USING (e_id)
         JOIN teams t1 ON (m.t1_id = t1.t_id)
         JOIN teams t2 ON (m.t2_id = t2.t_id);

Un FULL OUTER JOIN es una combinación de un LEFT y un RIGHT OUTER JOIN. Un FULL OUTER JOIN podría usarse para mostrar todos los partidos y todos los eventos, sin importar si hay o no eventos o partidos correspondientes respectivamente. En esta consulta esperamos ver tanto el partido sin evento como arriba como el evento sin partidos (2004 Division 1). Sólo modificaremos la consulta original. La diferencia es que los dos JOIN en la tabla de equipos también tienen que ser cambiados a FULL OUTER JOIN para poder ver algun registro de evento sin partidos. Si no hacemos este cambio, entonces aunque la unión entre partidos y eventos es FULL JOIN, los dos JOIN con equipo serán INNER JOIN y eliminarán los registros que no calcen. Note además que por convertir todos los JOIN a FULL OUTER JOIN, el equipo sin partidos también se mostrará.

   select t1.t_name as team_one, t2.t_name as team_two,
      m.t1_score, m.t2_score, e.e_name
      from matches m FULL OUTER JOIN events e USING (e_id)
         FULL OUTER JOIN teams t1 ON (m.t1_id = t1.t_id)
         FULL OUTER JOIN teams t2 ON (m.t2_id = t2.t_id);
   
         team_one       |      team_two       | t1_score | t2_score |     e_name      
   ---------------------+---------------------+----------+----------+-----------------
    Blues               | Twin Cities Amazons |       10 |        3 | 
    Twin Cities Amazons | Mudhens             |       12 |       10 | 2002 Division 1
    Twin Cities Amazons | Blues               |       11 |       25 | 2002 Division 1
    Twin Cities Amazons | Blues               |       18 |       30 | 2003 Division 1
    Mudhens             | Stingers            |        1 |       14 | 2003 Division 1
    Blues               | Stingers            |       35 |       16 | 2002 Division 1
    Blues               | Stingers            |       45 |        2 | 2003 Division 1
                        | Valkaries           |          |          | 
    Stingers            |                     |          |          | 
    Valkaries           |                     |          |          | 
                        |                     |          |          | 2004 Division 1
   (11 rows)
Contribuyó: elein at varlena.com

Comments and Corrections are welcome. Suggestions and contributions of items are also welcome. Send them in!.
Copyright A. Elein Mustain 2003, 2004,2005

Google
Search General Bits & varlena.com Search WWW