Está en la página 1de 250

Machine Translated by Google

LA VOZ DEL EXPERTO ® EN SQL

SQL inicial
Consultas
De novato a profesional

Aplique las operaciones correctas al


problema correcto para generar los
resultados correctos, cada vez

Segunda edicion

Clare Churcher
Machine Translated by Google

Inicio de consultas SQL


De novato a profesional

Segunda edicion

Clare Churcher
Machine Translated by Google

Inicio de consultas SQL

Clare Churcher
gran cuello
Nueva York, Estados Unidos

ISBN-13 (pbk): 978-1-4842-1954-6 DOI ISBN-13 (electrónico): 978-1-4842-1955-3


10.1007/978-1-4842-1955-3

Número de control de la Biblioteca del Congreso: 2016944320

Copyright © 2016 por Clare Churcher

Esta obra está sujeta a derechos de autor. Todos los derechos están reservados por el Editor, ya sea total o parcialmente el material,
específicamente los derechos de traducción, reimpresión, reutilización de ilustraciones, recitación, radiodifusión, reproducción en microfilmes o de
cualquier otra forma física, y transmisión o almacenamiento de información. y recuperación, adaptación electrónica, software de computadora, o por
metodología similar o diferente ahora conocida o desarrollada en el futuro. Se exceptúan de esta reserva legal los extractos breves relacionados con
reseñas o análisis académicos o el material suministrado específicamente con el fin de ser ingresado y ejecutado en un sistema informático, para uso
exclusivo del adquirente de la obra. La duplicación de esta publicación o partes de la misma está permitida únicamente de conformidad con las
disposiciones de la Ley de derechos de autor de la ubicación del editor, en su versión actual, y siempre se debe obtener el permiso de uso de Springer.

Los permisos de uso se pueden obtener a través de RightsLink en el Centro de autorización de derechos de autor. Las violaciones están sujetas
a enjuiciamiento bajo la respectiva Ley de Derechos de Autor.

En este libro pueden aparecer nombres, logotipos e imágenes de marcas registradas. En lugar de utilizar un símbolo de marca comercial con
cada aparición de un nombre, logotipo o imagen de marca registrada, utilizamos los nombres, logotipos e imágenes solo de manera editorial y en
beneficio del propietario de la marca comercial, sin intención de infringir la marca comercial.

El uso en esta publicación de nombres comerciales, marcas registradas, marcas de servicio y términos similares, incluso si no están identificados
como tales, no debe tomarse como una expresión de opinión sobre si están o no sujetos a derechos de propiedad.

Si bien se cree que los consejos y la información de este libro son verdaderos y precisos en la fecha de publicación, ni los autores ni los editores ni el
editor pueden aceptar ninguna responsabilidad legal por los errores u omisiones que puedan cometerse. El editor no ofrece ninguna garantía, expresa
o implícita, con respecto al material contenido en este documento.

Director general: Welmoed Spahr


Editor principal: Jonathan Gennick
Revisor técnico: George Anderson
Consejo editorial: Steve Anglin, Pramila Balen, Louise Corrigan, Jonathan Gennick, Robert Hutchinson,
Celestin Suresh John, Nikhil Karkal, James Markham, Susan McDermott, Matthew Moodie,
Ben Renow-Clarke, Gwenan Spearing
Editora coordinadora: Jill Balzano
Editor de estilo: April Rondeau
Compositor: SPi Global
Indexador: SPi Global
Artista: SPi Global

Distribuido al comercio de libros en todo el mundo por Springer Science+Business Media New York, 233 Spring Street, 6th Floor, New
York, NY 10013. Teléfono 1-800-SPRINGER, fax (201) 348-4505, correo electrónico orders-ny@ springer-sbm.com , o visite www.springer.com .
Apress Media, LLC es una LLC de California y el único miembro (propietario) es Springer Science + Business Media Finance Inc (SSBM Finance
Inc). SSBM Finance Inc es una corporación de Delaware.

Para obtener información sobre las traducciones, envíe un correo electrónico a rights@apress.com o visite www.apress.com .

Los libros Apress y Friends of ED se pueden comprar al por mayor para uso académico, corporativo o promocional. Las versiones y licencias
de libros electrónicos también están disponibles para la mayoría de los títulos. Para obtener más información, consulte nuestra página web de licencias
de libros electrónicos de ventas al por mayor especiales en www.apress.com/bulk-sales .

Cualquier código fuente u otro material complementario mencionado por el autor en este texto está disponible para los lectores en
www.apress.com . Para obtener información detallada sobre cómo localizar el código fuente de su libro, vaya a www.apress.com/source-code/ .

Impreso en papel libre de ácido


Machine Translated by Google

Para Mark y Ali


Machine Translated by Google
Machine Translated by Google

Contenido de un vistazo

Sobre el Autor ............................................... .................................................... ....xv

Acerca del revisor técnico .............................................. ..........................................xvii

Agradecimientos .................................................. ..........................................................xix

Introducción ................................................. .................................................... ..........xxi

ÿCapítulo 1: Descripción general de la base de datos relacional .................................. .................... 1

ÿCapítulo 2: Consultas simples en una tabla ....................................... ..................... 15

ÿCapítulo 3: Una primera mirada a las uniones ............................... ............................... 33

ÿCapítulo 4: Subconsultas ............................................... ............................................. 51

ÿCapítulo 5: Autouniones ............................................... ............................................... 67

ÿCapítulo 6: Relaciones múltiples entre tablas ........................................... ... 85

ÿCapítulo 7: Operaciones de ajuste ........................................... .......................................... 99

ÿCapítulo 8: Operaciones agregadas ............................................... .......................... 129

ÿCapítulo 9: Funciones de la ventana ........................................ .......................... 147

ÿCapítulo 10: Consideraciones de eficiencia ........................................... ..................... 161

ÿCapítulo 11: Cómo abordar una consulta ....................................... .......................... 175

ÿCapítulo 12: Problemas comunes ............................................... ............................. 195

ÿApéndice 1: Base de datos de ejemplo ........................................... ............................. 211

ÿApéndice 2: Notación relacional ............................................... ............................. 213

Índice .................................................. .................................................... .................... 233

v
Machine Translated by Google
Machine Translated by Google

Contenido

Sobre el Autor ............................................... .................................................... ....xv

Acerca del revisor técnico .............................................. ..........................................xvii

Agradecimientos .................................................. ..........................................................xix

Introducción ................................................. .................................................... ..........xxi

ÿCapítulo 1: Descripción general de la base de datos relacional .................................. .................... 1

Introducción a las tablas de bases de datos ............................................. ............................................. 1


Atributos .................................................. .................................................... ............................................. 2

La clave principal .............................................................. .................................................... ..........................................

3 Inserción y actualización de filas en una tabla ...... .................................................... ....................................... 3

Diseño de tablas apropiadas ..... .................................................... .................................................... ........ 5

Introducción a los modelos de datos .............................................. .................................................... 6

Recuperar información de una base de datos ............................................... ............................. 9

Enfoque basado en procesos ................................................ .................................................... ............................... 10

Enfoque de resultados .............................................. .................................................... ............................. 11

Por qué consideramos dos enfoques ............................................... .................................................... ........ 12

Resumen ................................................. .................................................... ..................... 13

ÿCapítulo 2: Consultas simples en una tabla ....................................... ..................... 15

Subconjuntos de Filas y Columnas ............................................... .......................................... 15

Uso de alias ................................................. .................................................... .......... 18

Guardar consultas ................................................ .................................................... ............. 19

Especificación de condiciones para seleccionar filas ............................................... ......................... 19

Operadores de comparación ................................................ .................................................... ........................ 19

Operadores logicos ................................................ .................................................... ............................... 21

viii
Machine Translated by Google

ÿ CONTENIDOS

Tratar con nulos ............................................... .................................................... ......... 23

Encontrar Nulos .................................................. .................................................... .......................................... 24

Comparaciones que involucran valores nulos ............................................... .................................................... ....... 24

Gestión de duplicados .................................................. .................................................... .... 25

Pedido de salida .................................................. .................................................... ............ 28

Realización de conteos simples ............................................... ............................................. 29

Evitar errores comunes ............................................... ............................................. 29

Uso incorrecto de una cláusula WHERE para responder preguntas con la palabra "ambos" ................................ 30

Uso incorrecto de una cláusula WHERE para responder preguntas con la palabra “no” .................................. 32

Resumen ................................................. .................................................... ..................... 32

ÿCapítulo 3: Una primera mirada a las uniones ............................... ............................... 33

El enfoque de proceso para las uniones ............................................... .......................................... 33


Producto cartesiano ................................................ .................................................... ............................... 33

Unir internamente ................................................ .................................................... ............................................. 35

Enfoque de resultados para las uniones ............................................... ............................................. 36

Ampliación de consultas de unión.................................................... .................................................... .. 38

Un enfoque basado en procesos .............................................. .................................................... ............................. 39

Orden de operaciones ............................................... .................................................... ............................. 41

Un enfoque de resultados ............................................... .................................................... ......................... 42

Expresar uniones a través de interfaces esquemáticas ............................................... ............................... 43

Otros tipos de uniones .............................................. .................................................... ....... 44


Uniones exteriores .................................................. .................................................... .......................................... 45

Resumen ................................................. .................................................... ..................... 48

ÿCapítulo 4: Subconsultas ............................................... ............................................. 51

EN Palabra clave .................................................. .................................................... ................... 51

Uso de IN con subconsultas ............................................... ............................................. 52

Tener cuidado con NOT y <> ........................................... .......................................... 54

EXISTE Palabra clave ............................................. .................................................... .......... 57

Diferentes tipos de subconsultas ............................................... .......................................... 60

viii
Machine Translated by Google

ÿ CONTENIDOS

Consultas internas que devuelven un único valor ........................................... .................................................... .. 60

Consultas internas que devuelven un conjunto de valores .................................. .................................................... .. 62

Consultas internas Comprobación de la existencia ............................................... .................................................... .... 62

Uso de subconsultas para actualizar ............................................... .......................................... 63

Resumen ................................................. .................................................... ..................... 64

Ejemplos de diferentes tipos de subconsultas ............................................... .............................................. sesenta y cinco

Ejemplos de diferentes usos para subconsultas ............................................... ............................................. 66

ÿCapítulo 5: Autouniones ............................................... ............................................... 67

Relaciones con uno mismo ................................................ .................................................... ........ 67

Creación de una autounión .............................................. .................................................... ............................. 70

Consultas que implican una autounión ............................................... .................................................... .......... 71

Un enfoque de resultados para las autouniones .................................. .................................................... ....... 76

Preguntas que involucran "ambos" ............................................... .......................................... 79

Un enfoque de resultado para las preguntas que involucran "ambos" .................................. ............................... 80

Un enfoque de proceso para las preguntas que involucran "ambos" .................................. ..................................... 81

Resumen ................................................. .................................................... ..................... 82

Relaciones con uno mismo ................................................ .................................................... ............................... 82

Preguntas que involucran la palabra "ambos" ........................................... .................................................... ....... 82

ÿCapítulo 6: Relaciones múltiples entre tablas ........................................... ... 85

Dos relaciones entre las mismas tablas .................................................. .................... 85

Extraer información de múltiples relaciones ............................................... .......... 89

Enfoque basado en procesos ................................................ .................................................... ............................... 90

Enfoque de resultados .............................................. .................................................... ............................. 93

Reglas del negocio ................................................ .................................................... ............. 94

Resumen ................................................. .................................................... ..................... 97

ÿCapítulo 7: Operaciones de ajuste ........................................... .......................................... 99

Descripción general de las operaciones básicas de configuración .................................. ..................................... 99

Tablas compatibles con Union ............................................... ............................................. 101

Garantizar la compatibilidad de la unión ............................................... .................................................... ............. 104

ix
Machine Translated by Google

ÿ CONTENIDOS

Unión ................................................. .................................................... ......................... 105

Selección de las columnas apropiadas ............................................... .................................................... .... 106

Usos de la unión .................................................. .................................................... ..................................... 108

Uniones y uniones exteriores completas ............................................... .................................................... ................... 109

Intersección ................................................. .................................................... ............... 111


Usos de la intersección ............................................... .................................................... .......................... 112

La importancia de proyectar columnas apropiadas ............................................... ............................... 115

Administración sin la palabra clave INTERSECT ............................................... .......................................... 116

Diferencia ................................................. .................................................... .................... 118


Usos de la diferencia ............................................... .................................................... ............................. 118

Gestión sin la palabra clave EXCEPT ............................................... ............................................... 121

División .................................................. .................................................... ...................... 122

Proyección de columnas apropiadas ............................................... .................................................... ........ 124

SQL para División .............................................................. .................................................... ............................. 125

Resumen ................................................. .................................................... ................... 126


Unión ................................................. .................................................... ............................................... 127

Intersección ................................................. .................................................... ...................................... 127

Diferencia ................................................. .................................................... .......................................... 127

División .................................................. .................................................... ............................................. 128

ÿCapítulo 8: Operaciones agregadas ............................................... .......................... 129

Funciones agregadas simples ............................................... .......................................... 129


La función CONTAR() .............................................. .................................................... .......................... 129

La función AVG() .............................................. .................................................... ............................... 132

La función ROUND() .............................................. .................................................... ......................... 134

Otras funciones agregadas ............................................... .................................................... .......... 135

Agrupación .................................................. .................................................... ..................... 135


Filtrado del resultado de una consulta agregada ........................................... ............................................. 140

Uso de agregados para realizar operaciones de división ............................................... .................................... 142

Consultas anidadas y agregados ............................................... ..................................... 144

Resumen ................................................. .................................................... ................... 146

X
Machine Translated by Google

ÿ CONTENIDOS

ÿCapítulo 9: Funciones de la ventana ........................................ .......................... 147

Agregados simples .................................................. .................................................... ..... 147

Particiones .................................................. .................................................... ................... 149

Cláusula Ordenar por ............................................... .................................................... ........... 150 Agregados

Acumulativos .................................. .................................................... ................................... 150

Clasificación............... .................................................... .................................................... ............................

152 Combinación de pedidos con particiones .................. .................................................... ............................. 153

enmarcar .................................................. .................................................... ..................... 156

Resumen ................................................. .................................................... ................... 159

SOBRE() ........................... .................................................... .................................................... ...................

159 SOBRE(PARTICIÓN POR <…>) ....................... .................................................... ..........................................

159 SOBRE(ORDENAR POR <…>) . .................................................... .................................................... ...........

159 SOBRE(PARTICIÓN POR <…> ORDEN POR <…>) .................. .................................................... ................

159 SOBRE(FILAS ENTRE <…> Y <…>) ..................... .................................................... ................... 159

ÿCapítulo 10: Consideraciones de eficiencia ........................................... ..................... 161

Qué sucede con una consulta .............................................. .......................................... 161

Búsqueda de un registro .............................................. .................................................... ......... 163

Almacenamiento de registros en orden .................................. .................................................... ............................. 16

Índice agrupado .................................................. .................................................... ............................. 164

Índices no agrupados .............................................. .................................................... ....................... 165

Índice agrupado en una clave compuesta ............................................... .................................................... .... 166

Actualización de índices ........................................... .................................................... ................................... 167

Índices de cobertura ............ .................................................... .................................................... .......... 168

Selectividad de índices ............................... .................................................... .......................................... 168

Técnicas de unión .................................................. .................................................... .......... 168

Bucles anidados ................................................ .................................................... .................................... 169

Fusionar Unirse ................................................ .................................................... .......................................... 170

Diferentes expresiones SQL para uniones ............................................... .................................................... .... 171

xi
Machine Translated by Google

ÿ CONTENIDOS

Resumen ................................................. .................................................... ................... 173

Clave primaria ................................................ .................................................... ...................................... 173

Llaves extranjeras ................................................ .................................................... ..................................... 173

DONDE Condiciones .............................................. .................................................... ............................. 173

ORDENAR POR, AGRUPAR POR y DISTINTO ........................................... .................................................... ...... 173

Usar las herramientas .................................................. .................................................... ..................................... 173

ÿCapítulo 11: Cómo abordar una consulta ....................................... .......................... 175

Comprensión de los datos ............................................... .................................................. 175

Determinar las relaciones entre tablas ............................................... .......................................... 175

Mundo real versus implementación ............................................... .................................................... ..... 178

¿Qué tablas están involucradas? .................................................... .................................................... ............. 180

Mire algunos valores de datos ............................................. .................................................... ................... 180

Método de la imagen grande .................................................. .................................................... ...... 181


Combinar las tablas .................................................. .................................................... .......................... 181

Encuentre el subconjunto de filas ............................................. .................................................... ...................... 183

Conservar las columnas apropiadas ............................................... .................................................... ........ 183

Considere una vista intermedia ............................................... .................................................... .......... 184

Detectar palabras clave en las preguntas ............................................... .................................... 184

Y, Ambos, También .............................................. .................................................... ..................................... 185

No nunca ............................................... .................................................... .......................................... 187

Todo, Cada .................................................. .................................................... ............................................ 188

¿No tienes idea de por dónde empezar? .................................................... ............................................. 189

Encuentre algunas tablas útiles ............................................... .................................................... ................... 189

Trate de responder la pregunta a mano ........................................... .................................................... ...... 189

Escriba una descripción del resultado obtenido ........................................... .................................. 190

¿Hay alternativas? .................................................... .................................................... .................... 190

Comprobación de consultas .................................................. .................................................... ....... 191


Marcar una fila que debe ser devuelta ........................................... .................................................... .192

Marcar una fila que no debe devolverse ........................................... ............................................. 192

xi
Machine Translated by Google

ÿ CONTENIDOS

Comprobar las condiciones de contorno ............................................... .................................................... ............... 192

Comprobar valores nulos .............................................. .................................................... ............................. 193

Resumen ................................................. .................................................... ................... 193

ÿCapítulo 12: Problemas comunes ............................................... ............................. 195

Mal diseño de la base de datos .............................................. .................................................... .195


Datos que no están normalizados ............................................... .................................................... .......... 195

Tablas sin clave primaria ............................................... .................................................... .......... 198

Tablas con claves foráneas faltantes ............................................... .................................................... ....... 199

Datos similares en dos tablas .............................................. .................................................... ................... 199

Tipos inapropiados .................................................. .................................................... .......................... 200

Problemas con los valores de datos .............................................. ............................................. 201

Nulos inesperados ............................................ .................................................... ............................. 201

Ortografía incorrecta o inconsistente ............................................... .................................................... ....... 202

Caracteres extraños en campos de texto ............................................... .................................................... ... 202

Caso inconsistente en campos de texto ............................................... .................................................... .......... 203

Diagnóstico de problemas .............................................. .................................................... .203


Comprobar partes de consultas anidadas de forma independiente .................................. .......................................... 204

Comprender cómo se combinan las tablas ............................................... .................................... 204

Quitar cláusulas WHERE adicionales.................................................. .................................................... ............ 204

Conservar todas las columnas ............................................... .................................................... .......................... 204

Comprobar consultas subyacentes en agregados ............................................... ............................................. 205

Síntomas comunes ................................................ .................................................... ... 205


No se devuelven filas ............................................... .................................................... .......................... 205

Faltan filas ............................................................. .................................................... ............................. 205

Más filas de las que debería haber ............................................... .................................................... ........ 207

Estadísticas o agregados incorrectos ............................................... .................................................... ...... 208

El orden es incorrecto ............................................... .................................................... ............................. 209

Errores tipográficos comunes y problemas de sintaxis .................................. ............................. 209

Resumen ................................................. .................................................... ................... 210

XIII
Machine Translated by Google

ÿ CONTENIDOS

ÿApéndice 1: Base de datos de ejemplo ........................................... ............................. 211

ÿApéndice 2: Notación relacional ............................................... ............................. 213


Introducción ................................................. .................................................... ............... 213

Relaciones, tuplas y atributos .................................................. .................................................... .......... 214

SQL, álgebra y cálculo ............................................... .................................................... ................... 216

Álgebra relacional: Especificación de las operaciones ............................................... .......... 216


Seleccione ................................................. .................................................... ....................................................... 217

Proyecto ................................................. .................................................... ............................................. 218

Combinación de Seleccionar y Proyectar .................................................... .................................................... ........ 218

Producto cartesiano ................................................ .................................................... ............................. 219

Unir internamente ................................................ .................................................... .......................................... 220

Unión, diferencia e intersección .................................................. .................................................... ....... 221

División .................................................. .................................................... ............................................. 223

Cálculo relacional: especificación del resultado ............................................... ................... 225


Expresiones de cálculo simple ............................................... .................................................... ............. 225

Variables libres y ligadas ............................................... .................................................... ................... 225

Cuantificador existencial y SQL ............................................... .................................................... ............. 226

Cuantificador Universal y SQL ............................................... .................................................... ............... 228

Un ejemplo ................................................ .................................................... .......... 229


álgebra .................................................. .................................................... ............................................. 229

Cálculo .................................................. .................................................... .......................................... 230

Conclusión ................................................. .................................................... .......................................... 231

Índice .................................................. .................................................... .................... 233

xiv
Machine Translated by Google

Sobre el Autor

Clare Churcher fue académica sénior en la Universidad de Lincoln,


Christchurch, Nueva Zelanda, durante veinte años y ganó un premio de enseñanza
por su contribución al desarrollo e impartición de varios cursos de pregrado y
posgrado, incluido el análisis y diseño de bases de datos.
Después de su tiempo en Lincoln, pasó dos años como analista de negocios en
Orion Health Software. Actualmente está desarrollando cursos de software de nivel
de posgrado para el Politécnico Tai Poutini, Christchurch, Nueva Zelanda.

XV
Machine Translated by Google
Machine Translated by Google

Acerca del revisor técnico

George Anderson Jr. es un administrador de base de datos activo con casi


una década de experiencia en SQL. Acredita su exposición a SQL, tanto en el
trabajo como a través de la comunidad de SQL, por brindarle muchas
oportunidades excelentes para aprender, crecer y establecer contactos. Cuando
no está protegiendo datos y escribiendo código, a George le gusta leer, jugar muy
mal al golf y pasar tiempo con su familia.

xvii
Machine Translated by Google
Machine Translated by Google

Expresiones de gratitud

En primer lugar, muchas, muchas gracias a mi esposo, Neville, por leer cada capítulo y brindarme tantas
sugerencias valiosas. Quisiera agradecer a uno de mis lectores, Scott Lawley, quien me brindó
comentarios útiles y sugirió que los términos enfoque de proceso y enfoque de resultado son más
amigables que álgebra y cálculo . Gracias a mi editor, Jonathan Gennick, por hacer posible esta segunda
edición, ya Jill Balzano por su excelente coordinación. Gracias también a mi empleador, el Politécnico Tai
Poutini, por su apoyo.

xix
Machine Translated by Google
Machine Translated by Google

Introducción

Visión general

La sintaxis de SQL es bastante fácil de aprender. Algunas ideas básicas y un puñado de palabras clave le permiten abordar una gran
variedad de consultas. Sin embargo, muchos usuarios a menudo se encuentran completamente perplejos cuando se enfrentan a un
problema en particular. Realmente no es de mucha ayuda que alguien diga “así es como yo lo haría”. Lo que necesita es una variedad de
formas de comenzar con un problema complicado. Una vez que haya comenzado con una consulta, debe poder verificar, modificar y refinar
su solución hasta que tenga lo que necesita.

Enfoque de dos frentes


A lo largo del libro he abordado diferentes tipos de consultas desde dos direcciones. Los dos enfoques tienen sus raíces en el cálculo y el
álgebra relacional formal. En el cuerpo del libro he mantenido las descripciones no matemáticas, sin embargo, el Apéndice 2 ofrece una
introducción a la notación formal para aquellos interesados en comprender la teoría subyacente. El primer enfoque, al que he llamado
enfoque de proceso , analiza cómo se deben manipular las tablas para recuperar el subconjunto de datos requerido. Encontrará
explicaciones de los diferentes tipos de operaciones que se pueden realizar en las tablas; por ejemplo, uniones, intersecciones, selecciones.
Se proporcionan explicaciones para ayudarlo a decidir cuál de estos podría ser útil en situaciones particulares. Una vez que comprenda
qué operaciones se necesitan, traducirlas a SQL es relativamente sencillo.

El segundo enfoque es el que uso cuando simplemente no puedo descifrar qué operaciones me darán los resultados requeridos. Este
enfoque, al que he llamado enfoque de resultado , le permite describir cómo podría condiciones
ser una fila esperada
debe obedecer.
en su resultado,
Al observar
eslos
decir,
datos,
quées
sorprendentemente fácil desarrollar una descripción semiformal de cómo sería una fila recuperada "correcta" (y, por implicación, cómo
reconocería una fila "incorrecta"). Traducir esta descripción semiformal en una consulta de trabajo es sencillo.

Siempre me sorprende el enfoque que adoptan mis alumnos cuando se enfrentan a un problema nuevo. Algunos verán
instantáneamente las operaciones que se necesitan y otros encontrarán que el enfoque de resultados es más intuitivo. La elección del
enfoque cambia de una consulta a otra, de una persona a otra y (sospecho) de un día a otro.
Tener más de una forma de comenzar significa que es menos probable que se sienta completamente desconcertado por un nuevo problema.

xxx
Machine Translated by Google

ÿ INTRODUCCIÓN

¿Para quién es este libro?


Este libro es para cualquiera que tenga una base de datos relacional bien diseñada y necesite extraer información de
ella. Es posible que haya notado en la oración anterior que la base de datos debe estar "bien diseñada". No puedo enfatizar
demasiado este punto. Si su base de datos está mal diseñada, entonces no podrá almacenar datos precisos y consistentes,
por lo que la información que recuperen sus consultas siempre será propensa a imprecisiones. Si está buscando diseñar una
base de datos desde cero, debería leer mi primer libro Beginner Database Design ”. 1 El capítulo final de este libro describirá
algunos problemas de diseño comunes con los que probablemente se encontrará y brindará algunos consejos sobre cómo
mitigar el impacto o corregir el problema.

Objetivo de este libro


En este libro se le presentarán las principales técnicas y palabras clave necesarias para crear consultas SQL.
Aprenderá sobre uniones, intersecciones, uniones, diferencias, selección de filas y proyección de columnas.
Verá cómo implementar estas ideas de diferentes maneras utilizando consultas simples y anidadas, y se le presentará una
variedad de técnicas para agregar y resumir datos, incluido el uso de funciones de ventana. También aprenderá cómo puede
investigar y mejorar la eficiencia de sus consultas.
Lo más importante de todo es que aprenderá diferentes formas de comenzar con un problema problemático. Casi
siempre hay varias formas diferentes de expresar una consulta, y mi objetivo es que para cualquier situación en particular
te proporcione un método de ataque que coincida con tu psique y estado de ánimo (es broma).

Nuevo en la Segunda Edición


He agregado un capítulo sobre funciones de ventana que describe la funcionalidad que estos conceptos introducidos
recientemente brindan para agregar y resumir datos.
También se incluye un apéndice que proporciona una introducción de fácil comprensión a los conceptos y la notación
relacionales formales.

1 Clare Churcher, Diseño de bases de datos para principiantes: de novato a profesional (Nueva York: Apress, 2012).

XXII
Machine Translated by Google

CAPÍTULO 1

Descripción general de la base de datos relacional

SQL (lenguaje de consulta estructurado) nos permite crear tablas, aplicar restricciones y manipular datos en una base de datos. En
este libro nos concentraremos en las consultas que nos permiten extraer información de una base de datos describiendo el
subconjunto de datos que necesitamos. Esos datos pueden ser un solo número de los nombres de, como
los miembros
el precio con
de unsuscripciones
producto, una lista
vencidas o un cálculo, como el monto total en dólares de los productos vendidos en los últimos 12 meses. En este libro, veremos
diferentes formas de abordar una consulta para que pueda expresarse correctamente en SQL.

Antes de entrar en detalles sobre cómo especificar consultas, revisaremos algunas de las ideas y la terminología asociada con
las bases de datos relacionales. También veremos los modelos de datos, que son una forma sucinta de describir cómo se arma una
base de datos en particular, es decir, qué datos se guardan, dónde y cómo se interrelaciona todo.

Es imperativo que la base de datos subyacente haya sido diseñada para representar con precisión la situación.
se trata. Esto significa que no solo se han creado las tablas adecuadas, sino también que se han aplicado las restricciones
apropiadas para que los datos sean coherentes y se mantengan coherentes a medida que evoluciona la base de datos.
Incluso con todos los SQL más sofisticados del mundo, es poco probable que obtenga respuestas precisas a las consultas si el
diseño de la base de datos subyacente es defectuoso. Si está configurando una nueva base de datos, debe consultar un libro de diseño 1
antes de embarcarse en el proyecto.

Introducción a las tablas de base de datos


En términos simples, una base de datos relacional es un conjunto de tablas .2 Cada tabla en una base de datos bien diseñada guarda
información sobre aspectos de una cosa, como clientes, ventas, equipos o torneos. A lo largo del libro, basaremos la mayoría de los
ejemplos en una base de datos para un palo de golf. Las tablas se presentarán a medida que avancemos, y se proporciona una
descripción general en el Apéndice 1.

Material complementario electrónico La versión en línea de este capítulo (doi: 10.1007/978-1-4842-1955-3_1 ) contiene
material complementario, que está disponible para usuarios autorizados.

1
Por ejemplo, puede consultar mi otro libro de Apress, Diseño básico de bases de datos: de novato a profesional.
(Nueva York: Apress, 2012).
2
Más correctamente, es un conjunto de relaciones. En el cuerpo del libro se utilizan palabras comunes como tabla y fila .
En el Apéndice 2 presentamos el vocabulario y la notación más formales.

© Clare Churcher 2016 1


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_1
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Atributos
Cuando se crea una tabla, necesitamos especificar qué información contendrá. Por ejemplo, una tabla de miembros puede
contener información sobre nombres, direcciones y detalles de contacto. Necesitamos decidir cuáles serán los datos individuales.
Por ejemplo, podríamos optar por separar la información del nombre en un título, un nombre, un apellido, iniciales y un nombre
preferido. Este tipo de separación nos permite una mayor flexibilidad en la forma en que se utilizan los datos. Por ejemplo,
podemos dirigir la correspondencia al Sr. JA Stevens y comenzar el mensaje con Dear Jim . Cada una de estas piezas separadas
de información es un atributo de la tabla.
Para definir un atributo, debemos proporcionar un nombre (por ejemplo, FamilyName, Handicap o DateOfBirth) y un dominio.
o tipo. Un dominio es un conjunto de valores permitidos y puede ser algo muy general o algo muy específico.
Por ejemplo, el dominio para columnas que almacenan fechas puede ser cualquier fecha válida (de modo que el 29 de febrero solo
se permite en años bisiestos), mientras que para columnas que contienen cantidades, el dominio puede ser valores enteros mayores
que 0. Inicialmente, podríamos pensar que el dominio para un atributo FamilyName podría ser cualquier cadena de caracteres, pero
pensándolo bien, tendremos que considerar si se permiten algunos signos de puntuación (probablemente sí), si se permiten números
(difícil de decir) y si debe haber una longitud mínima o máxima. Todos los sistemas de bases de datos tienen dominios o tipos
incorporados , como texto, número entero o fecha, que se pueden elegir para cada uno de los campos de una tabla. Los productos más
sofisticados permiten al usuario definir sus propios tipos, que se pueden usar en todas las tablas. Por ejemplo, podríamos definir un tipo
llamado CarRegistration que tenga una plantilla predeterminada de letras y dígitos. Incluso si no es posible definir sus propios tipos,
todos los buenos sistemas de bases de datos permiten al diseñador especificar restricciones sobre un atributo particular en una tabla.
Por ejemplo, en una tabla en particular podemos especificar que una fecha de nacimiento es una fecha en el pasado o que un hándicap
está entre 0 y 40. Es posible que se permita que algunos atributos estén vacíos, mientras que otros pueden requerir que tengan un valor.
Cuando vemos la tabla, los nombres de los atributos son los encabezados de columna y el dominio o tipo proporciona el
conjunto de valores permitidos. Una vez que hemos definido la tabla, agregamos datos proporcionando una fila para cada
instancia. Por ejemplo, si tenemos una tabla de miembros, como en la Figura 1-1, cada fila representa un miembro.

Figura 1-1. La tabla de miembros

2
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

La clave principal
Una de las características más importantes de una tabla de base de datos relacional es que cada una de sus filas debe ser única.
Dos filas de una tabla no deben tener valores idénticos para cada atributo. Si consideramos los datos de nuestros miembros, está
claro por qué esta restricción de unicidad es tan importante. Si, en la tabla de la Figura 1-1 (digamos,, teníamos dosNolan),
para Brenda filas idénticas
no
tendríamos forma de diferenciarlos. Podríamos asociar un equipo con una fila y un pago de suscripción con la otra, generando así
todo tipo de confusión.
La forma en que una base de datos relacional mantiene la unicidad de las filas en una tabla es especificando una clave
principal. Una clave principal es un atributo, o un conjunto de atributos, que se garantiza que sea diferente en cada fila de
una tabla determinada. Para datos como los datos de los miembros en este ejemplo, no podemos garantizar que todos nuestros
miembros tengan nombres o direcciones diferentes (un padre y un hijo pueden compartir un nombre y una dirección y ambos
pertenecen al club). Es importante que haya suficientes atributos para poder distinguir las filas en una tabla. Agregar una fecha de
nacimiento resolvería el problema mencionado anteriormente. Tratar con una gran cantidad de atributos como clave principal puede
volverse engorroso, por lo que para ayudar a distinguir diferentes miembros, hemos incluido un número de ID como uno de los
atributos en la tabla de la Figura 1-1 . Ahora podemos identificar de forma única a un miembro especificando
ventaja añadida de su
queID.también
Esto tiene la
podemos
hacer un seguimiento de los miembros si cambian de nombre. Agregar un número de identificación (a veces denominado clave
sustituta ) es muy común en las tablas de bases de datos. Si MemberID se define como la clave principal para la tabla de miembros,
el sistema de base de datos se asegurará de que en cada fila el valor de MemberID sea diferente. El sistema también se asegurará de
que el campo de clave principal siempre tenga un valor. Es decir, nunca podemos agregar una fila que tenga un campo MemberID
vacío. Estos dos requisitos para un campo de clave principal (exclusividad y no estar vacío) aseguran que dado un valor para MemberID

, siempre podemos encontrar una sola fila que represente a ese miembro. Veremos que esto también es importante.
cuando comencemos a ver las relaciones entre tablas más adelante en este capítulo.
El siguiente código muestra el código SQL para crear la tabla de miembros que se muestra en la Figura 1-1 . Cada
atributo tiene un nombre y tipo especificado. En SQL, la palabra clave INT significa un número entero o no fraccionario, y
CHAR(n) significa una cadena de caracteres de n longitud. El código también especifica que MemberID será la clave principal. Cada
tabla en una base de datos bien diseñada debe tener una cláusula de clave principal.

CREAR TABLA Miembro (


ID de miembro CLAVE PRIMARIA INT,
Apellido CHAR(20),
Nombre CHAR(20),
Hándicap INT,
Fecha de ingreso DATETIME,
Género CHAR(1));

Insertar y actualizar filas en una tabla


El énfasis de este libro está en obtener información precisa de una base de datos, pero los datos primero tienen que ingresar de
alguna manera. La mayoría de los desarrolladores de aplicaciones de bases de datos proporcionarán interfaces fáciles de usar para
insertar datos en las distintas tablas. A menudo se presenta un formulario al usuario para ingresar datos que pueden terminar en
varias tablas. La Figura 1-2 muestra un formulario simpleendelaMicrosoft
tabla de Miembros.
© Access que permite a un usuario ingresar y modificar datos

3
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-2. Un formulario que permite la entrada y actualización de datos en la tabla de Miembros

Es posible construir formularios web o utilizar lectores mecánicos, como lectores de códigos de barras, que pueden recopilar
datos e insertarlos en una base de datos. Los datos también se pueden agregar con actualizaciones masivas desde archivos o se pueden
importar desde otras aplicaciones. Detrás de todos los diferentes mecanismos de actualización de datos, se generan consultas de
actualización de SQL. Veremos tres tipos de consultas para insertar o cambiar datos solo para tener una idea de cómo se ven.

El código que sigue muestra el SQL para ingresar una fila completa en nuestra tabla de miembros. Los elementos de datos son
en el mismo orden que se especificó cuando se creó la tabla. Tenga en cuenta que los valores de fecha y cadena deben estar entre
comillas simples.

INSERTAR EN Miembro
VALORES (118, 'McKenzie', 'Melissa', '963270', 30, '05/10/1999', 'F')

Si muchos de los elementos de datos están vacíos, podemos especificar qué atributos tendrán valores. Si solo tuviéramos la
identificación y el apellido de un miembro, podríamos insertar solo esos dos valores como se muestra aquí:

INSERT INTO Miembro (MemberID, LastName)


VALORES (258, 'Olson')

Al agregar una nueva fila como se acaba de ver, siempre debemos proporcionar un valor para la clave principal.
También podemos modificar registros que ya están en la base de datos con una consulta de actualización. la siguiente consulta
encontrará la fila para el miembro con ID 118 y luego actualizará el número de teléfono:

ACTUALIZAR miembro

ESTABLECER Teléfono = '875077'


DONDE ID de miembro = 118

Esta consulta especifica qué filas se van a cambiar (la cláusula WHERE) y también especifica el campo que se va a cambiar.
actualizada (la cláusula SET).

4
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Diseño de tablas apropiadas


Incluso un sistema de base de datos bastante modesto tendrá cientos de atributos: nombres, fechas, direcciones, cantidades, precios,
descripciones, números de identificación, etc. Todos estos deben encontrar su camino en las tablas, y colocarlos en las tablas
correctas es fundamental para la precisión y utilidad general de la base de datos. Pueden surgir muchos problemas por tener atributos
en las tablas "incorrectas". Como una simple ilustración de lo que puede salir mal, mostraré brevemente los problemas asociados con
tener información redundante.
Digamos que queremos agregar equipos y noches de práctica a la información que mantenemos sobre los miembros de nuestro
club de golf. Podríamos agregar estos dos campos a la tabla Miembro, como en la Figura 1-3 .

Figura 1-3. Posible tabla de miembros

Inmediatamente, podemos ver que ha habido un problema con la entrada de datos porque Brenda Nolan tiene una noche
de práctica diferente al resto de los miembros de su equipo. La información sobre la noche de práctica de cada equipo se
almacena varias veces, por lo que inevitablemente surgirán inconsistencias. Si formulamos una consulta para encontrar la noche
de práctica para TeamB, ¿qué esperaríamos como respuesta? ¿Debería ser lunes, martes o ambos?

El problema aquí es que (en el lenguaje de la base de datos) la tabla no está correctamente normalizada . La normalización es
una forma formal de verificar si los atributos están en la tabla correcta. Está fuera del alcance de este libro profundizar en la
normalización, pero le mostraré brevemente cómo evitar el problema en este caso particular.
El problema es que estamos tratando de mantener información sobre dos cosas diferentes en nuestra tabla de miembros:
información sobre cada miembro (ID, nombres, etc.) e información sobre los equipos (las noches de práctica).
El atributo PracticeNight está en la tabla incorrecta. La figura 1-4 muestra una mejor solución con dos tablas: una para información
sobre los miembros y otra para información sobre los equipos.

5
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-4. Tablas de miembros y equipos

Esta separación de la información en dos tablas evita los datos inconsistentes que teníamos anteriormente. La noche de
práctica de cada equipo se almacena solo una vez. Si necesitamos averiguar qué noche Brenda Nolan debería estar en la práctica,
ahora debemos consultar dos tablas: la tabla de miembros para encontrar su equipo y luego la tabla de equipos para encontrar la noche
de práctica para ese equipo. La mayor parte de este libro trata sobre cómo hacer ese tipo de recuperación de datos.

Introducción a los modelos de datos


Incluso las bases de datos más simples pueden tener varias tablas. Un modelo de datos es un modelo conceptual de los datos
subyacentes y cómo se interrelacionan. Usaremos la notación de diagrama de clases del Lenguaje de Modelado Unificado ( UML ) 3
para representar nuestros modelos de datos. Hay muchas otras formas de representar la estructura de
datos (por ejemplo, diagramas de relación de entidad) que, para los fines de este libro, también serían adecuadas.
Elegimos usar UML ya que tiene un gran conjunto de herramientas de diagramación para desarrollar aplicaciones de software
que abarca no solo la estructura de los datos sino también su comportamiento. En esta sección, veremos cómo interpretar un
diagrama de clases y cómo traducirlo en tablas y restricciones en una base de datos relacional.
Una clase es como una plantilla para algo sobre lo que queremos conservar datos (eventos, personas, lugares, etc.). Por ejemplo,
es posible que queramos conservar nombres y otros detalles sobre los miembros de nuestro club de golf. La figura 1-5 muestra la
notación UML para una clase de miembro. El nombre de la clase está en el panel superior y el siguiente panel muestra los atributos .
Los diagramas de clase también pueden tener otro panel para mostrar métodos asociados con el comportamiento de la clase.

3 Si desea obtener más información sobre UML, consulte Grady Booch, James Rumbaugh e Ivar Jacobsen, The Unified
Modeling Language User Guide (Boston, MA: Addison Wesley, 2005). Los estándares actuales se pueden encontrar en
http://www.uml.org/ .

6
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-5. Representación UML de una clase Miembro

En una base de datos relacional, cada clase se representa como una tabla, los atributos son las columnas y cada instancia (en
este caso, un miembro individual del club) será una fila en la tabla.
El modelo de datos también puede representar la forma en que las diferentes clases dependen unas de otras. La figura 1-6
dos clases, miembro y equipo , muestra cómo están relacionados.

Figura 1-6. Una relación entre dos clases.

El par de números en cada extremo de la línea de jugadas por en la Figura 1-6 indica cuántos miembros juegan para un equipo
en particular, y viceversa. El primer número de cada par es el número mínimo. Suele ser 0 o 1 y, por lo tanto, a veces se conoce como
opcionalidad (es decir, indica si un miembro debe tener un equipo asociado o viceversa). El segundo número (conocido como
cardinalidad ) es el mayor número de objetos relacionados. Por lo general, es 1 o muchos (indicados por n o * ), aunque son posibles
otros números.
Las relaciones se pueden interpretar en ambas direcciones. La etiqueta de la relación en la Figura 1-6 implica
que estamos leyendo de izquierda a derecha y necesitaremos pensar en el verbo apropiado para interpretar el diagrama en la otra
dirección. "El equipo tiene miembros" servirá. Al leer la Figura 1-6 de izquierda a derecha, vemos que un miembro en particular no
tiene que jugar para un equipo y puede jugar como máximo para un equipo (los números 0 y 1 al final de la línea más cercana a la
clase Equipo) . Leyendo de derecha a izquierda, podemos decir que un equipo en particular no necesita tener ningún miembro, pero
puede tener muchos (los números 0 y n más cercanos a la clase Miembro).
Una relación como la de la Figura 1-6 se denomina relación 1: muchos (un miembro puede pertenecer a un solo equipo y un equipo
puede tener muchos miembros).
Podrías pensar que debería haber exactamente cuatro miembros para un equipo (por ejemplo, para un equipo entre clubes). A pesar de que
esto puede ser cierto cuando el equipo juega una ronda de golf, nuestra base de datos puede registrar diferentes números de
miembros asociados con el equipo a medida que agregamos y eliminamos jugadores a lo largo del año. Un modelo de datos por lo general

7
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

usa 0, 1 y muchos para modelar las relaciones entre tablas. Otras restricciones (como el número máximo en un equipo) se expresan más
4
generalmente con reglas comerciales o con casos de uso de UML.
Podemos representar una relación 1-Muchos en nuestra base de datos mirando la clave principal en el extremo 1 de la relación y agregando una
columna del mismo tipo a la tabla en el extremo Muchos. Para el modelo de la Figura 1-6
agregaríamos una columna Equipo a la tabla Miembro como se muestra en la Figura 1-7 .

Figura 1-7. Tabla de miembros con una columna de clave externa Equipo

La columna Equipo se denomina clave externa . Cualquier valor que no esté vacío en esta columna de la tabla Miembro debe
ser un valor que ya existe en la columna de clave principal de la tabla Equipo. El concepto de clave externa nos proporciona una restricción en la
tabla de miembros para que no podamos asignar miembros a equipos inexistentes.
Esta restricción se llama integridad referencial .
El SQL para crear una tabla con una clave foránea se muestra aquí:

CREAR miembro de la TABLA (


ID de miembro CLAVE PRIMARIA INT,
Apellido CHAR(20),
Nombre CHAR(20),
CARACTERÍSTICAS DEL TELÉFONO(20),

Hándicap INT,
Fecha de ingreso DATETIME,
Género CARÁCTER(1),
Equipo CHAR(20) REFERENCIAS CLAVE EXTRANJERAS Equipo);

Porque necesitamos comparar el valor en la columna de clave foránea de la tabla Miembro con el principal
columna clave de la tabla Equipo, estas dos columnas deben tener el mismo dominio o tipo de datos.
La mayoría de los productos de bases de datos tienen una interfaz gráfica para configurar y mostrar restricciones de clave externa.
La Figura 1-8 muestra las interfaces para Microsoft © SQL Server y Microsoft © Access. Estos diagramas, que son esencialmente implementaciones
del modelo de datos, son invaluables para comprender la estructura de la base de datos para que sepamos cómo extraer la información que
necesitamos.

4 Alistair Cockburn, Escritura de casos de uso efectivo (Boston, MA: Addison Wesley, 2001).

8
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-8. Diagramas para implementar 1 – Muchas relaciones usando claves foráneas

Las tablas de las Figuras 1-4 y 1-7 tienen esencialmente el mismo diseño. Para la Figura 1-4 llegamos al diseño
eliminando la columna PracticeNight de la tabla de Miembros y creando una nueva tabla de Equipo (un proceso de
normalización). Para la Figura 1-7 , primero consideramos un modelo de datos y agregamos la columna Equipo a la tabla
Miembro como una forma de representar la relación entre Miembro y Equipo. El resultado es el mismo
en que sea
se aborde
cual sea
el la
problema.
forma

A riesgo de repetirme, quiero advertir sobre la necesidad de garantizar que la base de datos esté diseñada correctamente.
Es casi seguro que el modelo simple de la figura 1-6 es bastante inadecuado incluso para la pequeña cantidad de datos que
contiene. Un club real probablemente querrá hacer un seguimiento de cómo evoluciona la membresía de los equipos a lo
largo de los años. Esto implicará incluir información sobre temporadas o años junto con la información de membresía del
equipo. Algunos miembros pueden jugar para más de un equipo durante un año si son llamados como suplentes. Esa información
puede o no ser necesaria para retener. Diseñar una base de datos útil es un trabajo complicado y está fuera del alcance de este
libro. 5

Recuperación de información de una base de datos


Ahora que tenemos una base de datos bien diseñada que consta de tablas normalizadas interrelacionadas, podemos
comenzar a ver cómo extraer información mediante consultas. Cuando me refiero a extraer o recuperar información no me
refiero a que estemos eliminando ningún dato. Piense en una consulta como si proporcionara una ventana a una pequeña parte
de la base de datos. Muchos sistemas de bases de datos tendrán una interfaz esquemática que puede ser útil para consultas simples.
La Figura 1-9 muestra la interfaz de Microsoft © Access para recuperar los nombres de los miembros senior de la tabla
de miembros. Las marcas de verificación indican qué columnas queremos recuperar y la fila Criterios nos permite especificar
condiciones en las filas que se devuelven.

5
Para obtener más información sobre el diseño de bases de datos, consulte mi otro libro de Apress, Beginning Database Design: From
Novice to Professional (Nueva York: Apress, 2012).

9
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-9. Interfaz de acceso para una consulta simple en la tabla de Miembros

La aplicación tomará la información de la interfaz gráfica y construirá una consulta SQL.


La mayoría de las aplicaciones le mostrarán el SQL que se genera y usted mismo puede modificarlo o escribirlo desde cero.
El SQL equivalente a la consulta representada en la Figura 1-9 es:

SELECCIONE Nombre, Apellido


DE Miembro
WHERE MemberType = 'Senior';

Esta consulta SQL contiene tres cláusulas: SELECT especifica qué columnas devolver, FROM especifica la(s) tabla(s)
donde se guarda la información y WHERE especifica las condiciones que deben satisfacer las filas devueltas.
Veremos la estructura de las sentencias SQL con más detalle más adelante, pero por ahora la intención de la consulta es
bastante clara.
Como necesitamos incluir más y más tablas conectadas en una variedad de formas, las interfaces esquemáticas
rápidamente se vuelven difíciles de manejar y, a menudo, necesitamos escribir los comandos SQL directamente. A menudo,
es más fácil pensar en una consulta de forma más abstracta. Con una clara comprensión abstracta de lo que se requiere, resulta
más sencillo convertir la idea en una instrucción SQL adecuada. Hay dos formas diferentes de abordar las consultas en una base
de datos relacional.

Enfoque de proceso Una forma

de abordar una consulta es pensar en términos de las operaciones que debemos realizar en las tablas. Pensemos en cómo
podríamos obtener una lista de nombres de miembros que practican un lunes. Podríamos imaginarnos primero recuperando
solo las filas de la tabla Team que tienen Monday en la columna PracticeNight. Luego podríamos unir esas filas con la tabla de
miembros (más información sobre las uniones más adelante) y luego extraer los nombres del resultado. Llamaremos a esto el
enfoque de proceso , ya que es una serie de pasos llevados a cabo en un orden particular.
La Figura 1-10 muestra los pasos recién descritos.

10
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-10. El enfoque de proceso: pensar en una consulta como una secuencia de operaciones

Enfoque de resultado Una forma

alternativa de pensar en la consulta de la sección anterior es examinar todas las filas en el Miembro
y solo devuelva aquellos que satisfagan los criterios de que el miembro está en un equipo que tiene el lunes como
noche de práctica. La Figura 1-11 muestra este tren de pensamiento. La fila m que estamos considerando en la tabla de
miembros satisface la condición sobre la noche de práctica del equipo, por lo que debemos recuperar los nombres de esa fila.

11
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Figura 1-11. Considerando si la fila m cumple con los criterios para la consulta.

Llamaremos a este tipo de pensamiento sobre una consulta enfoque de resultados porque describimos lo que
queremos en lugar de cómo obtenerlo.

Por qué consideramos dos enfoques La teoría de bases de datos

relacionales tiene su origen en la teoría de conjuntos. Si pensamos en nuestras tablas como conjuntos de filas, entonces una
consulta es una pregunta que requiere que manipulemos esos conjuntos para recuperar un subconjunto que contenga la
información que necesitamos. La teoría relacional tiene dos formas formales de especificar los criterios para extraer
subconjuntos de filas: álgebra relacional y cálculo relacional.
No necesitamos estas ideas abstractas para consultas simples. Sin embargo, si todas las consultas fueran simples, usted
no estar leyendo este libro. En primera instancia, las consultas se expresan en un lenguaje cotidiano que suele ser
ambiguo. Prueba esta simple expresión: "Encuéntrame a todos los estudiantes menores de 20 años o que vivan en casa y
obtén una asignación". Esto puede significar diferentes cosas dependiendo de dónde inserte las comas. Por ejemplo, una
coma después de "20" lleva a la interpretación de que todos los menores de 20 años están incluidos, mientras que una coma
después de "casa" sugiere que también deben recibir una asignación. Incluso después de haber resuelto lo que significa la
expresión en lenguaje natural, tenemos que pensar en la consulta en términos de las tablas reales en la base de datos. Esto
significa tener que ser bastante específico en la forma en que expresamos la consulta. Tanto el álgebra relacional como el
cálculo relacional nos brindan una forma poderosa de ser precisos y específicos.
¿Por qué no omitir todas estas cosas abstractas y seguir adelante y aprender SQL? Bueno, el lenguaje SQL consta de
elementos tanto de cálculo como de álgebra. Las versiones anteriores de SQL se basaban puramente en el cálculo relacional en
el sentido de que describías lo que querías recuperar en lugar de cómo . Las implementaciones modernas de SQL le permiten
especificar explícitamente operaciones algebraicas como uniones, uniones e intersecciones en las tablas también.
A menudo hay varias formas equivalentes de expresar una sentencia SQL. Algunas formas se basan en gran medida
en el cálculo, algunas se basan en el álgebra y otras son un poco de ambas. Durante mi tiempo como profesor universitario,
a menudo preguntaba a la clase si encontraban las expresiones de cálculo o álgebra más intuitivas para una consulta en
particular. La clase por lo general estaba igualmente dividida. Personalmente, encuentro que algunas consultas simplemente
se sienten obvias en términos de álgebra relacional, mientras que otras se sienten mucho más simples cuando se expresan
en cálculo relacional. Una vez que tengo la idea definida con uno u otro, la traducción a SQL (o algún otro lenguaje de consulta)
suele ser sencilla.

12
Machine Translated by Google

CAPÍTULO 1 ÿ DESCRIPCIÓN GENERAL DE LA BASE DE DATOS RELACIONALES

Podemos hacer uso de las ideas del álgebra relacional y el cálculo relacional sin profundizar en las matemáticas. En el
cuerpo del libro me refiero al enfoque de proceso (álgebra) y al enfoque de resultado
(cálculo). Cuantas más herramientas tenga a su disposición, más probable será que pueda expresar consultas complejas con
precisión. En el Apéndice 2 hay una introducción a la notación formal para el álgebra relacional y el cálculo relacional para
aquellos de ustedes que deseen agregar eso a su arsenal.

Resumen
En este capítulo se ha presentado una descripción general de las bases de datos relacionales. Hemos visto que una base de
datos relacional consta de un conjunto de tablas que representan los diferentes aspectos de nuestros datos (por ejemplo, una
tabla para miembros y una tabla para equipos). Los atributos necesarios para describir a los miembros o equipos se convierten en
las columnas de las tablas y cada columna tiene un conjunto de valores permitidos (un dominio). Cada tabla debe tener una clave
principal, que es un atributo o conjunto de atributos garantizados para tener un valor diferente para cada fila.
Es posible establecer restricciones entre tablas con claves foráneas. Una clave externa es un valor para una
columna (s) en una tabla que ya debe existir como un valor en la (s) columna (s) de clave principal de otra tabla.
Por ejemplo, el valor de Equipo en la tabla Miembro debe ser uno de los valores en el campo de clave principal de la tabla Equipo.

Suele ser útil pensar en las consultas de forma abstracta, y hay dos formas de hacerlo. El enfoque de proceso requiere
que pensemos en las operaciones que se pueden aplicar a las tablas en una base de datos. Es una forma de describir cómo
necesitamos manipular las tablas para extraer la información que necesitamos. El enfoque de resultados requiere que pensemos
qué criterios debe satisfacer nuestra información requerida. Diferentes personas encontrarán que uno u otro de estos enfoques se
siente más natural para diferentes consultas. SQL es un lenguaje para especificar consultas en una base de datos. Por lo general,
hay muchas formas equivalentes de especificar una consulta en SQL.
Algunos reflejan el enfoque del proceso y otros reflejan el enfoque del resultado, y algunos son un poco de ambos.

13
Machine Translated by Google

CAPITULO 2

Consultas simples en una tabla

Si una base de datos ha sido diseñada correctamente, los datos estarán ubicados en varias tablas diferentes. Por ejemplo, nuestra
base de datos de golf tiene tablas separadas para información sobre miembros, equipos y torneos, así como tablas que conectan
estos valores; por ejemplo, qué miembros juegan en qué equipos, participan en qué torneos, etc. Para hacer el mejor uso de
nuestros datos, necesitaremos inspeccionar los valores de diferentes tablas para recuperar la información que necesitamos.

En este capítulo, veremos cómo recuperar información de una sola tabla. La tabla puede ser una de las tablas permanentes
de la base de datos, o puede ser una tabla virtual que se ha reunido temporalmente como parte de una consulta más complicada.

He estado hablando de manera bastante imprecisa sobre "recuperar" filas y "devolver" información.
¿Qué sucede con las filas que resultan de una consulta? En realidad, no estamos eliminando datos de las tablas y colocándolos
en alguna parte. Una consulta es como una ventana a la base de datos a través de la cual podemos ver sólo la información que
necesitamos. Si los datos en la base de datos subyacente cambian, los resultados de nuestra consulta también cambiarán. No
está de más pensar en la información que resulta de una consulta como "recuperada" en una tabla "virtual", siempre que se dé cuenta
de que es solo temporal.

Subconjuntos de Filas y Columnas


Seleccionar subconjuntos de filas y/o columnas es una de las operaciones más comunes que realizaremos en una consulta.
En las siguientes secciones, veremos cómo seleccionar filas y columnas de una de las tablas originales en la base de datos. 1 Las
mismas ideas se aplican a la recuperación de información de tablas virtuales que resultan de otras manipulaciones de los datos.

Para determinar qué filas recuperar de una tabla, es necesario especificar una declaración de condición , el cual es un
que sea verdadera o falsa. Aplicamos la condición a cada fila de la tabla de forma independiente, reteniendo aquellas filas
para las que la condición es verdadera y descartando las demás. Digamos que queremos encontrar a todos los adultos
mayores en el club de golf. Solo queremos ese subconjunto de filas de la tabla Miembro donde el valor en el campo Tipo de
miembro es "Senior", como se muestra en la Figura 2-1 .

1
En los términos formales del álgebra relacional, la recuperación de un subconjunto de filas (tuplas) de una tabla (relación) se conoce como
operación de selección y la recuperación de un subconjunto de atributos (columnas) se conoce como operación de proyecto . Consulte el Apéndice 2
para obtener más información.

© Clare Churcher 2016 C. 15


Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_2
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Figura 2-1. Recuperando el subconjunto de filas para miembros senior.

El SQL de la consulta para recuperar miembros senior es el siguiente:

SELECCIONE *

DE Miembro
DONDE Tipo de miembro = 'Senior'

Esta consulta tiene tres partes, o cláusulas : La cláusula SELECT dice qué columnas recuperar. En este caso, significa
*
recuperar todas las columnas. La cláusula FROM dice qué tabla(s) involucra la consulta, y WHERE
La cláusula describe la condición para decidir si una fila en particular debe incluirse en el resultado. La condición dice que verifique el
valor en el campo MemberType. En SQL, cuando especificamos un valor real para un
carácter o campo de texto, debemos encerrar el valor entre comillas simples, como en 'Senior' .
Ahora veamos cómo podemos especificar que queremos ver solo algunas de las columnas en nuestro resultado. voy a
generalmente se refieren a seleccionar un subconjunto de filas y proyectar un subconjunto de columnas. A menudo, la proyección
de un subconjunto de columnas es el último paso de una serie de operaciones. Podemos pensar en recopilar todos los datos que
necesitamos y luego, al final, pedir solo los atributos o las columnas que necesitamos. Veremos en el Capítulo 7 que a veces también
necesitamos proyectar columnas similares desde tablas originales o virtuales antes de aplicar algunas de las operaciones de conjuntos,
como la unión y la intersección.
Si queremos una lista de teléfonos de todos los miembros, no necesitamos información adicional como discapacidades o unirse.
fechas. La Figura 2-2 muestra un subconjunto de las columnas de nombre y número de teléfono de la tabla Miembro.

dieciséis
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Figura 2-2. Proyectar un subconjunto de columnas para proporcionar una lista de teléfonos

El SQL para recuperar las columnas de nombre y teléfono de la tabla de miembros es:

SELECCIONE Apellido, Nombre, Teléfono


DE Miembro

Como queremos ver estos valores de columna para cada fila, esta consulta no tiene una cláusula WHERE.
Es muy sencillo combinar la recuperación de subconjuntos de filas y columnas. Podríamos hacer esto si
quería una lista de teléfonos solo para los miembros más antiguos, como en la Figura 2-3 .

17
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Figura 2-3. Recuperación de un subconjunto de filas y columnas para producir una lista de teléfonos de miembros senior

El SQL para la consulta representada en la Figura 2-3 es:

SELECCIONE Apellido, Nombre, Teléfono


DE Miembro
DONDE Tipo de miembro = 'Senior'

Uso de alias
A medida que nuestras consultas se vuelvan más complicadas, incorporarán una serie de tablas diferentes. Algunas de las tablas
pueden tener los mismos nombres de columna y es posible que debamos distinguirlas entre sí. En SQL podemos anteponer cada uno
de los atributos de nuestra consulta con el nombre de la tabla de la que proviene, como se muestra aquí:

SELECT Member.LastName, Member.FirstName, Member.Phone FROM Member


WHERE Member.MemberType = 'Senior'

Porque teclear el nombre completo de la tabla puede volverse tedioso, y también porque en algunas consultas podríamos
necesita comparar datos de más de una fila de una tabla, SQL tiene la noción de un alias . Echa un vistazo a la siguiente consulta:

SELECCIONE m.Apellido, m.Nombre, m.Teléfono DESDE


Miembro m DONDE m.MemberType = 'Senior'

18
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

En la cláusula FROM, hemos declarado un alias o nombre alternativo para la tabla de miembros, en este caso m. Podemos
darle a nuestro alias cualquier nombre o letra que queramos; más corto es mejor. Luego, en el resto de la consulta podemos
usar el alias siempre que queramos especificar un atributo de esa tabla. Es una buena idea adquirir el hábito de usar un alias de
tabla para cada tabla que contribuya a la consulta.

Guardar consultas
Es posible mantener el resultado de una consulta en una nueva tabla permanente (a veces llamada instantánea ), pero
generalmente no queremos hacerlo porque quedará obsoleta si los datos subyacentes cambian. Lo que normalmente
queremos hacer es guardar las instrucciones de consulta para poder hacer la misma pregunta otro día. Considere nuestra
consulta de la lista de teléfonos. De vez en cuando, después de que se haya actualizado la membresía del club, produciremos
una nueva lista de teléfonos. En lugar de tener que construir la consulta cada vez, podemos guardar las instrucciones en lo que
se conoce como una vista . El siguiente código muestra cómo crear una vista que podemos usar para proporcionar listas de
teléfonos actualizadas. Tenemos que dar un nombre a la vista, que puede ser cualquier cosa que queramos (PhoneList parece
sensato), y luego proporcionamos la declaración SQL para recuperar los datos apropiados:

CREAR VER Lista de teléfonos


COMO SELECCIONAR m.Apellido, m.Nombre,
m.Teléfono DE Miembro m

Puede pensar en PhoneList como las instrucciones para crear una tabla "virtual" que podemos usar en otras consultas
de la misma manera que usamos tablas reales. Solo debemos recordar que la tabla virtual se crea sobre la marcha ejecutando
la consulta en la tabla de miembros permanentes y luego desaparece. Para obtener nuestra lista de teléfonos ahora, simplemente
podemos usar la vista PhoneList:

SELECCIONE * DE la lista de teléfonos

Especificación de condiciones para seleccionar filas


En las consultas que vimos en las secciones anteriores, usamos condiciones o criterios muy simples para determinar
si incluir una fila en el resultado de una consulta. En la siguiente sección, veremos más de cerca las diferentes formas en
que puede especificar condiciones más complicadas.

Operadores de comparación Una condición

es una declaración o expresión que es verdadera o falsa, como MemberType = 'Senior' . Este
tipo de expresiones se denominan expresiones booleanas en honor al matemático inglés del siglo XIX, George Boole, quien
investigó sus propiedades. Las condiciones que usamos para seleccionar filas de una tabla generalmente implican comparar
los valores de un atributo con algún valor constante u otro atributo. Por ejemplo, podemos preguntar si el valor de un atributo
es igual, diferente o mayor que algún valor. La tabla 2-1 muestra algunos operadores de comparación que podemos usar en
nuestras consultas.

19
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Tabla 2-1. Operadores de comparación

Operador Significado Ejemplos de declaración verdadera

= igual 5=5 , 'Junior' = 'Junior'

< Menos que 4<5 , 'Ann' < 'Zebedeo'

<= Menos que o igual a 4<=5 , 5<=5

> Mas grande que 5>4 , 'Zebedeo' > 'Ann'

>= Mayor qué o igual a 5>=4 , 5>=5

<> No es igual 5<>4 , 'Junior' <> 'Mayor'

Solo una nota rápida de precaución: en la Tabla , algunos de nuestros ejemplos comparan números, y algunos
2-1 compare caracteres. Recuerde del Capítulo 1 que cuando creamos una tabla, especificamos el tipo de cada campo; por
ejemplo, MemberID se declaró como INT (entero o número completo) y LastName como CHAR(20) (un campo de 20
caracteres). Con campos como enteros, las comparaciones son numéricas. Con campos de texto o de caracteres, las
comparaciones son alfabéticas, y con campos de fecha y hora, las comparaciones son cronológicas (las fechas anteriores van
primero).
2 o el valor Unicode del
Cuando comparamos los atributos de los caracteres, la comparación se basa en los
caracteres ASCII. Como podríamos esperar, "A" (valor ASCII 65) viene antes que "Z" (ASCII 90), por lo que "A" < "Z". Con una
cadena de caracteres, si la primera letra es la misma entonces el orden lo decide la segunda, y así sucesivamente. Así que
"ANNABEL" < "ANNE". Sin embargo, los caracteres en minúsculas tienen códigos ASCII más altos que los de mayúsculas. Esto
significa que “a” (ASCII 97) > “Z” (ASCII 90). Si ordena una lista de nombres alfabéticamente, de manera predeterminada,
aparecerá un nombre que comience con una letra minúscula después de los que comiencen con letras mayúsculas. Por ejemplo,
"van Dyke" aparecerá después de "Zebedee".
Si ponemos números en un campo de caracteres, también se ordenarán alfabéticamente. Esto significa que tendrá
comparaciones como "400" < "5", porque el primer carácter, "4" (ASCII 34), en el texto de la izquierda es menor que el
primer carácter, "5" (ASCII 35), al lado derecho. Por lo tanto, asegúrese de que si una columna va a contener números que
desea comparar y ordenar numéricamente, que se declare como un tipo numérico, o obtendrá resultados bastante
sorprendentes de sus consultas. Del mismo modo, las fechas deben estar en una columna declarada con uno de los tipos de
fecha o las comparaciones y el orden pueden no ser los esperados.
Con los operadores de comparación, podemos crear muchas consultas diferentes. La tabla 2-2 muestra algunos ejemplos de
Expresiones booleanas que podemos usar como condiciones en la cláusula WHERE de una instrucción SQL para
seleccionar filas de la tabla de miembros.

Tabla 2-2. Ejemplos de expresiones booleanas en la tabla de miembros

Expresión Filas recuperadas

Tipo de miembro = 'Junior' Todos los miembros menores

Hándicap <= 12 Todos los socios con un hándicap de 12 o menos


Fecha de ingreso >= '01/01/2008' Todos los que se han unido después de principios de 2008
Género = 'F' Todas las mujeres

2 http://www.asciitable.com/

20
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Algunas implementaciones de SQL distinguen entre mayúsculas y minúsculas al comparar texto y otras no. siendo caso
sensitivo significa que las letras mayúsculas se tratan como si fueran diferentes de sus equivalentes en minúsculas; en otras
palabras, "Junior" es diferente de "junior", que es diferente de "JUNIOR". Por lo general, reviso cualquier nuevo sistema de base
de datos que uso para ver qué hace. Si no le importa el caso del atributo que está considerando (es decir, está feliz de recuperar
filas donde MemberType es "Junior" o "jUnIoR" o lo que sea), puede utilizar la función SQL SUPERIOR. Esto convertirá el valor de
cada atributo de texto en mayúsculas antes de realizar como
la comparación.
se muestraLuego
aquí: puede comparar eso con el valor literal en mayúsculas,

SELECCIONE *
DE Miembro m DONDE SUPERIOR (m.MemberType) = 'JUNIOR'

Operadores lógicos Podemos

combinar expresiones booleanas para crear condiciones más interesantes. Por ejemplo, podemos especificar que dos expresiones
deben ser ambas verdaderas antes de recuperar una fila en particular.
Supongamos que queremos encontrar a todas las jóvenes. Esto requiere dos condiciones para ser verdad: deben ser
mujeres y deben ser juniors. Podemos expresar fácilmente cada una de estas condiciones de forma independiente. Después de
eso, podemos usar el operador lógico AND para exigir que ambas condiciones sean verdaderas:

SELECCIONE *
FROM Miembro m DONDE m.MemberType = 'Junior' AND m.Gender = 'F'

Veremos tres operadores lógicos: AND usa OR , O , y no . Ya hemos visto cómo funciona AND. Si nosotros
entre dos expresiones, entonces solo una de las expresiones debe ser verdadera (pero si ambas son verdaderas, también está
bien). NOT se usa antes de una expresión. Por ejemplo, para nuestra tabla de miembros, podríamos solicitar filas que obedezcan
la condición NOT (MemberType = 'Social') . Esto significa verificar cada fila, y si el valor de MemberType es "Social", entonces no
queremos esa fila. La tabla 2-3 da algunos ejemplos más del uso de operadores lógicos en condiciones.

Tabla 2-3. Ejemplos de expresión de operadores

lógicos Descripción de los datos

MemberType = 'Senior' AND Handicap < 12 Mayores con minusvalía menores de 12 años

MemberType = 'Senior' O Hándicap < 12 Todos los seniors así como cualquier otra persona con un buen
hándicap (los menores de 12 años)

NOT(Tipo de miembro = 'Social') Todos los miembros excepto los sociales (para los datos actuales,
serían solo seniors y juniors)

La Figura 2-4 muestra una representación esquemática de las consultas en la Tabla 2-3 . Cada círculo representa un conjunto
de filas (es decir, las de los socios sociales o las de los socios con minusvalías menores de 12 años). El área sombreada representa
el resultado de la operación.

21
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Sénior <12 Sénior <12

Social

Tipo de miembro = 'Senior' MemberType = 'Senior' O NOT MemberType = 'Social'


Y Hándicap < 12 Hándicap < 12

Figura 2-4. Representación esquemática de los operadores lógicos.

Las tablas de verdad de la figura 2-5 pueden ser útiles para comprender cómo funcionan los operadores lógicos. Leíste que
en las Figuras 2-5a y 2-5b Cada expresión puede , tenemos dos expresiones, una en la parte superior y otra en la parte inferior izquierda.
tener uno de dos valores: Verdadero (V) o Falso (F). Si las combinamos con la expresión booleana AND, la figura 2-5a muestra que
la declaración general es verdadera solo si las dos declaraciones contribuyentes son verdaderas (el cuadrado en la parte superior
izquierda). Si los combinamos con una declaración OR, entonces la declaración general es falsa solo si ambas declaraciones
contribuyentes son falsas (abajo a la derecha de la figura 2-5b ). La tabla de la figura 2-5c dice que si nuestra afirmación original es
verdadera y anteponemos NO, entonces el resultado es falso (columna izquierda) y viceversa.

expresión 1 expresión 1 Expresión


T F T F T F

T T F T T T F T

F F F F T F

a) Y b) O c) NO

Figura 2-5. Tablas de verdad para operadores lógicos (T = verdadero, F = falso)

A veces puede ser un poco complicado convertir descripciones de lenguaje natural en expresiones booleanas. Si le pidieron una
lista que incluyera a todas las mujeres y todos los juniors (¡no pregunte por qué!), podría traducir esto literalmente y escribir la condición
MemberType = 'Junior' AND Gender = 'F' . Sin embargo, el AND significa ambos
las condiciones deben ser verdaderas, por lo que esto nos daría mujeres jóvenes. Lo que realmente significa nuestra declaración en
lenguaje natural es "Quiero la fila para cualquier miembro si es mujer o junior (o ambos)". Ten cuidado.

22
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Tratar con nulos


Los datos de ejemplo en la tabla de Miembros que se muestra anteriormente en la Figura 2-1 son precisos y completos. Cada
fila tiene un valor para cada atributo, excepto Handicap,tan
quelimpios
no se aplica
y ordenados.
a algunos
Consideremos
miembros. Los
algunos
datosdatos
realesdiferentes,
no suelencomo
ser
en la figura 2-6 .

Figura 2-6. Tabla con datos faltantes

Cuando no hay ningún valor en una celda de una tabla, se dice que es nulo . Los valores nulos en una base de
datos pueden causar algunos dolores de cabeza. Considere realizar las siguientes dos consultas: una para producir una lista
de miembros masculinos y la otra una lista de mujeres. Dado que los golfistas deben identificarse como hombre o mujer para
fines de competencia, podríamos suponer que todos los miembros del club aparecerían en una u otra lista. Sin embargo, para
los datos de la Figura, dejaríamos
2-6 estamos fuera a Kim
hablando Spence. reales
de personas Se podría argumentar
y clubes reales conque losmenos
datos datos que
no deberían
precisos yser así, pero
completos.
Tal vez Kim olvidó (o se negó) a completar la parte de género del formulario de solicitud. Podemos protegernos contra esto
insistiendo en que no se permiten valores nulos en un campo en particular cuando creamos una tabla. La siguiente instrucción
SQL muestra cómo podemos hacer que Género sea un campo que siempre requiera un valor:

CREAR TABLA Miembro (


ID de miembro CLAVE PRIMARIA INT,
.....
Género CHAR(1) NO NULO,

....)

Vale la pena tener en cuenta que hacer que los campos sean NOT NULL puede crear más dolores de cabeza de los que cura. si kim
Spence no completó todas las casillas en su solicitud de membresía, pero organizó el pago de la suscripción, entonces
queremos registrarlo como miembro y preocuparnos por los detalles completos más adelante. Si hacemos que Género sea
un campo obligatorio, entonces no podemos ingresar un registro para él/ella en la tabla, o tenemos que adivinar cuál es su
género. Ninguna de estas opciones es una buena estrategia, por lo que es mejor ser parco al hacer que los campos sean
obligatorios. Recuerde que nuestros campos de clave principal (por definición) siempre necesitan un valor.

23
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

No todos los valores de nulo significan que hay un problema con los datos. En nuestra tabla de miembros, un campo puede ser nulo
porque no se aplica a un miembro en particular. La discapacidad de Helen y Sarah puede ser genuinamente nula
porque no tienen discapacidades. Sin embargo, es justo suponer que cada miembro debe tener un valor para MemberType
, espere
y JoinDate, por lo que los valores nulosque
en sus
estas
tablas
columnas
tengan
sedatos
debenfaltantes.
a que no conocemos el valor. En el mundo real,

Encontrar nulos Dado

que en nuestras tablas podemos tener nulos que nos pueden causar problemas, es útil poder encontrarlos.
Después de haber ingresado un lote de nuevos miembros en la base de datos, podemos verificar si hay problemas. Podríamos querer
obtener una lista de todos los miembros que no tienen un valor para Género, digamos.
IS NULL:Para hacer esto podemos usar la frase SQL

SELECCIONE *
DE Miembro m DONDE m.Gender ES NULO

Alternativamente, podríamos querer recuperar solo aquellos miembros que tienen un valor en una celda. Si
queremos los nombres y handicaps de solo aquellos miembros que tienen un valor para Handicap,
NOT podemos usar el
operador para crear la siguiente consulta:

SELECCIONE *
DESDE Miembro m DONDE NO (m.Handicap IS NULL)

Comparaciones que involucran valores nulos Dado que vamos

a tener valores nulos inesperados en nuestras tablas, es importante saber cómo manejarlos. ¿Qué filas coincidirán
con las dos condiciones que se muestran aquí?

Género = 'F'
NO (Género = 'F')

Podrías pensar que si realizamos dos consultas, una para obtener todas las filas que cumplen una condición y
otra para todas las filas que no coinciden, obtendremos la tabla completa. Pero, de hecho, no lo hacemos. Kim no se
incluirá con la primera condición, porque claramente el valor de Género no es igual a 'F' . Pero cuando
preguntamos si el valor NO es 'F', no podemos decirlo porque no sabemos cuál es el valor. Podría ser 'F' si tuviera un valor.
En SQL, cuando comparamos valores nulos con algo, no obtenemos ni Verdadero ni Falso porque simplemente no lo
sabemos. Esto probablemente tenga más sentido si pensamos en las desventajas. Si preguntamos por todos y también
de Sarah nunca se , por
recuperará.
aquellos miembros
La pregunta
que
nocumplen
se aplicacon
a ella:
NOTno(Hándicap 12) o Hándicap <=12 Hándicap > 12, la ,fila
tiene una >discapacidad.

Una vez que tomamos en cuenta los valores nulos, nuestras expresiones para las condiciones en realidad podrían tener uno de tres
valores: Verdadero, Falso o “No sé”. Así es más o menos cómo funciona el mundo, si lo piensas. Solo las filas que
son verdaderas para una condición se recuperan en una consulta. Si la condición es falsa o si no se sabe, la fila no se
recupera.
Si incluimos "No sé" en las tablas de verdad, se verán como las de la Figura 2-7 . Para un Y
operación, si una expresión es falsa O , entonces no importan los demás, el resultado será False. Por un
operación, si una expresión es verdadera , entonces no importa los demás, por lo que el resultado será True .

24
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

expresión 1 expresión 1 Expresión


T F ? T F ? ¿TF?

TT F ? TT T T F T ?

F F F F F T F ?

?? F ? ?T ? ?

a) Y b) O c) NO

Figura 2-7. Tablas de verdad con lógica de tres valores (V = Verdadero, F = Falso, ? = No sabe)

Gestión de duplicados
Si nuestras tablas han sido bien diseñadas, tendrán una clave primaria. Esto asegura que cada fila sea única.
Sin embargo, tan pronto como recuperamos un subconjunto de datos de las tablas, es posible que el resultado no tenga filas únicas.3
Veamos un ejemplo.
Considere recuperar solo la columna FirstName de la tabla de miembros. La Figura 2-8 muestra dos resultados posibles.

3
Formalmente, en términos de álgebra relacional, el resultado de cada operación generará otra relación o conjunto de filas únicas.
Consulte el Apéndice 2 para obtener más información.

25
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Figura 2-8. Proyectar la columna FirstName de la tabla de miembros

Es útil pensar por qué podríamos realizar una consulta recuperando solo nombres. Tal vez la consulta sea para
ayudar a preparar un juego de etiquetas para una fiesta en un club. Si ese es el caso, entonces dos Thomas y un William
se sentirán excluidos si usamos la salida única.
Podrías pensar, ¿a qué viene tanto alboroto? Por supuesto que queremos mantener todas las filas. Sin
embargo, considere recuperar solo la columna con los tipos de membresía. La Figura 2-9 muestra las salidas con
duplicados incluidos y eliminados.

26
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Figura 2-9. Proyectar la columna MemberType de la tabla de miembros

Es bastante difícil pensar en una situación en la que desee las filas duplicadas en la Figura 2-9a . Los dos
Las operaciones que hemos considerado suenan similares en el lenguaje natural. "Dame una lista de nombres" y "Dame
una lista de tipos de membresía" suenan como el mismo tipo de pregunta, pero significan cosas muy diferentes. El primero
significa "Dame un nombre para cada miembro" y el otro significa "Dame una lista de tipos de miembros únicos".

¿Qué hace SQL? Si decimos SELECT MemberType FROM Member , obtendremos el resultado en la Figura 2-9a
con todos los duplicados incluidos. Si no queremos los duplicados, entonces podemos usar la palabra clave DISTINCT:

SELECCIONE DISTINCT m.MemberType


FROM Miembro m

Que conserve o no los duplicados depende en gran medida de la información que necesite, por lo que
hay que pensarlo detenidamente. Si esperaba el conjunto de filas de la Figura 2-9b y obtuvo la Figura 2-9a , lo más probable
, es cometido
es que se dé cuenta. Con los dos conjuntos de filas de la figura 2-8 quizás haya mucho másundifícil
error.detectar
Adquieraque
el hábito
usted de
pensar en duplicados para todas sus consultas.

27
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Pedido de salida
De vez en cuando me refiero a un "conjunto de filas" en lugar de una tabla o una tabla virtual. La palabra conjunto tiene dos
implicaciones. Una es que no hay duplicados (¡y lo hemos discutido mucho!). La otra implicación es que no hay un orden particular en
las filas de nuestro conjunto. En teoría, no tenemos una primera fila o una última fila o una fila siguiente. Si ejecutamos una consulta
para recuperar todas las filas, o solo algunas filas, de una tabla, entonces no tenemos garantía en qué orden se devolverán. Sin
embargo, a veces nos gustaría mostrar los resultados en un orden particular. Podemos hacer esto con la frase clave ORDER BY . A
continuación se muestra cómo recuperar la información de los miembros ordenada alfabéticamente por apellido:

SELECCIONE *

DE Miembro m
ORDENAR POR m.Apellido

Podemos ordenar por dos o más valores. Por ejemplo, si queremos ordenar miembros Senior con el mismo podemos incluir esos
LastName por el valor de su FirstName , dos atributos (en ese orden) en el
Cláusula ORDER BY:

SELECCIONE *

DE Miembro m
DONDE m.MemberType = 'Senior'
ORDENAR POR m.Apellido, m.Nombre

El tipo de un campo determina cómo se ordenarán los valores. Por defecto, los campos de texto estarán ordenados
alfabéticamente, los campos numéricos se ordenarán numéricamente (el más pequeño primero) y los campos de fecha y
hora cronológicamente (fechas y horas anteriores primero). También podemos especificar que se invierta el orden con la palabra
clave DESC (para descender). Hay una palabra clave equivalente ASC (para ascender), que es la predeterminada si no se especifica
ninguna. Lo siguiente devolverá los nombres de los miembros y los handicaps ordenados en orden descendente; es decir, con el valor
más alto de handicap primero:

SELECCIONE m.Apellido, m.Nombre, m.Handicap DE Miembro


m
ORDENAR POR m.Handicap DESC

La forma en que se ordenan los valores nulos en cualquier salida depende de la aplicación; tendrás que comprobar. Por ejemplo,
en SQL Server y Microsoft Access, los valores nulos aparecerán en la parte superior de una lista ascendente y en la parte inferior
de una lista descendente. Oracle proporciona palabras clave como NULLS FIRST y NULLS LAST para que pueda elegir dónde van los
valores nulos. Un pequeño truco para colocar los valores nulos al final de una lista ascendente en SQL Server es usar un
declaración del caso:

SELECCIONE m.Apellido, m.Nombre, m.Handicap DE Miembro


m
ORDENAR POR (CASO

CUANDO m.Handicap ES NULO ENTONCES 1 DE LO


CONTRARIO 0

FIN), m.Hándicap

La consulta anterior tiene dos atributos en la cláusula ORDER BY. Ordena en primer lugar por el enunciado del caso entre paréntesis.
Puede pensar en la declaración del caso como la creación de una columna virtual que da el valor 0 a aquellas filas con un hándicap y 1 a
aquellas que no tienen ningún valor de hándicap. Cuando ordenamos por este primer atributo en la cláusula ORDER BY, las filas con un
valor para una desventaja estarán antes de los valores nulos. Dentro de estos grupos, las filas se ordenarán por el valor del hándicap en
orden ascendente.

28
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Realización de conteos simples


Además de recuperar un subconjunto de filas y columnas de una tabla, también podemos usar consultas SQL para proporcionar algunas
estadísticas. Existen funciones SQL que nos permiten contar registros, valores totales o promedio, encontrar valores máximos y mínimos,
etc. En esta sección, veremos algunas consultas simples para contar registros. Volveremos sobre este tema en el capítulo 8.

Podemos usar la función COUNT para devolver el número de registros en la tabla de miembros. En el siguiente
* significa contar cada registro:
consulta,

SELECCIONE CONTEO(*) DE Miembro

También podemos contar un subconjunto de filas agregando una cláusula WHERE para especificar las filas que queremos incluir.
Por ejemplo, podemos usar la siguiente consulta para contar el número de miembros senior:

SELECCIONE COUNT(*) FROM Miembro


m DONDE m.MemberType = 'Senior'

Como acabamos de hablar de nulos y valores duplicados, vale la pena mencionar aquí brevemente como un parámetro a la función
cómo estos afectarán nuestros conteos. En lugar de usar * COUNT para que cuente todas las filas, podemos poner un atributo
como Handicap entre paréntesis. Si hacemos esto, solo se incluirán en el recuento aquellas filas con un valor en el campo Hándicap.

SELECCIONE CONTEO (Hándicap) DE Miembro

También podemos especificar que queremos contar el número de valores únicos para un atributo. Si queremos saber cuántos
valores diferentes de MemberType aparecen en la tabla de miembros, podemos usar lo siguiente
consulta:

SELECCIONE COUNT(DISTINCT MemberType) FROM Miembro

Vale la pena reiterar que diferentes programas de base de datos admitirán diferentes partes de la sintaxis estándar de SQL. Por
ejemplo, Microsoft Access actualmente no admite COUNT(DISTINCT MemberType) , visto en la consulta anterior. Por lo general,
una manera
hay
de evitar estas diferencias para encontrar una consulta equivalente, y veremos cómo reformular la consulta anterior y otros problemas
relacionados con agregados y resúmenes en el Capítulo 8.

Evitar errores comunes


Recuperar un subconjunto de filas y columnas de una sola tabla es la consulta SQL más simple. Sin embargo, has visto que todavía
necesitas tener cuidado. Es importante recordar que habrá valores nulos en sus tablas y pensar detenidamente en cómo los tratarán sus
condiciones de selección. También debe recordar que si no retiene los campos de clave principal de sus tablas, existe la posibilidad de
tener filas duplicadas y debe tratarlas de manera adecuada.

Hay un par de otros errores que se cometen comúnmente al seleccionar un subconjunto de filas. Así que les presentaré algunas
no se haga evidente con una tabla como la base de datos de , más de las mesas en el club de golf.
miembros. La Figura 2-10 muestra parte de la tabla de Miembros y otras dos tablas: Entrada y Torneo. La primera fila de la tabla
Entrada registra que la persona 118 (Melissa McKenzie) ingresó al torneo 24 (Leeston) en 2014.

29
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Figura 2-10. Presentamos las mesas de Torneo y Entrada

Podemos usar algunas de las operaciones SQL que ya hemos visto en la tabla de entrada para responder preguntas
como en qué torneos (solo el número de TourID) ha ingresado la persona 258, quién (solo el número de ID de miembro)
number) ha ingresado alguna vez al torneo 24, o que ingresó al torneo 36 en 2015. El siguiente es el SQL para la última
consulta:

SELECCIONE
e.MemberID FROM
Entrada e DONDE e.TourID = 36 Y e.Year = 2015

Uso incorrecto de una cláusula WHERE para responder preguntas con el


Palabra "ambos"

En la sección anterior, usamos el operador lógico AND para encontrar filas en la tabla Entry donde TourID = 36 y Year = 2015
eran verdaderos.

30
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Digamos que queríamos encontrar a los miembros que participaron en los dos torneos 36 y 38. Hay un
tentación de usar nuevamente el operador AND y escribir la consulta de la siguiente manera:

SELECCIONE
e.MemberID FROM
Entrada e DONDE e.TourID = 36 Y e.TourID= 38

¿Puedes averiguar qué devolverá esta consulta? Aquí es donde es útil pensar en términos de la fila
variable e investigando cada fila en la entrada de la tabla como en la Figura 2-11.

Figura 2-11. La variable de fila e investiga cada fila de forma independiente.

Imagina que nuestro dedo apunta a la fila que se muestra en el diagrama. ¿Esta fila (415, 36, 2015) cumple la condición
e.TourID = 36 AND e.TourID= 38 ? Satisface la primera parte, pero el operador AND requiere que la fila satisfaga ambas condiciones.
Ninguna fila en nuestra tabla tendrá tanto 36 como 38 en la columna del torneo porque cada fila es para una sola entrada. La
consulta SQL que sugerimos nunca encontrará ninguna fila; siempre devolverá una tabla vacía. Si cambiamos el operador booleano
, obtendremos
a OR en la Figura 2-10 devuelto; sin embargo, también obtendremos a cualquiera que haya ingresado 36 o 38,
la pero
fila indicada
no
necesariamente ambos.

Esta consulta en particular no se puede resolver con una simple cláusula WHERE. Por definición, la condición en WHERE se
aplica a cada fila de forma independiente . Para responder a la pregunta sobre quién se ha inscrito en ambos concursos, debemos mirar
más de una fila de la tabla de Inscripción al mismo tiempo (es decir, dos dedos). Si tenemos dos dedos, uno apuntando a la fila que se
muestra en la Figura 2-10 y otro apuntando a la fila siguiente, podemos deducir que 415 ha entrado en ambos torneos. Veremos cómo
hacer esto en el Capítulo 5.

31
Machine Translated by Google

CAPÍTULO 2 ÿ CONSULTAS SIMPLES EN UNA MESA

Uso incorrecto de una cláusula WHERE para responder preguntas con el


palabra "no"
Ahora consideremos otro error común. Es fácil encontrar a las personas que han ingresado al torneo 38 con la condición e.TourID =
38 . Es tentador tratar de recuperar a las personas que no han ingresado al torneo 38 cambiando ligeramente la condición. ¿Puedes
averiguar qué filas recuperará la siguiente consulta SQL?

SELECCIONE ID de miembro electrónico

DESDE Entrada
e DONDE e.TourID <> 38

¿Qué pasa con la fila a la que apunta el dedo en la figura 2-11 ? ¿Esto satisface e.TourID <> 38? Ciertamente lo hace. Pero
esto no significa que 415 no haya entrado en el torneo 38 (la siguiente fila dice que sí). La consulta, de hecho, devuelve a todas las
personas que han ingresado a algún torneo que no es el torneo 38 (¡lo cual es poco probable que sea una pregunta que quieras
hacer!).
Este es otro tipo de pregunta que no se puede responder con una simple cláusula WHERE que busca filas
independientes en una tabla. De hecho, ni siquiera podemos responder a esta pregunta con una consulta que involucre solo la tabla
Entrada. El miembro 138, Michael Stone, no ha entrado en el torneo 38, pero ni siquiera aparece en la tabla de entradas porque
nunca ha entrado en ningún torneo. Veremos cómo lidiar con preguntas como esta en el Capítulo 7.

Resumen
En este capítulo, hemos visto consultas en una sola tabla. Algunos de los puntos principales tratados son:

• Podemos devolver un subconjunto de filas que satisfagan una condición dada usando una cláusula WHERE.
La condición es una expresión booleana, que es una afirmación que es verdadera o falsa. La condición
se aplica a cada fila de la tabla de forma independiente.

• La cláusula SELECT nos permite especificar un subconjunto de columnas.

• Debido a que el resultado de una consulta es un conjunto de filas, no podemos garantizar el orden en que se
devolverán las filas. Si queremos mostrar el resultado en un orden particular, podemos usar la cláusula
ORDER BY.

• Es posible crear una vista, que esencialmente almacena un comando SQL para que pueda ejecutarlo
una y otra vez a medida que cambian los datos en las tablas base.

• Es probable que las tablas tengan valores nulos (tanto a propósito como por error). Siempre revisa
cómo se aplicarán sus condiciones a los valores nulos.

• Cuando proyecta un subconjunto de columnas utilizando un comando SQL, el valor predeterminado es


retener filas duplicadas en el resultado. Piense siempre en cómo debe lidiar con los duplicados y use la
palabra clave DISTINCT si desea filas únicas.

• La cláusula WHERE considera solo una fila a la vez. No lo use para consultas que
requieren que mires varias filas a la vez, como quién ingresó a ambos torneos o quién no ingresó a
este torneo.

32
Machine Translated by Google

CAPÍTULO 3

Un primer vistazo a las uniones

En el capítulo anterior, vimos cómo recuperar subconjuntos de filas y/o columnas de una sola tabla.
Vimos en el Capítulo 1 que para mantener los datos con precisión en una base de datos, los diferentes aspectos de la
información deben separarse en tablas normalizadas. La mayoría de las consultas requerirán información de dos o más tablas.
Podemos combinar datos de dos tablas de varias maneras diferentes dependiendo de la naturaleza de la información que estamos
tratando de extraer. La operación de dos tablas que se encuentra con más frecuencia es la unión. En el Capítulo 1 también
presentamos dos formas diferentes de abordar una consulta: el enfoque de proceso y el enfoque de resultado . El primero describe
cómo combinaremos las tablas para lograr los datos requeridos, mientras que el segundo describe qué criterios deben cumplir los
datos recuperados.

El enfoque de proceso para las uniones


Una combinación nos permite combinar datos relacionados de dos tablas. El ejemplo con el que comenzaremos usa el Miembro
y Escriba tablas para encontrar las cuotas de membresía para cada miembro del club de golf. El primer paso para realizar
una unión es una operación denominada producto cartesiano.

Producto cartesiano
Un producto cartesiano es la operación más versátil entre dos tablas porque se puede aplicar a dos tablas cualesquiera de
cualquier forma. Habiendo dicho eso, rara vez produce información particularmente útil por sí solo, por lo que su principal reclamo
a la fama es como el primer paso de una unión.
Un producto cartesiano es un poco como poner dos mesas una al lado de la otra. Echemos un vistazo a las dos tablas
de la Figura 3-1 : una tabla Miembro abreviada y la tabla Tipo.

© Clare Churcher 2016 33


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_3
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-1. Dos tablas permanentes en la base de datos.

La tabla virtual resultante del producto cartesiano tendrá una columna por cada columna de las dos tablas
contribuyentes. Las filas de la tabla resultante constan de todas las combinaciones de filas de las tablas originales. La
figura 3-2 muestra las primeras filas del producto cartesiano.

Figura 3-2. Primeras filas del producto cartesiano entre las tablas Miembro y Tipo

34
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Tenemos las cuatro columnas de la tabla Miembro y las dos columnas de la tabla Tipo, lo que nos da un total de seis columnas.
Cada fila de la tabla Miembro aparece en la tabla resultante junto a cada fila de la tabla Tipo. Tenemos a Melissa McKenzie apareciendo
en cuatro filas, una vez con cada una de las cuatro filas en la tabla Tipo (Asociado, Junior, Senior, Social). El número total de filas será
el número de filas de cada tabla multiplicado entre sí; en otras palabras, para esta tabla de Miembros recortada, tenemos 10 filas por 4
filas (del Tipo), dando un total de 40 filas. Los productos cartesianos pueden producir tablas de resultados muy, muy grandes, por lo que
no nos brindan mucha información útil por sí mismos.

Una operación de producto cartesiano se representa en SQL por CROSS JOIN. El SQL para recuperar los datos que se muestran
en la Figura 3-2 es:

SELECCIONE *

FROM Miembro m CROSS JOIN Tipo t;

No todas las versiones de SQL admiten las mismas palabras clave y frases (por ejemplo, Microsoft Access 2013 no admite la
frase clave CROSS JOIN). En 1992, se agregaron al estándar SQL palabras clave que representan algunas operaciones de álgebra
relacional (como CROSS JOIN ) y ha habido una serie de actualizaciones desdetodas
incorporan entonces . Sin embargo,
las partes no todos
del estándar losproveedores
y otros proveedores
brindan funcionalidad adicional. Más adelante en el capítulo veremos el enfoque de resultados para proporcionar formas equivalentes
de expresar consultas que funcionarán cuando las palabras clave de la operación del álgebra relacional no estén disponibles.

Unir internamente

Si observa la tabla de la Figura 3-2 , puede ver que la mayoría de las filas carecen de sentido. Por ejemplo, las filas primera, tercera
y cuarta tienen a la miembro junior Melissa McKenzie junto con información sobre los tipos de membresía asociada, senior y social.
Es difícil ver cómo estas filas alguna vez serán útiles.
Sin embargo, la segunda fila, donde los tipos de miembros de cada tabla coinciden, es útil porque nos permite ver qué tarifa paga
Melissa. Si tomamos solo el subconjunto de filas donde el valor en la columna MemberType coincide con el valor en la columna
Type, entonces tenemos información útil sobre las tarifas de cada uno de nuestros miembros. La figura 3-3 muestra las filas que
nos gustaría conservar.

1 Organización Internacional de Normalización. Tecnología de la información — Lenguajes de base de datos — SQL . ISO,
Ginebra, Suiza, 1992. ISO/IEC 9075:1992.

35
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-3. Producto cartesiano seguido de la selección de un subconjunto de filas

La operación que se muestra en la figura 3-3 (un producto cartesiano seguido de la selección de un subconjunto de filas) se
conoce como unión interna (a menudo llamada simplemente unión ). La condición que usamos para seleccionar las filas se conoce como
condición de combinación . El SQL para la unión interna en la Figura 3-3 es:

SELECT *

FROM Miembro m INNER JOIN Escriba t ON m.MemberType = t.Type;

Se usa la palabra clave INNER JOIN, y podemos ver la condición para seleccionar las filas después de la palabra clave. Una vez más,
EN es posible que algunas versiones de SQL no admitan la frase INNER JOIN; sin embargo, veremos otras formas de expresar la consulta
más adelante en este capítulo.
Las dos columnas que estamos comparando (MemberType y Type) deben ser compatibles con las uniones . Formalmente, esto
significa que ambos deben provenir del mismo dominio o conjunto de valores posibles. En términos prácticos, la compatibilidad de
combinación generalmente significa que las columnas en cada una de las tablas tienen el mismo tipo de datos. Por ejemplo, ambas
columnas serán números enteros o ambas fechas. Diferentes productos de base de datos pueden interpretar la compatibilidad de
combinación de manera diferente. Algunos pueden permitirle unirse a un flotante (número con un punto decimal) en una tabla y un número
entero en otra. Algunos pueden ser quisquillosos acerca de si los campos de texto tienen la misma longitud (por ejemplo, CHAR(10) o
CHAR(15) ), y otros no. Le recomiendo que no intente unirse a campos con diferentes tipos a menos que tenga muy claro lo que hace su
producto en particular. La mejor estrategia, como siempre, es pensar cuidadosamente cuando diseñes tus tablas. Los atributos que es
probable que se unan deben tener los mismos tipos.

Enfoque de resultado para uniones


Echemos un vistazo a las uniones con el enfoque de resultado. En lugar de ver cómo combinaremos las tablas, veremos qué criterios deben
cumplir las filas recuperadas.

36
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Comencemos con el producto cartesiano: queremos un conjunto de filas formado por combinaciones de filas de cada
de las tablas contribuyentes. La figura 3-4 muestra cómo podemos concebir esto. Estamos mirando dos tablas, por lo que necesitamos
dos dedos para realizar un seguimiento de las filas. El dedo m mira cada fila de la tabla de miembros por turno. Actualmente apunta a la
fila 3. Para cada fila de la tabla Miembro, el dedo t apuntará a cada fila de la tabla Tipo. Para el producto cartesiano conservamos todas
las combinaciones de las filas. En términos de la Figura 3-4 , el producto cartesiano se puede expresar en lenguaje natural como:

Escribiré todos los atributos de la fila m y todos los atributos de la fila t siempre que m
proviene de la tabla Miembro y t proviene de la tabla Tipo .

Figura 3-4. Las variables de fila m y t apuntan a cada fila en las tablas Miembro y Tipos, respectivamente

El SQL para la consulta representada en la Figura 3-4 y que da como resultado el resultado que se muestra en la Figura 3-2 es:

SELECCIONE *

DESDE Miembro m, Tipo t;

La declaración anterior devolverá las mismas filas que la expresión que habíamos usado anteriormente que usaba la frase CROSS
JOIN.
Para una combinación, tenemos la condición adicional de que queremos recuperar solo aquellas combinaciones de filas donde
el tipo de membresía de cada tabla es el mismo. Esto lo podemos expresar en lenguaje natural como:

Escribiré todos los atributos de la fila m y todos los atributos de la fila t siempre que m
proviene de la tabla Member y t proviene de la tabla Type y m.MemberType = t.Type .

El par de filas representadas en la figura 3-5 satisface esa condición y, por lo tanto, se recuperará. Si m permanece donde está y t
baja una fila, la condición ya no se cumplirá y la nueva combinación no se incluirá.

37
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-5. Las filas se recuperarán donde m.MemberType = t.Type

Podemos traducir la consulta representada en la Figura 3-5 a SQL de la siguiente manera:

SELECCIONE *

DESDE Miembro m, Escriba t


DONDE m.MemberType = t.Type;

Si observamos detenidamente la declaración anterior, podemos ver que las dos primeras líneas representan el cartesiano
product, y la cláusula WHERE en la última línea está seleccionando un subconjunto de las filas donde los tipos de membresía son
los mismos. Así fue como definimos una unión interna en la sección anterior. La declaración anterior producirá las mismas filas que
nuestra declaración anterior para una combinación interna, que se ve nuevamente aquí:

SELECCIONE *

FROM Miembro m INNER JOIN Tipo t ON m.MemberType = t.Type;

La primera declaración dice cómo son las filas que se recuperarán (enfoque de resultado) y la segunda expresa qué operación
debemos usar para recuperar esas filas (enfoque de proceso). No importa cuál use, solo depende de cómo se encuentre pensando en la
consulta. A veces existe la posibilidad de que la forma en que expresa la consulta pueda afectar el rendimiento, y hablaremos más sobre
esto en el Capítulo 9.
En realidad, la mayoría de los productos de base de datos son bastante inteligentes para optimizar o encontrar la forma más rápida de
realizar una consulta, independientemente de cómo la exprese. Por ejemplo, en SQL Server, las dos expresiones para la combinación
que se muestran se realizan de la misma manera. De hecho, en SQL Server 2013, si escribe el código en la primera declaración en la
interfaz predeterminada para crear una vista, será reemplazado por el código que usa la frase INNER JOIN.

Ampliación de consultas de combinación

Ahora que hemos agregado uniones a nuestro arsenal de operaciones, podemos realizar numerosos tipos de consultas.
Debido a que el resultado de una combinación (como con cualquier operación) es otra tabla, podemos unir ese resultado a una tercera
tabla (y luego a otra) y luego seleccionar y proyectar filas y columnas para lograr el resultado requerido.
Veamos un ejemplo usando las tablas en la Figura 3-6 . La tabla Entry utiliza dos claves foráneas ( MemberID
y TourID) para mantener información sobre qué miembros han ingresado a los diferentes torneos. La primera línea en la tabla Entrada
dice que el miembro 118 ingresó al torneo 24 en 2014. Si necesitamos información adicional (por ejemplo, el nombre de un miembro o el
nombre de un torneo), necesitamos usar las claves externas para encontrar las filas apropiadas. en las tablas de Miembros y Torneos,
respectivamente.

38
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-6. Tablas permanentes en la base de datos del club

Busquemos los nombres de todos los que participaron en el torneo de Leeston en 2014. Describiré dos
diferentes enfoques, y probablemente encontrará que uno le atrae más que el otro.

Un enfoque basado en procesos

Comenzamos con tres tablas, por lo que necesitamos alguna operación que combine datos de más de una tabla.
Podemos unir la tabla de Miembros a la tabla de Entrada y el resultado a la tabla de Torneo, como se muestra en la Figura 3-7 .

39
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-7. Unirse a las mesas de miembros, entradas y torneos

La condición de unión para la primera unión entre las tablas Miembro y Entrada es que m.MemberID = e.MemberID
como se muestra en las cajas rectangulares de la Figura 3-7 . Para la segunda combinación entre el resultado de
la primera combinación y la tabla del Torneo, la condición es que e.TourID = t.TourID como se muestra en los círculos.
No habrá ninguna diferencia si elegimos unir primero la Entrada y el Torneo y luego unir el resultado al Miembro. .
El SQL para realizar los dos joins es:

SELECT
* FROM (Miembro m INNER JOIN Entrada e ON m.MemberID = e.MemberID)
INNER JOIN Torneo t ON e.TourID = t.TourID;

40
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

La tabla virtual resultante de las dos uniones en esta consulta tiene toda la información que necesitamos para responder
a nuestra pregunta. Solo necesitamos seleccionar las filas que cumplan las condiciones sobre el año y el nombre del torneo
agregando una cláusula WHERE, y luego proyectar los atributos del nombre especificándolos en la cláusula SELECT. La
consulta SQL completa para devolver los nombres de todos los que participaron en el torneo de Leeston en 2014 es:

SELECT LastName, FirstName


FROM (Miembro m INNER JOIN Entrada e ON m.MemberID = e.MemberID)
INNER JOIN Torneo t ON e.TourID = t.TourID WHERE
TourName = 'Leeston'
Y Año = 2014;

Orden de las operaciones En la

descripción de la sección anterior, unimos todas las tablas primero y luego seleccionamos las filas y columnas apropiadas.
El resultado de la unión es una mesa intermedia (como en la Figura 3-7 ) que es potencialmente extremadamente grande
si hay muchos miembros y torneos. Podríamos haber hecho las operaciones en un orden diferente. Podríamos haber
seleccionado primero solo el torneo de Leeston de la tabla de Torneos y los torneos de 2014 de las tablas de Entrada, como
se muestra en la Figura 3-8 . Unir estas dos tablas más pequeñas entre sí y luego unir ese resultado con Member daría como
resultado una tabla intermedia mucho más pequeña.

Figura 3-8. Seleccionar filas de las tablas de Entrada y Torneo antes de unirse a ellas

Entonces, ¿debemos preocuparnos por el orden de las operaciones? La respuesta es "sí": el orden de las operaciones
hace una gran diferencia, pero si está utilizando SQL, entonces no es su problema de qué preocuparse. La instrucción
SQL siempre será la misma, pero con las tablas posiblemente en un orden diferente. La instrucción SQL se envía al
motor de cualquier programa de base de datos que esté utilizando y la consulta se optimizará. Esto significa que el
programa de la base de datos determina el mejor orden para hacer las cosas. Algunos productos hacen esto extremadamente
bien, otros no tan bien. Muchos productos tienen herramientas de análisis que le permitirán ver en qué orden se están haciendo
las cosas. Para muchas consultas, escribir su SQL de manera diferente no hace mucha diferencia, pero puede hacer que las
cosas sean más eficientes al proporcionar índices para sus tablas. Veremos estos temas más de cerca en el Capítulo 9.

41
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Un enfoque de resultados
La razón por la que la forma en que escribimos nuestras declaraciones SQL a menudo no afecta la eficiencia de una consulta es
que SQL se basa fundamentalmente en el cálculo relacional, que describe los criterios que deben cumplir las filas recuperadas.
Los estándares SQL originales ni siquiera tenían palabras clave como INNER JOIN.
claveLas
describen
declaraciones
cómo deberían
SQL sin estas
ser laspalabras
filas
recuperadas, por lo que no tienen nada que decir sobre cómo. Veamos un enfoque de resultados para encontrar los nombres de
los miembros que ingresaron a los torneos de Leeston en 2014.
Queremos recuperar solo algunos nombres de la tabla de miembros. Olvídese de las uniones y piense en cómo sabría
si un nombre en particular debe recuperarse si le mostraran las tres tablas y no supiera nada sobre bases de datos, claves
externas, uniones ni nada. Imagine un dedo m recorriendo la mesa, como en la figura 3-9 .

Figura 3-9. Usar variables de fila para describir las filas que satisfacen las condiciones de la consulta

42
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

¿Queremos escribir Barbara Olson, el nombre al que apunta actualmente m? ¿Cómo lo sabríamos? Bueno,
primero tenemos que encontrar una fila con su ID (235) en la tabla Entrada para el año 2014 como la que señala el dedo e.
Luego tenemos que encontrar una fila con ese ID de torneo (24) en la tabla de torneos y verificar que sea un torneo de
Leeston. Mirar la Figura 3-9 nos da suficiente información para saber ,que Barbara
vemos Olson
que las filassídonde
participó en los
están un tres
torneo de Leeston
dedos
en 2014. Este conjunto de condiciones describe cómo debería ser una fila en la tabla de resultados.

Ahora escribamos ese último párrafo de manera un poco más sucinta. Lea la siguiente oración con referencia a las filas
indicadas en la Figura 3-9 :

Escribiré los nombres de la fila m , donde m proviene de la tabla de Miembros , si hay una
fila e en la tabla de Entrada donde m.MemberID es el mismo que e.MemberID y e.Year es
2014 y también existe una fila t en la tabla de Torneos donde e.TourID es lo mismo que
t.TourId y t.TourName tiene el valor "Leeston".

El SQL refleja el párrafo anterior. Mire cuidadosamente la siguiente declaración con referencia
a la Figura 3-9 :

SELECCIONE m.Apellido, m.Nombre DE


Miembro m, Entrada e, Torneo t DONDE
m.MemberID = e.MemberID
AND e.TourID = t.TourID AND
t.TourName = 'Leeston' AND e.Year = 2014;

Puede ver cómo la instrucción SQL describe cómo debería ser una fila recuperada. Si miras con cuidado
en el estado de cuenta, también puede detectar las operaciones. La segunda línea (la cláusula FROM) es un gran
producto cartesiano, las siguientes dos líneas son las condiciones de unión (que darían como resultado una tabla como la de
la Figura 3-7 ), la línea final selecciona las filas con el año apropiado y nombre del torneo, y la línea de la cláusula SELECT
nos dice que proyectemos solo los nombres.
La declaración anterior de SQL es equivalente a la que usa las palabras clave INNER JOIN. Ambos serán y el otro
devuelve el mismo conjunto de filas: uno refleja el proceso subyacente de cómo , refleja el subyacente
resultado de qué.

Expresar uniones a través de interfaces esquemáticas Este libro trata sobre consultas en

SQL, pero la mayoría de los productos de bases de datos también proporcionan una interfaz esquemática para expresar
consultas. Solo para completar, le mostraré cómo se ve una interfaz esquemática típica para recuperar los nombres de
los miembros que ingresaron al torneo de Leeston en 2014.
La Figura 3-10 muestra la interfaz de Microsoft Access, pero la mayoría de los productos tienen algo muy similar. Las
tablas están representadas por los rectángulos en la sección superior con las líneas que muestran las uniones entre ellos.
Las columnas a recuperar tienen una marca de verificación (ÿ) en la fila marcada Mostrar, y las condiciones para seleccionar
una fila en particular se muestran para los campos relevantes en la fila marcada Criterios.

43
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-10. Interfaz digramatica de Microsoft Access para la consulta para encontrar los nombres de los miembros que ingresaron
al torneo de Leeston en 2014

Otros tipos de uniones


Las uniones que hemos estado viendo en este capítulo son uniones igualitarias . Una unión equitativa es aquella en la que la condición de
unión tiene un operador igual, como en m.MemberID = e.MemberID . Este es el tipo de
operadores. condición
Una más común,
combinación es solo pero puede tener
un producto diferentes
cartesiano
seguido de la selección de un subconjunto de filas, y la condición de selección puede constar de diferentes operadores de comparación (por
ejemplo, <> o >) y también de operadores lógicos (por ejemplo, Y o NO). Este tipo de uniones no aparecen tan a menudo.

También puede encontrarse con una unión natural . Una unión natural asume que se unirá en columnas que tienen el mismo
nombre en ambas tablas. La condición de combinación es que los valores de las dos columnas con el mismo nombre sean iguales y una
de esas columnas se eliminará del resultado. Por ejemplo:

SELECCIONAR DE
Miembro NATURAL JOIN Entrada;

Esto produciría casi el mismo resultado que:

SELECT * FROM
Miembro m INNER JOIN Entrada m ON m.MemberID = e.MemberID;

En la declaración de unión natural, se supone implícitamente que la condición de unión es la igualdad entre los dos atributos con el
mismo nombre, MemberID. La única diferencia
columnasentre las dos consultas
de MemberID. Oracle es que para
admite la combinación
uniones natural
naturales, pero SQLsolo se devolverá
Server una de las
y Access no.

44
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Uniones externas

Un tipo de combinación que utilizará mucho y que es importante comprender es la combinación externa . La mejor
manera de entender una combinación externa es ver dónde son útiles. Eche un vistazo a las tablas Member y Type
(modificadas) en la Figura 3-11 .

Figura 3-11. Tablas de miembros y tipos

Es posible que desee producir diferentes listas de la tabla de miembros, como números y nombres, nombres y
tipos de miembros, etc. En estas listas, espera ver todos los miembros (para la tabla de la Figura 3-11 , serían nueve ,
filas). Entonces podrías pensar que además de ver los números y nombres en tu lista de miembros, también incluirás
la cuota de membresía. Une las dos tablas (con la condición MemberType = Type) y descubre que "pierde" a uno de
sus miembros: Sarah Beck (vea la figura 3-12 ).

Figura 3-12. Unión interna entre miembro y tipo, y "perdemos" a Sarah Beck

El motivo es que Sarah no tiene ningún valor para MemberType en la tabla de miembros. Veamos el producto
cartesiano, que es el primer paso para hacer una unión. La figura 3-13 muestra las filas del producto cartesiano que
incluyen a Sarah.

45
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-13. Parte del producto cartesiano entre las tablas Miembro y Tipo

Habiendo hecho el producto cartesiano, ahora tenemos que hacer la parte final de nuestra operación de combinación, que es
aplicar la condición ( MemberType = Type ). Como puede ver en la Figura 3-13 , , que no hay ninguna fila para Sarah Beck que
cumple esta condición porque tiene un valor nulo o vacío en MemberType.
Considere las siguientes dos preguntas en lenguaje natural: "Obtenga las tarifas para los miembros" y "Obtenga toda la
información de los miembros, incluidas las tarifas". El primero tiene una implicación de "Solo consígueme los miembros que
tienen tarifas", mientras que el segundo tiene más una sensación de "Consígueme a todos los miembros e incluye las tarifas
para aquellos que las tienen". Una de las mayores dificultades al escribir consultas es tratar de decidir exactamente qué es lo
que quieres. ¡Es aún más difícil si estás tratando de entender lo que alguien más está pidiendo!
Digamos que lo que realmente queremos es una lista de todos nuestros miembros, y donde podemos encontrar la
información de tarifas, nos gustaría incluir eso. En este caso, queremos ver a Sarah Beck incluida en el resultado, pero sin que se muestre
ninguna tarifa. Eso es lo que hace una combinación externa. Las combinaciones externas pueden ser de tres tipos: combinaciones
izquierdas,
externas
derechas y completas. Una combinación externa izquierda recupera todas las filas de la tabla de la izquierda, incluidas aquellas con un valor
nulo en el campo de combinación, como se muestra en la Figura 3-14 . Vemos que, además de todas las filas de la combinación interna
(Figura 3-12 ), también tenemos una fila de la tabla Miembro para Sarah, que tenía un valor nulo para el campo de combinación MemberType.
Los campos en esa fila que habrían venido de la tabla de la derecha (Tipo y Tarifa) tienen valores nulos.

46
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-14. Resultado de la combinación externa izquierda entre tablas de miembros y tipos

El SQL para la combinación externa que se muestra en la Figura 3-14 es similar a una combinación interna, pero la frase clave INNER
JOIN se reemplaza con LEFT OUTER JOIN (o en algunas aplicaciones simplemente LEFT JOIN):

SELECCIONE *

FROM Miembro m LEFT OUTER JOIN Escriba t ON m.MemberType = t.Type;

Es bastante razonable decir que no habríamos necesitado una unión externa si todos los miembros tuvieran un valor para el campo MemberType (como

probablemente deberían). Eso puede ser cierto para este caso, pero recuerde mis advertencias en el Capítulo 2 acerca de asumir que los campos que deberían
tener datos tendrán datos. En otras situaciones, los datos en el campo de combinación pueden estar legítimamente vacíos. Veremos en capítulos posteriores
consultas como "Enumere todos los miembros y los nombres de sus entrenadores, si tienen uno". "Perder" filas porque usó una unión interna cuando debería haber
usado una unión externa es un problema muy común y, a veces, es bastante difícil de detectar.

¿Qué pasa con las uniones externas derechas y completas? Las combinaciones externas izquierda y derecha son iguales y solo dependen del orden en que
coloca las tablas en la declaración de combinación. La siguiente instrucción SQL devolverá la misma información que se muestra en la Figura 3-14 , aunque las
columnas pueden presentarse en un orden diferente:

SELECCIONE *

FROM Tipo t RIGHT OUTER JOIN Miembro m ON m.MemberType = t.Type;

Simplemente hemos cambiado el orden de las tablas en la declaración de unión. Cualquier fila con un valor nulo en la combinación
Se incluirá el campo de la tabla de la derecha (Miembro).
Una combinación externa completa conservará las filas con un valor nulo en el campo de combinación en cualquiera de las tablas. El SQL para la combinación
externa completa se muestra aquí y dará como resultado la tabla que se ve en la Figura 3-15 :

SELECCIONE *

FROM Miembro m FULL OUTER JOIN Escriba t ON m.MemberType = t.Type;

47
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

Figura 3-15. Resultado de una combinación externa completa entre tablas de miembros y tipos

Tenemos una fila para Sarah Beck rellenada con valores mínimos para las columnas que faltan en la tabla Tipo. También tenemos la primera
fila, que nos muestra la información sobre el tipo de membresía Asociado aunque no hay filas en la tabla Miembro con Asociado como tipo de miembro.
En esta fila, cada valor faltante de la tabla de miembros se reemplaza por un valor nulo.

No todas las implementaciones de SQL tienen una combinación externa completa implementada explícitamente. Access 2013 no lo hace.
Sin embargo, siempre hay formas alternativas en SQL para recuperar la información que necesita. En el Capítulo 7 , le mostraré cómo obtener el
equivalente de una combinación externa completa usando un operador de unión entre una combinación externa izquierda y derecha (¡que es lo que
tuve que hacer para obtener la captura de pantalla de la Figura 3-15 !) .

Resumen
Un producto cartesiano combina dos tablas. La tabla resultante tiene una columna para cada columna de las dos tablas y hay una fila para cada
combinación de filas de las tablas contribuyentes. El SQL para un producto cartesiano que refleja el enfoque de proceso es:

SELECCIONE *

DESDE <tabla1> CROSS JOIN <tabla2>;

El SQL para una combinación interna que refleja el enfoque de resultado es:

SELECCIONE *

DESDE <tabla1>,<tabla2>;

Una combinación interna comienza con un producto cartesiano y luego una condición de combinación determina qué combinaciones de filas de
las dos tablas contribuyentes se conservarán.
El SQL para una combinación interna que refleja el enfoque del proceso es:

SELECCIONE *

DESDE <tabla1> UNIÓN INTERNA <tabla2>

ON <condición de unión>;

48
Machine Translated by Google

CAPÍTULO 3 ÿ UN PRIMER VISTAZO A LAS UNIONES

El SQL para una combinación interna que refleja el enfoque de resultado es:

SELECCIONE *...

FROM <tabla1>, <tabla2> WHERE

<condición de unión>;

Si una (o ambas) de las tablas tiene filas con un valor nulo en el campo involucrado en la condición de unión, entonces esa fila
no aparecerá en el resultado de una combinación interna. Si se requiere esa fila, puede usar uniones externas.
El SQL para una combinación externa, que conservará todas las filas de la tabla de la izquierda, incluidas aquellas con un valor nulo en el campo de combinación,
es:

SELECCIONE *

DESDE <tabla1> UNIÓN EXTERNA IZQUIERDA <tabla2>


ON <condición de unión>;

Existen expresiones similares para las uniones externas derechas y las uniones externas completas.

49
Machine Translated by Google

CAPÍTULO 4

Subconsultas

En los capítulos anteriores, analizamos la recuperación de un subconjunto de filas y columnas de una sola tabla, y también
analizamos cómo se pueden usar los productos cartesianos y las uniones para recuperar datos de dos o más tablas. En
muchos de los ejemplos fue posible construir consultas SQL bastante diferentes para producir el mismo resultado.
Según el contexto o el problema, probablemente encontrará que un enfoque le resultará más natural.
A medida que las consultas se vuelven más complicadas, podemos encontrar que podemos pensar en expresiones
SQL para pequeñas partes de una consulta, pero no para todo de una sola vez. Es posible devolver datos de una consulta y
luego hacer referencia a esos datos con otra consulta, todo en una instrucción SQL. Esta idea de una consulta dentro de una
consulta es muy poderosa. Escuchará el concepto denominado consulta y subconsulta o consultas internas y externas o
consultas anidadas.
En este capítulo, veremos las subconsultas y dos nuevas palabras clave de SQL, EXISTS e IN. Veremos cómo usar las
subconsultas como una forma alternativa de abordar algunas de las consultas que ya hemos hecho y también cómo el
anidamiento abrirá otras posibilidades.

EN palabra clave
La palabra clave IN nos permite seleccionar filas de una tabla, donde la condición permite que un atributo tenga uno de varios
valores. Por ejemplo, si quisiéramos recuperar los ID de miembros de las filas de nuestra tabla Entrada para torneos con ID
36, 38 o 40, podríamos hacerlo con un operador booleano OR como en la siguiente consulta:

SELECCIONE
e.MemberID FROM
Entrada e DONDE e.TourID = 36 O e.TourID = 38 O e.TourID = 40;

Claramente, las declaraciones de este tipo comenzarán a volverse difíciles de manejar a medida que crezca el
número de opciones posibles. Usando la palabra clave IN, podemos construir una declaración más compacta donde el
conjunto de valores posibles está encerrado entre paréntesis y separado por comas. En la siguiente consulta, se investiga
cada fila de Entry, y si TourID es uno de los valores entre paréntesis, entonces la condición WHERE es verdadera y se devolverá
esa fila:

SELECCIONE
e.MemberID FROM
Entrada e DONDE e.TourID IN (36, 38, 40);

© Clare Churcher 2016 51


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_4
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Es posible combinar IN con el operador lógico NOT. Sin embargo, debes tener mucho cuidado. Considere la siguiente
consulta:

SELECCIONE
e.MemberID FROM
Entrada e DONDE e.TourID NOT IN (36, 38, 40);

La consulta anterior devolverá los ID de los miembros que hayan ingresado a cualquier torneo que no esté en el
lista. Sin embargo, tenga en cuenta que esos miembros también pueden haber ingresado a uno de los torneos en la lista.
Más adelante en este capítulo veremos cómo responder con precisión a preguntas como "quién no ha entrado en estos
torneos".

Uso de IN con subconsultas


La utilidad real de la palabra clave IN es que podemos usar otra declaración SQL para generar el conjunto de valores.
Por ejemplo, la razón por la que alguien pudo haber estado interesado en los torneos 36, 38 y 40 podría haber sido porque
son los torneos abiertos actuales. En lugar de enumerar los torneos abiertos individualmente, podemos usar otra consulta
SQL para generar el conjunto de valores que necesitamos. La lista se reconstruirá cada vez que se ejecute la consulta para
que el conjunto de torneos abiertos permanezca actualizado a medida que cambien los datos.
Veamos un ejemplo específico del uso de una consulta para generar el conjunto de valores para la cláusula
.
IN. He reproducido algunas de las columnas de la tabla de Miembros junto con las tablas de Entrada y Torneo en la Figura 4-1

Figura 4-1. Mesas de miembros, entradas y torneos

52
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

La consulta para generar el conjunto de IDs para los torneos Open es:

SELECCIONE
t.TourID FROM Torneo
t DONDE t.TourType = 'Open';

Ahora podemos reemplazar la lista de valores explícitos (36, 38, 40) en las consultas anteriores con la instrucción SQL
anterior:

SELECCIONE
e.MemberID FROM
Entrada e WHERE
e.TourID IN
( SELECCIONE
t.TourID FROM Torneo t WHERE t.TourType = 'Open');

La declaración SELECT dentro de los paréntesis a veces se denomina subconsulta . para trabajar correctamente
con la palabra clave IN, la parte interna de la consulta debe devolver una lista de valores únicos. Lo he sangrado solo para
que sea más fácil de leer (SQL ignorará el espacio en blanco agregado). Puede comprender una consulta anidada
leyéndola desde "adentro hacia afuera". La instrucción SELECT interna recupera el conjunto de ID de torneos requeridos
de la tabla de Torneos, y luego la instrucción SELECT externa nos encuentra todas las entradas de la tabla de Entrada
para los torneos EN ese conjunto.
Para facilitar la comprensión, es posible agregar comentarios a las sentencias SQL. En la declaración que sigue, la línea
que comienza con -- es un comentario y se ignorará. También es posible usar /* y */ alrededor de un bloque de más de una
línea de código.

SELECT e.MemberID
FROM Entrada e
WHERE e.TourID IN ( --
La subconsulta devuelve ID de torneos abiertos SELECT
t.TourID FROM Torneo t WHERE t.TourType = 'Open');

Eche otro vistazo a las tablas de la Figura 4-1 . ¿De qué otra forma podríamos haber recuperado entradas para
torneos abiertos? Realizamos consultas similares en el capítulo anterior utilizando una unión. Podemos unir las dos
tablas, Entrada y Torneo, en sus campos comunes Torneos TourID, y luego , seleccione
proyectar la aquellas
solo columnafilas
MemberID. VerAbierto
que son para lo
siguiente:

SELECCIONE
e.MemberID FROM Entrada e INNER JOIN Torneo t ON e.TourID = t.TourID
WHERE t.TourType = 'Open';

Las declaraciones SQL con y sin la subconsulta recuperan la misma información. Como he dicho varias veces,
a menudo hay varias formas diferentes de escribir una consulta en SQL. Cuantos más métodos conozca, más probable
será que pueda encontrar una manera de expresar una consulta complicada.

53
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Tener cuidado con NOT y <>


Además de hacer una pregunta como "¿Cuáles son las identificaciones de los miembros que han ingresado a un torneo abierto?" es
igual de probable que queramos saber "¿Cuáles son las identificaciones de los miembros que no han ingresado a un torneo abierto?"
Suenan muy similares, pero una vez que comenzamos a usar negativos en nuestras preguntas, debemos tener mucho cuidado con
lo que realmente queremos decir. En el Capítulo 7, investigaremos la construcción de consultas usando operaciones de conjuntos,
pero para mantener este capítulo completo, hablaré sobre cómo los aspectos negativos afectan el uso de subconsultas en particular.

En la sección anterior construimos dos sentencias SQL para recuperar ID de miembros para miembros que han ingresado
a un torneo abierto. Uno usó una subconsulta y otro una unión. Para encontrar quién no ha ingresado a un torneo abierto, uno podría
intentar cambiar IN a NOT IN en el ejemplo de la subconsulta, de la siguiente manera:

SELECCIONE ID de miembro electrónico

DESDE Entrada
e DONDE e.TourID NO EN
(SELECCIONE
t.TourID DE Torneo t
DONDE t.TourType = 'Abrir');

En el ejemplo de combinación, existe la tentación de modificar t.TourType = 'Open' a t.TourType <> 'Open':

SELECCIONE ID de miembro electrónico

FROM Entrada e INNER JOIN Torneo t ON e.TourID = t.TourID WHERE t.TourType


<>'Open';

Piense detenidamente qué filas devolverán estas dos consultas. De hecho, ambos devuelven el mismo conjunto de filas,
pero esas filas pueden incluir miembros que han ingresado a un torneo abierto, así como también aquellos que no lo han hecho.

La tabla de la Figura 4-2 muestra el resultado de la unión interna entre Entrada y Torneo. El conjunto inferior de filas son
todos para torneos abiertos, y se recuperarán mediante una consulta que tenga la condición WHERE t.TourType = 'Open' . El
conjunto superior de entradas es para torneos que no sean Open y se recuperará mediante la consulta que tiene la condición WHERE
t.TourType <> 'Open' .

54
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

'
Figura 4-2. TourType = 'Abierto' versus TourType <> Abierto'

Podemos ver que algunos miembros (indicados por círculos) aparecen en ambos conjuntos. La figura
4-3 es otra representación de la información de la tabla de la figura 4-2 , pero muestra dos conjuntos de
miembros en lugar de entradas: el círculo superior representa a los que han entrado en un torneo abierto y el círculo
inferior a los que han entrado en un torneo que no es un torneo abierto. Cuatro miembros están en ambos conjuntos.

55
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Torneos abiertos

235

228 258

415 239

118 286

Otros Torneos

Figura 4-3. Miembros que han ingresado a torneos abiertos, otros torneos o ambos

Ahora volvamos a la pregunta original. ¿Qué miembros no han entrado en un torneo Open? Nosotros
hay que tener cuidado de diferenciar los dos conjuntos representados en la Figura 4-4.

Torneos abiertos Torneos abiertos

235 235

228 258 228 258

415 239 415 239

118 286 118 286

Otros Torneos Otros Torneos

una. El área sombreada es gente b. El área sombreada son las


que no han entrado en personas que ingresaron a un
torneo abierto torneo que no es un torneo abierto.

Figura 4-4. Es importante tener cuidado de distinguir el SQL para estas dos situaciones.

56
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

La Figura 4-4a muestra el conjunto de personas que no han ingresado a ningún torneo Abierto. La figura 4-4b muestra
a los miembros que han ingresado a algo que no sea un torneo abierto (pero sin excluir a aquellos que también pueden haber
ingresado a un torneo abierto). Por ejemplo, el miembro 118 nunca ha entrado en un torneo abierto, mientras que el miembro 228
ha entrado en torneos abiertos así como en otros tipos de torneos.
Las dos consultas al comienzo de esta sección recuperarán el conjunto de miembros representado en la Figura
4-4b . Se devolverá al miembro 228 (que ha entrado en un torneo abierto) (porque ha entrado en un torneo que no es un torneo
abierto). Esto no es lo que queremos y es un error muy común.
Para decidir si alguien ha ingresado a una competencia abierta, necesitamos encontrar solo una entrada que coincida.
Para decidir si alguien no ha ingresado a una competencia abierta, debemos verificar todas las entradas abiertas para asegurarnos
de que ese miembro no aparezca.
En términos de nuestras tablas unidas en la Figura4-2 , encontrar a las personas que han ingresado a un torneo abierto
requiere una cláusula WHERE simple: WHERE t.TourType = 'Open'. Recuerde que cada fila se inspecciona de forma independiente
para decidir si cumple los criterios de una cláusula WHERE. Sin embargo, para encontrar personas que no hayan ingresado a un
torneo abierto, debemos investigar cada fila de la tabla para asegurarnos de que no haya una entrada para un miembro en
particular. Esta es una tarea mucho más compleja. De hecho, también debemos considerar a los miembros que nunca han
participado en ningún torneo. Las identificaciones de estos miembros no aparecerán en la tabla de entrada, por lo que también
tenemos que investigar la tabla de miembros para encontrar la lista completa.
Se puede encontrar miembros que no hayan ingresado a un torneo abierto con un enfoque de proceso utilizando las
operaciones de conjunto que se encuentran en el Capítulo 7. Sin embargo, también podemos usar el enfoque de resultado para
construir una consulta precisa. Para hacer eso, primero debemos introducir la palabra clave EXISTS.

EXISTE Palabra clave


Comencemos con una simple pregunta. Por ejemplo, "¿Cuáles son los nombres de todos los miembros que alguna vez
participaron en un torneo?" Podemos empezar por pensar en términos de qué filas de la tabla de miembros satisfarían nuestra
pregunta. Considere la siguiente oración y la figura 4-5 juntas:

Escribiré los nombres de la fila m, donde m proviene de la tabla de Miembros , si existe una fila e en la tabla de
Entrada donde m.MemberID = e.MemberID .

57
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Figura 4-5. William Cooper ha entrado en un torneo porque existe una fila coincidente en la tabla de entrada

Podemos traducir la declaración

Escribiré los nombres de la fila m fila e en la , donde m proviene de la tabla de miembros , si existe un
tabla Entrada donde m.MemberID = e.MemberID .

casi directamente en SQL con el uso de la palabra clave EXISTS:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m
DONDE EXISTE
(SELECCIONE * DESDE Entrada e DONDE e.MemberID = m.MemberID);

Este es otro ejemplo de una consulta anidada donde tenemos dos sentencias SQL SELECT, una dentro de la otra. Este
es un poco diferente del ejemplo más simple que vimos anteriormente en el capítulo. El DONDE
condición en la consulta interna se refiere a parte de la fila que se considera en la consulta externa; es decir, e.MemberID = m.MemberID
Figura 4-5 . La variable m estáque
. Encuentro comprobando
la forma máscada filadedeinterpretar
fácil la tabla de miembros.
estas La consulta
consultas anidadasinterna
es con busca una fila
referencia a unendiagrama
la tabla Entrada
como con
el mismo valor para MemberID que la fila en consideración en la tabla Miembro. Si tal fila (o varias de esas filas) EXISTEN

, entonces estamos en el negocio.

58
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Para aquellos de ustedes que están pensando que esto parece una forma complicada de obtener un resultado simple,
tienen razón (en parte). La consulta que usa la cláusula EXISTS recupera los mismos miembros que una combinación interna
(en MemberID) entre Member y Entry. .
Sin embargo, ¿y si queremos a aquellos miembros que no han entrado en un torneo? Esto requiere solo un pequeño cambio
en nuestra nueva consulta SQL. En lugar de buscar miembros en los que exista una fila coincidente en Entry, ahora queremos
aquellos en los que no exista una fila coincidente. Agregar la palabra NOT a las declaraciones SQL anteriores nos da lo que
necesitamos:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m DONDE NO EXISTE

(SELECCIONE * DESDE Entrada e DONDE e.MemberID = m.MemberID);

La construcción NOT EXISTS revisará cada fila e en la tabla Entry, verificando si hay
es una fila que coincide con el ID de miembro de la fila actual en la tabla de miembros. El nombre del miembro se
recuperará solo si no se encuentra ninguna fila coincidente.
Ahora tenemos suficiente munición para abordar la consulta sobre los miembros que no han ingresado a un Open
torneo. Consulte la Figura 4-6 para decidir si se debe incluir a William Cooper en el resultado.

Figura 4-6. Existe una entrada para un torneo abierto para William Cooper

59
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Las filas indicadas en la Figura 4-6 muestran que existe una entrada para William Cooper, por lo que no incluiremos
nuestro resultado.
Ahora, mire esta declaración en lenguaje natural que describe la Figura 4-6 :

,
Escribiré los nombres de la fila m donde m proviene de la tabla de Miembros , siempre que no exista (una fila e en la
tabla de Entrada donde m.MemberID = e.MemberID junto con una fila t en la tabla de Torneos donde e.TourID =
t.TourID y t.TourType = 'Open')

El SQL que refleja la declaración anterior es:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m DONDE NO EXISTE

(SELECCIONE * DE Entrada e, Torneo t DONDE


m.MemberID = e.MemberID AND e.TourID =
t.TourID AND t.TourType = 'Open');

Veremos el enfoque de proceso para consultas como esta cuando cubramos las operaciones de conjuntos en el Capítulo 7.

Diferentes tipos de subconsultas


Vimos diferentes tipos de subconsultas en las secciones anteriores. Es útil revisar algunas de las opciones aquí.
La parte interna de la consulta anidada puede devolver un solo valor (p. ej., el hándicap de Bárbara), un conjunto de valores
(p. ej., los ID de torneos abiertos) o un conjunto de filas (p. ej., entradas en torneos abiertos). Además, las consultas internas y
externas pueden ser independientes hasta cierto punto, o pueden estar correlacionadas.

Consultas internas que devuelven un valor único Las consultas internas

que devuelven un valor único suelen ser útiles en situaciones en las que simplemente se recupera un subconjunto de filas.
Consideremos las desventajas de nuestros miembros, como se muestra en la Figura 4-7 .

Figura 4-7. Parte de la tabla de miembros que muestra nombres y desventajas

60
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Si queremos encontrar aquellos miembros con una desventaja de menos de 16, esto se puede hacer simplemente con el siguiente SQL:

SELECCIONE
* DESDE Miembro

m DONDE m.Hándicap < 16;

¿Qué debemos hacer si queremos encontrar a todos los miembros con una discapacidad menor que la de Barbara Olson? los
la consulta anterior hará eso por nosotros, pero solo si la desventaja de Bárbara de 16 no cambia. Para que la consulta funcione para
cualquiera que sea la desventaja actual de Barbara, podemos reemplazar el valor único 16 con el resultado de una consulta interna:

SELECCIONE *

DE Miembro m

DONDE Hándicap <


(SELECCIONE Hándicap
DE Miembro

WHERE Apellido = 'Olson' AND Nombre = 'Bárbara');

Necesitamos comparar Handicap con un solo valor. Si en una situación como esta nuestra consulta interna devuelve más
de un valor (por ejemplo, si hubiera más de una Barbara Olson en el club), obtendríamos un error al intentar ejecutar la consulta.

Una consulta interna que devuelve un valor único también es útil si queremos comparar valores con un agregado de algún tipo. Por ejemplo,
podríamos querer encontrar todos los miembros que tienen una discapacidad menor que el promedio.
En este caso, podemos usar la consulta interna para devolver el valor promedio:

SELECCIONE
* FROM Miembro m

DONDE m.Hándicap <


(SELECCIONE PROMEDIO(Hándicap)
DE miembro);

Si te lo tomas con calma, puedes construir gradualmente consultas bastante complicadas. Digamos que queremos ver
si algún miembro junior tiene un hándicap más bajo que el promedio de los seniors. La consulta interna tiene que devolver el hándicap de
valor promedio para un miembro senior, y luego queremos seleccionar a todos los jóvenes con un hándicap menor que eso. En la
instrucción SQL que sigue, tanto la consulta interna como la externa tienen una condición SELECT adicional (la interna recupera solo los
seniors y la externa recupera solo los juniors):

SELECCIONE
* FROM Miembro m

DONDE m.MemberType = 'Junior' AND Handicap < ( SELECT


AVG(Handicap)
FROM Miembro

DONDE MemberType = 'Senior');

61
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Consultas internas que devuelven un conjunto de valores


Aquí es donde comenzamos este capítulo. Cuando usamos la palabra clave IN, SQL esperará encontrar un conjunto de valores únicos.
Por ejemplo, podríamos solicitar filas de la tabla Entrada para miembros con ID EN un conjunto de valores. En

la siguiente instrucción, la consulta interna selecciona los ID de todos los miembros senior y la consulta externa devuelve las entradas de
esos miembros:

SELECCIONE *

DESDE Entrada
e DONDE e.MemberID IN
(SELECCIONE m.MemberID
DE Miembro m

WHERE m.MemberType = 'Senior');

La sección interna de este tipo de consulta debe devolver solo una columna. IN espera una lista de sencillos
valores (en este caso, una lista de MemberID ). Si la sección interna devuelve más de una columna (por ejemplo, SELECT * FROM
Member), obtendremos un error.
Muchas consultas anidadas como esta se pueden escribir de otras maneras, a menudo usando una combinación interna como
discutido anteriormente en el capítulo. Algunas consultas se sentirán más naturales para usted de una forma u otra.

Consultas internas que verifican la existencia


Otro tipo de consulta interna es la que vimos trabajar con la palabra clave EXISTS. Una declaración usando EXISTS
solo mira para ver si la consulta interna devuelve alguna fila. Los valores reales o el número de filas devueltas no son importantes. La
consulta que sigue devuelve las filas de la tabla Miembro donde podemos encontrar una fila correspondiente en la tabla Entrada para ese
miembro:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m
DONDE EXISTE

(SELECCIONE * DESDE Entrada


e DONDE e.MemberID = m.MemberID);

Debido a que los valores reales recuperados por la consulta interna no son importantes, la consulta interna a menudo tiene la forma
SELECT * FROM .
Otra característica de este tipo de consulta es que las secciones interior y exterior suelen estar correlacionadas. Con esto queremos
decir que la cláusula WHERE en la sección interior se refiere a los valores de la tabla en la sección exterior. En este caso el

consulta interna está comprobando si la fila actual en la tabla de entrada tiene el mismo ID de miembro que el miembro actualmente bajo
consideración en la consulta externa. Encuentro que la forma más fácil de visualizar esto es como se ilustra en la Figura 4-5 .
Es difícil pensar en una consulta EXISTS sensata que no correlacione los valores en el interior y el exterior.
secciones. Considere lo que devolverá la siguiente consulta:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m
DONDE EXISTE

(SELECCIONE * DE la entrada e);

La consulta anterior realmente no tiene ningún sentido. Dice escribir los nombres de cada miembro si hay una fila en la tabla Entrada
(¡cualquier fila!). Si la tabla de entrada está vacía, no obtendremos nada devuelto; de lo contrario, obtendremos todos los nombres de todos
los miembros. No puedo pensar por qué querrías hacer eso. Las consultas EXISTS son útiles cuando buscamos valores coincidentes en otro
lugar, y es por eso que la condición SELECT necesita comparar valores de las secciones interna y externa.

62
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Es interesante comparar las dos consultas siguientes. Ambos devuelven los nombres de los miembros que
han entrado en un torneo, pero los resultados son ligeramente diferentes. El primero usa una cláusula EXISTS:

SELECCIONE m.Apellido
FROM Miembro m
DONDE EXISTE

(SELECCIONE * FROM Entrada


e DONDE e.MemberID = m.MemberID);

El segundo usa un INNER JOIN:

SELECT m.LastName
FROM Miembro m INNER JOIN Entrada e ON e.MemberID = m.MemberID;

La diferencia entre las dos consultas es el número de filas que se devuelven.


La primera consulta inspecciona cada fila de la tabla de miembros solo una vez y devuelve el apellido si existe al menos una
entrada para ese miembro en la tabla de entradas. El apellido de cualquier miembro se escribirá una sola vez.

La segunda consulta forma una combinación entre las dos tablas que consistirá en cada combinación de filas en Member y Entry con
el mismo MemberID. El nombre de un miembro en particular se escribirá tantas veces como el número de torneos en los que participó.

Es una diferencia sutil, pero importante, especialmente si desea contar los devueltos.
filas Agregar DISTINCT en la cláusula SELECT del segundo ejemplo hará que los resultados de las dos consultas sean iguales.

Uso de subconsultas para actualizar


Este libro trata principalmente sobre consultas para recuperar datos, pero muchas de las mismas ideas se pueden usar para actualizar
datos y agregar o eliminar registros. En el Capítulo 1 analizamos consultas simples como actualizar el número de teléfono de un miembro
en particular, como se muestra aquí:

ACTUALIZAR Miembro
m SET m.Teléfono = '875076'
DONDE ID de miembro m = 118;

También analizamos la inserción y eliminación de filas de una tabla. Para insertar una fila, enumeramos las columnas para las que
proporcionamos valores y luego los valores, como se muestra a continuación:

INSERTAR EN Entrada (MemberID, TourID, Año)


VALORES (153, 25, 2016);

Ahora, consideremos una situación en la que queremos agregar una entrada para el torneo 25 en 2016 para cada uno de los
, donde la izquierda
juveniles en el club. Queremos agregar un conjunto de filas a la tabla Entrada, como se muestra en la Figura 4-8 ,
la columna tiene las identificaciones de miembro para cada uno de los juniors y las siguientes dos columnas son el torneo específico (25)
y el año (2016) para cada entrada. .

63
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Figura 4-8. Filas que se agregarán a la tabla de entrada

Podemos escribir una consulta SQL para devolver un conjunto de filas como las de la Figura 4-8 :

SELECCIONE m.MemberID, 25, 2016


DESDE Miembro m DONDE
m.MemberType = 'Junior';

Esta consulta es un poco diferente de otras que hemos visto porque tiene constantes en SELECT
cláusula. Construirá una fila para cada miembro junior con la identificación del miembro y las dos constantes 25 (para el torneo) y
2016 (para el año).
Ahora podemos usar la consulta anterior como una subconsulta en nuestra consulta INSERT. En lugar de proporcionar solo un
valor con la palabra clave VALUES, podemos proporcionar un conjunto de valores resultantes de la subconsulta. En la siguiente consulta,
,
el SELECT interno producirá el conjunto de filas que se ve en la Figura 4-8 y el INSERT externo las colocará en la tabla Entry:

INSERTAR EN Entrada (MemberID, TourID, Año)


-- crear una entrada en el torneo 25, 2016 para cada Junior
SELECCIONE ID de miembro, 25, 2016
DE Miembro
WHERE Tipo de miembro = 'Junior';

El mismo potencial para el uso de subconsultas se aplica a otros problemas de actualización. Digamos, con el fin de
encontrar un ejemplo, que después de ingresar datos en la tabla de Entrada para el torneo social 2016 en Kaiapoi (torneo 25) te
das cuenta de que solo los jugadores con handicap de 20 o más podían ingresar. Podría usar una subconsulta para eliminar las entradas
de los miembros con desventajas de menos de 20:

ELIMINAR DE Entrada
DONDE TourID = 25 Y Año = 2016 Y
ID de miembro EN

(SELECCIONE MemberID DE Miembro DONDE Hándicap < 20);

Resumen
Podemos usar subconsultas junto con las palabras clave IN y EXISTS en muchas situaciones. He aquí un resumen de las situaciones
que hemos visto en este capítulo.

64
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Ejemplos de diferentes tipos de subconsultas

Muchas consultas anidadas se pueden escribir de formas alternativas. En el Capítulo 9, veremos los problemas de rendimiento
relacionados con las diferentes formas de expresar las consultas, pero en general, debe usar la forma que le resulte más natural al
diseñar una consulta. Estos son algunos ejemplos de consultas anidadas y formas alternativas de expresarlas.

Una subconsulta que devuelve un solo valor

Encuentre los torneos en los que ha participado el miembro Cooper:

SELECCIONE e.TourID, e.Year FROM Entrada e WHERE e.MemberID =


(SELECT m.MemberID FROM Member m WHERE m.LastName =
'Cooper');

Una forma alternativa de escribir la consulta anterior es usar una combinación:

SELECT e.TourID, e.Year FROM


Entry e INNER JOIN Member m ON e.MemberID = m.MemberID WHERE m.LastName
= 'Cooper';

Una subconsulta que devuelve un conjunto de valores únicos

Encuentra todas las entradas para un torneo Open:

SELECCIONE *

DESDE Entrada
e DONDE e.TourID EN
(SELECCIONE t.TourID DESDE Torneo t DONDE
t.TourType = 'Open');

La consulta anterior se puede reemplazar con:

SELECCIONE e.MemberID, e.TourID, e.Year


FROM Entrada e INNER JOIN Torneo t ON e.TourID = t.TourID WHERE t.TourType =
'Open';

Una subconsulta que comprueba la existencia

Encuentra los nombres de los miembros que han ingresado a cualquier torneo:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m
DONDE EXISTE
(SELECCIONE * DESDE
Entrada e DONDE e.MemberID = m.MemberID);

sesenta y cinco
Machine Translated by Google

CAPÍTULO 4 ÿ SUBCONSULTAS

Esto se puede reemplazar con:

SELECT DISTINCT m.LastName, m.FirstName FROM Member


m INNER JOIN Entry e ON e.MemberID = m.MemberID;

Ejemplos de diferentes usos para subconsultas


Las subconsultas se pueden utilizar en muchas situaciones, incluidas las siguientes:

Construcción de consultas con negativos


Encuentra los nombres de los miembros que no han ingresado a un torneo:

SELECCIONE * DE Miembro m
DONDE NO EXISTE (SELECCIONE

* DE Entrada e DONDE
e.MemberID = m.MemberID);

Comparación de valores con los resultados de agregados


Encuentre los nombres de los miembros con discapacidades inferiores a la media:

SELECCIONE m.Apellido, m.Nombre DE Miembro m DONDE m.Hándicap < (SELECCIONE


PROMEDIO(Hándicap) DE Miembro);

Actualizar datos

Agregue una fila en la tabla de entrada para cada junior para el torneo 25 en 2016:

INSERTAR EN Entrada (MemberID, TourID, Año)


SELECCIONE ID de miembro, 25, 2016
FROM Miembro DONDE MemberType = 'Junior';

66
Machine Translated by Google

CAPÍTULO 5

Auto une

Cuando seleccionamos un subconjunto de filas en función de una condición en una cláusula WHERE, la condición se evalúa para cada
fila de forma independiente. Un ejemplo podría ser una consulta para encontrar todos los miembros que han ingresado al torneo 36.
La condición TourID = 36 se puede evaluar para cada fila en la tabla Entrada para lograr el resultado requerido.
Sin embargo, si queremos encontrar miembros que hayan ingresado en los torneos 36 y 24, no podemos hacerlo inspeccionando solo
una fila de la tabla de Entrada. Necesitamos encontrar dos filas (o entradas) para el mismo miembro, una para cada uno de los torneos
especificados. Una simple cláusula WHERE no puede lograr esto.
En este capítulo veremos las uniones automáticas. Con una unión entre dos tablas, primero hacemos un producto
cartesiano que nos da una combinación de filas de cada tabla. En un self join, hacemos lo mismo pero con dos copias de la misma tabla.
Esto nos proporciona todas las combinaciones de pares de filas de la tabla original.
Esta es una forma de escribir una consulta que necesita información de más de una fila en una tabla para satisfacer alguna
condición. Nos permitirá responder preguntas relacionadas con la palabra ambos ; por ejemplo, "¿Qué miembros participaron en
estos dos torneos?" Self joins también nos permitirá realizar consultas sobre tablas involucradas en relaciones self. Veremos primero
las relaciones con uno mismo.

Relaciones personales
Agreguemos más información a nuestra tabla de miembros. Supongamos que algunos miembros tienen entrenadores asignados.
¿Cómo representamos eso en los diagramas de clase de los que hablamos en el Capítulo 1? Podríamos tomar el enfoque que se
, con
muestra en la Figura 5-1 . De izquierda dos clases:
a derecha, Socio y Entrenador.
un entrenador puede tenerRecuerda lo que significan
varios miembros las líneas
para entrenar y los
(el 0..n másnúmeros.
cercano
a la clase Miembro). De derecha a izquierda, un miembro en particular puede tener un solo entrenador o ningún entrenador (el 0..1 más
cercano a la clase de Entrenador).

Figura 5-1. Modelo de datos para entrenadores y miembros (¡no recomendado!)

© Clare Churcher 2016 67


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_5
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

El problema con el modelo de la figura 5-1 es que los entrenadores, con toda probabilidad, son miembros del club.
Cuando implementamos este modelo con una tabla Coach y una tabla Member, algunas personas tendrán una fila registrando sus
detalles en cada tabla. Por ejemplo, Brenda Nolan tiene una fila en la tabla Miembros. Cuando asuma el rol de entrenadora, también
necesitaríamos una fila sobre ella en la tabla Entrenadora. Ahora, si Brenda obtiene un nuevo número de teléfono, alguien debe
acordarse de cambiarlo en ambas tablas. Con toda probabilidad esto no sucederá y terminaremos con el número anterior en una de
las tablas.
En este ejemplo, en realidad no tenemos dos clases separadas de miembros y entrenadores. solo tenemos uno
clase de miembros, algunos de los cuales entrenan a otros miembros. Esta relación con uno mismo se muestra en la Figura 5-2 .

Figura 5-2. Modelo de datos para miembros que entrenan a otros miembros

La línea de relación en la Figura 5-2 se puede leer en el sentido de las manecillas del reloj para decir que un miembro en particular
podría entrenar a varios otros miembros oa ninguno (0..n). En la otra dirección, podemos leer que un miembro en particular puede
tener un entrenador o ninguno (0..1).
En el Capítulo 1 mostramos cómo representar una relación 1 – Muchos agregando una columna a la tabla en el extremo 1 de
la relación, que tendrá valores de la clave principal de la tabla en el otro extremo. El modelo de la Figura 5-2 es exactamente el mismo
tipo de relación 1 – Muchos, excepto que tenemos la misma tabla en cada extremo, por lo tanto, una relación propia . Para representar
, en el miembro
la relación, podemos agregar una columna, tabla Coach, como se muestra en la Figura 5-3 . Los valores en el campo Entrenador
también deben existir en el campo clave MemberID .

68
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Figura 5-3. Entrenador de columnas agregado a la tabla de miembros

La primera fila de la tabla en la Figura 5-3 nos dice que Melissa es entrenada por el miembro 153, y podemos ver en la tercera línea de la
tabla que el miembro 153 es Brenda. Necesitamos que el valor en el campo Entrenador sea

restringido a ser uno de nuestros miembros existentes para que no podamos agregar accidentalmente un número de miembro no válido en la columna
Entrenador. Podemos hacer esto haciendo que el campo Coach sea una clave foránea. Recuerde del Capítulo 1 que una clave externa es un campo en
el que cualquier valor que no esté vacío en el campo ya debe existir como clave principal en otra tabla. Para la tabla de la Figura 5-3 , MemberType es
una clave externa que hace referencia a la tabla Type, lo que significa
Para que cualquier
la columna valor
Entrenador, en la "otra"
la tabla columna MemberType
es la ya debeen
tabla de miembros existir
sí. Laensiguiente
la tabla Type.
instrucción SQL muestra cómo usaríamos ALTER

Comando para agregar la nueva columna de clave externa Coach:

Miembro de ALTER TABLE

AÑADIR Entrenador INT EXTRANJERO REFERENCIAS CLAVE Miembro;

Con la tabla de miembros modificada, ahora podemos responder varios tipos diferentes de preguntas. Por ejemplo:

• ¿Cuáles son los nombres de los entrenadores?

• ¿Cuál es el nombre del entrenador de Jane Gilmore?


¿Alguien está siendo entrenado por alguien con un hándicap más alto?

• ¿Alguna mujer está siendo entrenada por hombres?

Ninguna de estas preguntas puede responderse inspeccionando una sola fila en la tabla. Lo que requerimos es un
unirse a sí mismo en la tabla de miembros. La forma más fácil de entender una unión automática es ver cómo hacemos una.

69
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Crear una autocombinación

Recuerde del Capítulo 3 la definición de una combinación entre dos tablas: un producto cartesiano (todas las
combinaciones de filas de cada tabla) seguido de la selección de un subconjunto de esas filas que satisfacen alguna
condición de combinación. Para una unión automática, pensamos en dos ,copias vemosdeparte del producto
la misma tabla. Encartesiano
la Figura 5-4
entre dos copias de la tabla Miembro. Para distinguir los diferentes elementos del producto, le he dado al y al segundo un
primero copie un alias, , alias diferente, c (verás por qué en un minuto). En la pequeña sección de
m el producto cartesiano visible en la Figura 5-4 , vemos la primera fila (Melissa) de la copia m emparejada con cada uno de los
filas de la copia c. Algunos
las columnas
de los encabezados
están truncados,
de ya que se estaba volviendo bastante ancho.

Figura 5-4. Producto cartesiano entre dos copias de la tabla de miembros

Para consultas sobre coaching, las filas interesantes del producto cartesiano son aquellas en las que el
valor de Coach de m es el mismo que MemberID de c, puede . e información
ver que la sobre
tercera línea
sobre Melissa
su contiene
entrenador información
(de la copia
(de la m
copia
de Member
c de )
Member ). Ahora la elección de alias queda clara: m para columnas sobre un miembro; c para las columnas sobre el
entrenador de ese miembro. La elección de alias útiles puede facilitar mucho la comprensión de las autouniones. Las
filas que nos gustaría seleccionar del producto cartesiano son aquellas que satisfacen m.Coach = c.MemberID. Esta
es la condición de unión requerida para encontrar información sobre los miembros y sus entrenadores. El SQL para
la unión automática es:

SELECT
* FROM Miembro m INNER JOIN Miembro c ON m.Coach = c.MemberID;

70
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

La tabla resultante de la unión automática se muestra en la Figura 5-5 .

Figura 5-5. Únase a sí mismo en la tabla de miembros para recuperar información sobre los miembros y sus entrenadores

Ahora que ya tenemos los resultados del self join, podemos responder a las preguntas planteadas en el apartado anterior
sobre el coaching. La parte más complicada de todo esto fue reconocer que mantener la información sobre los miembros y
entrenadores es una relación personal y, en primer lugar, diseñar la tabla de miembros de manera adecuada.

Consultas que implican una autounión Con la tabla

unida de la figura 5-5 como base, podemos responder todo tipo de preguntas simplemente seleccionando subconjuntos de
filas y proyectando las columnas apropiadas. Cada vez que necesito hacer consultas que implican autocombinaciones,
generalmente realizo la combinación primero, reteniendo todas las filas y columnas como en la Figura 5-5 . Con la mesa unida
(o un boceto rápido de las columnas) frente a mí, el camino a seguir suele ser relativamente simple. Veamos cómo funciona esto
con algunas preguntas.

¿Cuáles son los nombres de los entrenadores?


Mirando la Figura 5-5 la , podemos ver que los nombres de los entrenadores están en las columnas que vienen de la parte c de
unión. Solo queremos una lista de los nombres en las columnas c.LastName y c.FirstName para que esas columnas puedan
incluirse en la cláusula SELECT. No queremos que se repitan los nombres, por lo que usamos la palabra clave DISTINCT . La
siguiente instrucción SQL devolverá los nombres de los dos entrenadores, Brenda Nolan y William Cooper.

SELECCIONE DISTINCT c.FirstName, c.LastName


FROM Miembro m INNER JOIN Miembro c ON m.Coach = c.MemberID;

71
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

¿Quién está siendo entrenado por alguien con una discapacidad más alta?
Para saber quién está siendo entrenado por alguien con un hándicap más alto, debemos comparar el hándicap del miembro
( m.Handicap ) con el hándicap del entrenador de ese miembro ( c.Handicap ). Lo que se requiere es una cláusula WHERE
después de la cláusula de unión para encontrar dónde el hándicap del miembro es menor que el hándicap del entrenador:

SELECT *
FROM Miembro m INNER JOIN Miembro c ON m.Coach = c.MemberID WHERE
m.Handicap < c.Handicap;

Para los datos de la Figura 5-5, esto recuperará los datos en las últimas cuatro filas. (No tienes que ser un gran
golfista sea un buen entrenador!) Habiendo hecho la unión y seleccionado las filas apropiadas, podemos elegir qué columnas
queremos que aparezcan en el resultado final y listarlas en la cláusula SELECT.

Enumere los nombres de todos los miembros y los nombres de sus entrenadores

Enumerar los nombres de los miembros y sus entrenadores suena bastante trivial, pero si no tenemos cuidado, podemos
equivocarnos. Una primera idea podría ser proyectar solo las cuatro columnas que contienen los nombres de los miembros y
entrenadores de la tabla unida en la Figura 5-5 . Sin embargo, solo hay 10 filas en la tabla unida, mientras que hay 20 miembros
en la tabla de miembros. El problema aquí es que no todos los miembros tienen entrenadores. Vimos situaciones como esta en la
sección sobre combinaciones externas en el Capítulo 3.
Para recapitular, volvamos al producto cartesiano de dos copias de la tabla Miembro, pero observemos algunas filas que
involucran un miembro sin entrenador, como se muestra en la Figura 5-6 .

Figura 5-6. Parte del producto cartesiano entre dos copias de la tabla de miembros

72
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

La condición de unión ( m.Coach = c.MemberID ) nunca se cumple para un miembro con un valor nulo en la
columna Entrenador, por lo que todos esos miembros faltarán en nuestra tabla unida. Sólo tenemos que tener cuidado de
entender lo que realmente queremos. ¿Queremos una lista de todos los miembros con entrenadores (10 filas) o una lista de
todos los miembros junto con el nombre de su entrenador si tiene uno (20 filas)? Si es lo último, necesitamos una combinación externa.
Necesitamos ver el nombre de cada miembro (de la copia m de la tabla de miembros), junto con el nombre de su entrenador,
si lo tiene (de la copia c). El SQL para esta combinación externa es:

SELECCIONE m.Apellido AS MemberLast, m.FirstName AS MemberFirst,


c.LastName AS CoachLast, c.FirstName AS CoachFirst FROM Member
m LEFT OUTER JOIN Member c ON m.Coach = c.MemberID;

En la consulta anterior le hemos dado a cada atributo de salida un alias de columna . Un alias de columna cambia
temporalmente el nombre de una columna para mejorar la legibilidad de la salida. En este caso, ayuda al lector a distinguir qué
nombre pertenece a quién, como se muestra en la Figura 5-7 . Sin los alias, los atributos se etiquetarían como m.LastName y
c.LastName y así sucesivamente, que no son tan fáciles de entender. Recuerde del Capítulo 3 que para una combinación externa
izquierda, donde no hay una fila coincidente de la tabla de la derecha, esas columnas se llenarán con valores nulos. La figura 5-7
muestra el resultado de la combinación externa izquierda.

Figura 5-7. Combinación externa izquierda para enumerar todos los miembros y entrenadores

73
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

¿Quién entrena a los entrenadores o quién es mi abuela?


La unión automática entre dos copias de la tabla de miembros nos muestra un nivel de miembros y entrenadores. Si
, podemos
observamos las filas de la Figura 5-7 dirigida por
verBrenda
que Thomas
Nolan,Sexton
que noes
tiene
entrenado
entrenador.
por William
La jerarquía
Cooper,
no quien
es tanainteresante
su vez es
para este problema, pero hay varias situaciones análogas donde la jerarquía es de considerable interés. La genealogía es una.

Considere el modelo de datos y parte de la tabla Person en la figura 5-8 . En aras de mantener las cosas realmente simples,
consideraremos solo un poco de información sobre mujeres y madres biológicas.

Figura 5-8. Modelo de datos para mujeres y sus madres biológicas

La relación de la figura 5-8 se puede leer en el sentido de las agujas del reloj como “una persona puede ser madre de
varias personas más” y en sentido contrario como “una persona tiene como máximo una madre y puede no tener ninguna”. Ahora.
en la vida real, esa última afirmación no suena bien, seguramente todos tienen una madre. Sin embargo, como ocurre con todas
las bases de datos, esta base de datos es solo una aproximación de las complejidades de la vida real y solo puede conservar los
datos que están disponibles. A menos que rastreemos a todos hasta el limo primigenio, habrá algunas personas en nuestra mesa
cuya madre no conocemos. Brenda es una. La tabla y el modelo de la figura 5-8 tienen exactamente la misma estructura que
nuestro ejemplo de miembro y entrenador, pero una pregunta como "¿Quién es la abuela de Sue?" parece un poco más probable
que "¿Quién entrena a mi entrenador?"
Entonces, ¿cómo obtenemos información sobre las personas junto con información sobre sus madres? Al igual que en la
sección anterior, debemos unir la tabla Person a sí misma. (No olvide hacer que la combinación sea externa para no perder a
Brenda). El SQL es:

SELECCIONE *

FROM Persona p LEFT OUTER JOIN Persona m en p.Madre = m.ID;

La interfaz esquemática de Access para la unión se muestra en la Figura 5-9 , junto con la tabla resultante. He
dada la primera copia de la tabla el alias p de persona y la segunda copia el alias m de madre .

74
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Figura 5-9. Encontrar personas y sus madres: diagrama de acceso para la combinación externa izquierda y la tabla resultante

Ahora, ¿qué hay de volver a la generación anterior? Para eso, necesitamos realizar otra combinación externa izquierda
entre la tabla de resultados en la Figura 5-9 y otra copia de Personas (con el alias g de abuela ).
El SQL para las dos uniones externas izquierdas es:

SELECT *
FROM (Persona p LEFT JOIN Persona m ON p.Madre = m.ID)
LEFT JOIN Persona g ON m.Madre = g.ID;

La tabla resultante se muestra en la Figura 5-10 .

75
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Figura 5-10. Encontrar tres generaciones: Diagrama de acceso para las uniones externas izquierdas y la tabla resultante

Claramente, podemos seguir haciendo más y más uniones automáticas hasta que nos quedemos sin generaciones. Es probable
que este tipo de consultas jerárquicas aparezcan siempre que tengamos relaciones personales. Un pequeño inconveniente es que
debemos especificar el número de uniones en cada consulta. El SQL estándar no tiene la noción de una consulta que sigue realizando
automáticamente las uniones automáticas hasta que se agota el número de generaciones, como "Buscar a todas mis antepasadas
femeninas"; sin embargo, algunas implementaciones admiten esto. 1

Un enfoque de resultados para las uniones automáticas Las preguntas

de las secciones anteriores fueron bastante fáciles de responder una vez que nos dimos cuenta de que necesitábamos uniones automáticas.
Este fue un ejemplo del enfoque de proceso: ¿qué operaciones debemos realizar? A veces, sin embargo, estas realizaciones no
siempre llegan cuando las necesitas. Cada vez que mi mente se queda en blanco ante una consulta, recurro a un enfoque de resultados.

Miremos nuevamente nuestra tabla de miembros y hagamos una pregunta simple: ¿Quién es el entrenador de Melissa? no pienses
sobre relaciones o uniones, solo mire los datos desde la perspectiva de un profano. En la Figura 5-11 , puede ver cómo averiguar la
respuesta, incluso si nunca ha oído hablar de una unión automática (la mayoría de la gente no lo ha hecho).

1Algunas implementaciones de SQL admiten consultas recursivas que pueden realizar un seguimiento de las relaciones propias. Consulte su
.
documentación para buscar frases clave como CON o CONECTAR POR

76
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Figura 5-11. Encontrar al entrenador de Melissa

Para encontrar el entrenador de Melissa, primero buscamos la fila de Melissa (m en la figura 5-11 ) y luego observamos que su entrenador
es el miembro 153. Luego encontramos otra fila (c para entrenador) que tiene el valor MemberID de 153; podemos ver que la entrenadora
de Melissa es Brenda. No necesita saber nada sobre relaciones personales o claves externas o uniones para darse cuenta. Pero una vez que
tenga esa lógica clara en su mente, puede escribirla en lenguaje natural, y luego la traducción a SQL es bastante sencilla.

Escribamos una descripción de la Figura 5-11 :

Necesito ver dos filas (m y c) en la tabla de miembros y quiero escribir c.FirstName


donde c.MemberID tiene el mismo valor que m.Coach y m.FirstName es 'Melissa'

Y aquí está el SQL correspondiente:

SELECCIONE c.FirstName
FROM Miembro m, Member c
WHERE c.MemberID = m.Coach AND m.FirstName = 'Melissa';

Entonces, ¿cómo se corresponde este enfoque de salida con el enfoque de proceso que consideramos anteriormente? Como tu
podría esperar, el SQL anterior es solo una forma alternativa de establecer la misma consulta que aquella en la que usamos la combinación
automática. En la instrucción SQL anterior, la línea media es el producto cartesiano entre dos copias de la tabla de miembros y la primera
parte de la cláusula WHERE es la condición de unión. La declaración FROM Member m, Member c WHERE c.MemberID = m.Coach es solo
otra forma de expresar la autounión que usamos en las secciones anteriores.

Probemos una de las otras consultas usando un enfoque de resultado: ¿Quién está siendo entrenado por alguien con un
mayor hándicap? La imagen que necesitaría en mi cabeza para responder a esta pregunta se muestra en la Figura 5-12 .

77
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Figura 5-12. Encontrar miembros que sean entrenados por alguien con un hándicap más alto

Podemos ver que Deborah, cuyo hándicap es 12, está siendo entrenada por el miembro 235. Miembro 235,
William, tiene un hándicap de 14, por lo que Deborah satisface nuestros criterios. Aquí está la declaración más general
que representa la lógica representada en la Figura 5-12 :

Voy a mirar cada fila (m) en la tabla de miembros y escribiré m.FirstName y m.LastName si existe alguna otra fila (c)
en la tabla de miembros donde c.MemberID es lo mismo que m. Entrenador y m.Handicap es menor que c.Handicap

El SQL sigue de una manera sencilla:

SELECCIONE m.FirstName, m.LastName


FROM Miembro m, Miembro c WHERE
c.MemberID = m.Coach AND m.Handicap < c.Handicap;

Una vez más, puede ver el equivalente de self join en la consulta anterior ( FROM Member m, Member
c DONDE c.MemberID = m.Coach) . La utilidad de este enfoque de resultados es que no necesita comprender qué es una
unión automática, ni debe dar el salto mental de que la necesita. Al pensar en términos de dedos virtuales y qué filas están
involucradas para ayudarlo con su decisión, puede esbozar una declaración de los criterios. El SQL generalmente se sigue
fácilmente de eso.

78
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Preguntas que involucran "ambos"


En la sección "Evitar errores comunes" del Capítulo 2, analizamos preguntas como "¿Qué miembros han ingresado a los torneos
24 y 36?" Para recapitular, he reproducido la tabla Entrada en la Figura 5-13 .

Figura 5-13. Mesa de entrada

79
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Un primer intento común en una instrucción SQL para encontrar entradas en ambos torneos es el siguiente:

-- No producirá el resultado deseado SELECCIONE


e.MemberID FROM Entrada e WHERE e.TourID =
24 AND e.TourID = 36;

Recuerde que se aplica una condición DONDE a cada fila de la tabla individualmente. La condición (ej.
TourID = 24 AND e.TourID = 36 ) nunca es cierto para ninguna fila individual, ya que cada fila tiene un solo valor para
TourID . La consulta anterior nunca devolverá
y 36) simultáneamente. ninguna
Tal consulta puedefila
serporque el valor
bastante en TourID
peligrosa, noelpuede
porque serpuede
usuario dos cosas diferentes
interpretar el (24
resultado vacío como que ningún miembro ha ingresado a ambos torneos, mientras que la declaración de la consulta es
incorrecta.

Para responder a la pregunta, debemos observar más de una fila en la tabla Entrada. encuentro un resultado
enfoque sea el más natural para tratar cuestiones que involucren “ambos”.

Un enfoque de resultado para preguntas que involucran "ambos"


La imagen que necesito en mi cabeza para responder "¿Qué miembros han ingresado a los dos torneos 24 y 36?" se
muestra en la Figura 5-14 .

Figura 5-14. ¿Qué miembros han entrado en los dos torneos 24 y 36?

Mirando la Figura 5-14 , está bastante claro que el miembro 228 ha entrado en ambos torneos. estamos para
buscando dos filas (dos dedos, e1 y e2) con valores de MemberID coincidentes y donde las filas tienen los dos valores de
TourID requeridos.
Una expresión más general de la lógica que se muestra en la Figura 5-14 es:

Voy a mirar cada fila (e1) en la tabla Entrada . Escribiré el ID de miembro de esa fila si TourID
tiene el valor 24 y también puedo encontrar otra fila ( e2) en la tabla Entrada con el mismo
valor para MemberID y que tiene 36 como valor para TourID.

80
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

El SQL se sigue de aquí. Si tiene problemas con él, consulte la Figura 5-14 .

SELECCIONE
e1.MemberID DE Entrada e1,
Entrada e2 DONDE e1.MemberID =
e2.MemberID Y e1.TourID = 24 Y e2.TourID = 36;

Un enfoque de proceso para las preguntas que involucran "ambos"


Como siempre, tenemos varias formas de pensar en una consulta. Eche un vistazo a las dos líneas centrales de la última consulta.
DESDE la entrada e1, la entrada e2 es un producto cartesiano (que nos dará todas las combinaciones de pares de filas),
seguido de la selección de un subconjunto de filas que satisfagan ( DONDE e1.MemberID = e2.MemberID ). Esta es una unión. De
hecho, es una unión automática entre dos copias de la tabla de entrada. Parte de la unión entre dos copias de la tabla Entrada se
muestra en la Figura 5-15 .

Figura 5-15. Parte de la unión automática entre dos copias de la tabla de Entrada

81
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

La autounión en la Figura 5-15 muestra esas combinaciones de filas de la tabla Entrada para el mismo
miembro. Por ejemplo, podemos ver todas las combinaciones de filas que involucran al miembro 228. Podemos usar esta
unión automática para responder la pregunta sobre los miembros que han ingresado a los torneos 24 y 36. Solo necesitamos
encontrar una fila que tenga 24 de la primera copia y 36 de la segunda copia (o viceversa), es decir, e1.TourID = 24 Y
e2.TourID = 36 .
El SQL para esta autounión seguido de la cláusula WHERE para seleccionar las filas con los valores apropiados de
TourID se muestra aquí:

SELECCIONE
e1.MemberID FROM Entrada e1 INNER JOIN Entrada e2 ON e1.MemberID =
e2.MemberID DONDE e1.TourID = 24 Y e2.TourID = 36;

Si compara las dos consultas para encontrar las entradas en los dos torneos 24 y 26, verá lo similares que son. Ambos
producirán exactamente el mismo resultado. Probablemente encontrará uno u otro más intuitivo.

Resumen
Muchas consultas requieren que obtengamos información de dos filas de una tabla. Esto aparece en una serie de
situaciones. Los principales son donde tenemos relaciones personales o donde hay preguntas que involucran la palabra
"ambos". Hemos analizado tanto los enfoques de procesos como los enfoques de resultados para estas consultas. Ambos
dieron como resultado declaraciones SQL de aspecto muy similar que devuelven el mismo resultado. Tener los dos
enfoques diferentes es útil para aquellas ocasiones en las que la declaración de consulta no es inmediatamente obvia.

Auto-relaciones Tenemos una

auto-relación cuando diferentes instancias de una clase están relacionadas entre sí. En el ejemplo de este capítulo, teníamos
que algunos miembros son entrenadores de otros miembros.
Desde una perspectiva de proceso, las consultas sobre coaches o relaciones de coaching requieren uniones
automáticas, que toman dos copias de la tabla y las unen. En el siguiente ejemplo, la copia de la tabla Miembro con la
información sobre el miembro tiene el alias m , y la copia con información sobre el entrenador tiene el alias c :

SELECT m.LastName, m.FirstName, c.LastName, c.FirstName FROM


Miembro m INNER JOIN Miembro c ON m.Coach = c.MemberID

Alternativamente, desde un enfoque de salida, podríamos generar esta consulta equivalente:

SELECCIONE m.FirstName, m.LastName, c.LastName, c.FirstName FROM


Miembro m, Miembro c WHERE c.MemberID = m.Coach

Ambas consultas pueden formar la base de consultas para responder una serie de preguntas sobre el coaching.

Preguntas que involucran la palabra "ambos"


Las preguntas con la palabra "ambos" a menudo significan que debemos mirar dos filas en una tabla. En nuestro
ejemplo, queríamos encontrar el MemberID de los miembros que ingresaron a los torneos 24 y 36.

82
Machine Translated by Google

CAPÍTULO 5 ÿ UNIONES AUTOMÁTICAS

Desde un enfoque de resultado, necesitábamos encontrar dos filas en la tabla Entrada (e1 y e2) para el mismo
miembro. Una de las filas debía ser para el torneo 24 y la otra para el torneo 36. A continuación se muestra la consulta
SQL basada en resultados:

SELECCIONE
e1.MemberID DE Entrada e1,
Entrada e2 DONDE e1.MemberID = e2.MemberID Y e1.TourID = 24 Y e2.TourID = 36;

Alternativamente, desde un enfoque de proceso, podríamos reconocer la necesidad de una autocombinación entre
dos copias de la tabla de entrada, que se realiza mediante la condición de combinación e1.MemberIDdebería
= e2.MemberID.
ir seguidoEsto
de
una cláusula WHERE para devolver las filas con los valores TourID apropiados.
La consulta de autounión equivalente a la consulta anterior es:

SELECCIONE
e1.MemberID FROM Entrada e1 INNER JOIN Entrada e2 ON e1.MemberID =
e2.MemberID DONDE e1.TourID = 24 Y e2.TourID = 36;

83
Machine Translated by Google

CAPÍTULO 6

Relaciones múltiples
Entre Mesas

Hemos analizado el 1 simple: muchas relaciones entre tablas (p. ej., cada miembro está asociado con un tipo de miembro) y
también hemos analizado las relaciones entre sí (p. ej., los miembros pueden asesorar a otros miembros). Otra situación que
ocurre con frecuencia es cuando hay más de una relación entre las mismas dos tablas.

Dos relaciones entre las mismas tablas


Consideremos cómo podríamos introducir la idea de equipos en la base de datos de clubes de golf. Podemos comenzar
pensando en qué información básica debemos mantener sobre un equipo. La Figura 6-1 muestra una clase que representa un
equipo simple junto con algunas filas en una tabla que representa la clase.

Figura 6-1. La clase Team y algunas filas en una tabla Team

Ahora debemos pensar en las relaciones entre la nueva clase Team y nuestras otras clases. La relación más obvia
es que los miembros jugarán por equipos. La figura 6-2 muestra un posible diagrama de clases que representa esta situación.

Figura 6-2. Un miembro puede pertenecer a un equipo.

© Clare Churcher 2016 85


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_6
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Interpretando el diagrama de clases en la Figura 6-2 de izquierda a derecha, tenemos que un miembro en particular puede
jugar en un equipo (el 1 más cercano a la clase Team), pero un miembro no necesita jugar para ningún equipo (el 0 más cercano a la
clase Team). Leyendo de derecha a izquierda, tenemos que un equipo podría tener muchos miembros jugando para él (el n más
cercano a la clase Miembro) pero podría no tener ninguno (el 0 más cercano a la clase Miembro). Esa última declaración puede
parecer un poco extraña, pero cuando agregamos nuevos equipos, o queremos comenzar de nuevo en una nueva temporada, es
posible que un equipo no tenga ningún miembro de inmediato.
Para representar una relación 1 – Muchos, recuerde del Capítulo 1 que tomamos la clave principal de la tabla en el extremo 1
de la relación y la agregamos como clave externa a la tabla en el extremo Muchos. La figura 6-3 muestra un nuevo campo de clave
externa, Equipo , que se refiere a la tabla Team.

Figura 6-3. Equipo de campo de clave externa en la tabla Miembro

Otra relación que es probable que ocurra entre el Miembro y el Equipo es que un miembro puede administrar un
equipo. La figura 6-4 muestra esta relación adicional en el diagrama de clases.

86
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Figura 6-4. Dos relaciones entre las clases Miembro y Equipo

La línea superior de la figura 6-4 se puede interpretar, de izquierda a derecha, como que indica que un miembro en particular podría
administrar (como máximo) un equipo; y de derecha a izquierda, ya que cada equipo tiene exactamente un entrenador.
Esta nueva relación es una relación 1 – 1. Para 1 – Muchas relaciones siempre hemos tomado la primaria
clave de un extremo de la relación y colóquela en la mesa en el otro extremo. Esta vez, ambos extremos tienen una cardinalidad de
1. Podríamos colocar una columna Team_I_Manage en la tabla Member o una columna Manager en la tabla Team. Este último es
más sensato, ya que el atributo obligatorio Manager es una información más importante sobre los equipos que el Team_I_Manage
opcional para los miembros. Generalmente, en una relación 1 – 1, tomamos la clave principal del extremo obligatorio (1:1 en el diagrama
de la Figura 6-4 ) y la colocamos como clave externa en el otro extremo.

La tabla Equipo, con su nueva columna de clave externa Administrador, se muestra junto con la tabla Miembro en la Figura
6-5 .

87
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Figura 6-5. Claves foráneas Team en la tabla Member y Manager en la tabla Team para representar las relaciones en la Figura 6-4

En la tabla de Miembros, podemos ver que cuatro personas juegan para el Equipo B (Brenda Nolan, William Cooper, Robert
Pollard y Betty Young), y en la tabla de Equipo, podemos ver que el miembro 153 (Brenda Nolan) es el gerente del Equipo B. . Notará
que no hay nada en el modelo de datos que indique si un gerente debe ser miembro del equipo o no. El gerente del Equipo B es
miembro del Equipo B, mientras que el gerente del Equipo A, 239 (Thomas Spence), no es miembro del Equipo A. Las únicas
restricciones implícitas en las claves externas son que el administrador de un equipo debe estar en la tabla de miembros y un miembro
solo puede pertenecer a un equipo que existe en la tabla de equipos.
Es posible que algunos de ustedes también se hayan dado cuenta de que hacer que Manager sea una clave externa no
impide que la misma persona administre más de un equipo. La restricción de clave externa no nos impide poner al miembro 239
como administrador tanto del Equipo A como del Equipo B. Hemos establecido efectivamente una relación de 1 – Muchos entre el
Equipo y el Miembro para la relación Administra. Si desea evitar que un solo miembro administre más de un equipo, puede poner
una restricción ÚNICA en la columna Gerente de la tabla Equipo.
Este tipo de situación se analiza con mayor profundidad en mi libro de diseño de bases de datos. 1 El siguiente SQL crearía una
tabla Team donde Manager es una clave externa que se refiere a la tabla Member y un miembro en particular solo puede aparecer
una vez en la columna Manager de la tabla:

CREAR MESA Equipo (


TeamName CHAR(10) CLAVE PRINCIPAL,
PrácticaNoche CHAR(20),
Gerente INT EXTRANJERO REFERENCIAS CLAVE Miembro ÚNICO);

1 Clare Churcher, Diseño de bases de datos para principiantes: de novato a profesional (Nueva York: Apress, 2012).

88
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Extracción de información de múltiples relaciones


Ahora que tenemos las tablas Team y Member y sus dos relaciones (Plays for y Manages), podemos comenzar a extraer
información. Si solo consideramos una relación a la vez, es relativamente sencillo construir consultas. Si queremos una
lista de los miembros que juegan para un equipo junto con la información básica sobre sus equipos de la tabla Team,
simplemente podemos unir las tablas Member y Team en Team = TeamName como en la consulta SQL aquí:

SELECCIONE m.MemberID, m.LastName, m.FirstName, m.Team,


t.TeamName, t.PracticeNight, t.Manager
FROM Miembro m INNER JOIN Equipo t ON m.Team = t.TeamName;

En la Figura 6-6 se muestra una representación gráfica y el resultado de la consulta anterior .

Figura 6-6. Unirse a Miembro y Equipo para obtener información adicional sobre el equipo de un miembro

89
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Del mismo modo, si queremos recuperar información sobre equipos, incluido el nombre del entrenador, podemos
únase a Miembro y Equipo en Manager = MemberID :

SELECCIONE t.TeamName, t.PracticeNight, t.Manager,


m.MemberID, m.LastName, m.FirstName
FROM Equipo t INNER JOIN Miembro m ON t.Manager = m.MemberID;

En la Figura 6-7 se muestra una representación gráfica y el resultado de la consulta anterior .

Figura 6-7. Unirse a Miembro y Equipo para obtener información adicional sobre el administrador de un equipo

Ahora veremos cómo recuperar información que involucre ambos tipos de relaciones.

Enfoque basado en procesos

La información de la combinación que se muestra en la Figura 6-6 no es particularmente útil. Tenemos las identificaciones
de los gerentes, pero sería más útil tener sus nombres también. Necesitamos otra unión. Primero, veremos qué hará Access de
manera predeterminada si agrega las tablas Miembro y Equipo en la interfaz de diseño de consultas. Esto se muestra en la Figura
6-8 .

90
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Figura 6-8. Uniones predeterminadas en Access si el miembro y el equipo se agregan a la interfaz de consulta esquemática

Una mirada al SQL de la consulta en la Figura 6-8 revela que está uniendo las tablas de esta manera:

SELECT *
FROM Miembro m INNER JOIN Equipo t ON

t.TeamName = m.Team AND m.MemberID = t.Manager;

¿Puedes averiguar qué pregunta está respondiendo esta consulta? La salida se muestra en la Figura 6-9 .

Figura 6-9. Salida para la combinación de acceso predeterminada en la figura 6-8

Para comprender lo que sucede con la combinación anterior, es útil considerar el producto cartesiano de Member y Team. El producto
cartesiano nos da todas laselcombinaciones de Team
Gerente y donde filas dey cada tabla. La condición de unión dice mostrar solo filas donde el MemberID es el mismo que
TeamName

son lo mismo. En el lenguaje cotidiano, esto equivale a "Muéstrame los miembros que gestionan el equipo en el que están". Para nuestros datos, esa
es solo la única fila de Brenda Nolan que vemos en la Figura 6-9 .
Entonces, ¿cómo construimos una consulta que nos muestre los nombres de los miembros, sus equipos y los nombres de los gerentes de
los equipos? La consulta que sigue proporcionará la información sobre los miembros, sus equipos y las identificaciones de los gerentes ( t.Manager );
sin embargo, no proporciona los nombres de los gerentes:

SELECT m.MemberID, m.LastName, m.FirstName, t.TeamName, t.Manager FROM Miembro m INNER


JOIN Team t ON m.Team = t.TeamName;

Lo que debemos hacer es tomar el resultado de la unión anterior y unirlo a una segunda copia de la tabla de miembros (m2) para
recuperar los nombres de los administradores. Queremos que la condición de combinación sea que t.Manager = m2.MemberID para obtener los
nombres del administrador. La figura 6-10 muestra una representación esquemática y el resultado de las dos uniones.

91
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Figura 6-10. Dos combinaciones y dos copias de la tabla de miembros para incluir los nombres de los jefes de equipo

La primera combinación nos brinda la información de los miembros de la primera copia de la tabla de miembros y la
información de la tabla de equipos para ese miembro; la segunda combinación nos da el nombre del administrador del equipo de la
segunda copia de la tabla de miembros. El SQL para las dos uniones es:

SELECCIONAR * DESDE (Miembro m INNER JOIN Equipo t ON m.Team = t.TeamName)


INNER JOIN Miembro m2 ON t.Manager = m2.MemberID;

Puede que le resulte instructivo comparar esta última consulta y salida con la consulta que implica un solo
unión entre las tablas Miembro y Equipo que se muestran en las Figuras 6-8 y 6-9 .
Ahora estamos en condiciones de generar una variedad de informes sobre los equipos y sus miembros. Figura 6-11
muestra un informe basado en la consulta anterior y su salida, que se muestra en la Figura 6-10 .

92
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Figura 6-11. Un informe basado en la consulta que se muestra en la Figura 6-10

El informe se ha agrupado por equipo, con la información del equipo y del administrador (de la tabla Equipo y copia m2 de la tabla Miembro)
en un encabezado de grupo. Los miembros del equipo (desde la primera copia m del Member
tabla) se encuentran en la parte detallada del informe.

Enfoque de resultados
Ahora veremos una forma alternativa de construir una consulta para recuperar toda la información sobre un equipo (nombres de los
miembros, nombre del equipo y nombre del gerente) para un informe como el de la Figura 6-11 . Encuentro la idea

de dos uniones bastante intuitiva, pero otras personas prefieren un enfoque diferente.
He reproducido las dos tablas de la figura 6-12 . Ahora, sin pensar en uniones, veamos cómo podemos
elija un miembro y descubra en qué equipo está y quién es el gerente de ese equipo.

Figura 6-12. Encontrar un miembro del equipo (William Cooper), el nombre de su equipo y el nombre del gerente del equipo

93
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Sin necesidad de pensar en uniones, podemos encontrar la información que necesitamos. Necesitamos información de tres filas. Veamos
un caso específico. Una fila (m) de la tabla de miembros nos dará el nombre de un miembro (William Cooper en la figura 6-12 ). Necesitamos
encontrar la fila ( t ) en la tabla Team para su equipo ( m.Team = t.TeamName ).
Luego necesitamos otra fila en la tabla de miembros (m2) para el administrador del equipo (t.Manager = m2.MemberID).
Con la ayuda de la Figura 6-12 podemos construir el siguiente SQL:

SELECCIONE m.Apellido, m.Nombre, m.Equipo, m2.Apellido, m2.Nombre DESDE Miembro m, Equipo


t, Miembro m2 DONDE m.Equipo = t.Nombre de equipo Y t.Gerente = m2.ID de miembro

Podríamos reemplazar m.Team con t.TeamName en la cláusula SELECT de la consulta anterior si lo deseamos.
La consulta anterior es equivalente a la consulta con las dos uniones. La cláusula FROM es el producto cartesiano de las tres
tablas. La cláusula WHERE proporciona la condición de unión para la unión entre el miembro (m) y el equipo (t) en m.Team = t.TeamName
y la condición de unión para la unión entre el equipo y otra copia del miembro (m2) en t.Manager = m2.ID de miembro
.

Reglas del negocio


El modelo de datos de la Figura 6-4 se vuelve a mostrar a continuación como Figura 6-13 .

Figura 6-13. Dos relaciones entre las clases Miembro y Equipo

Los miembros pueden pertenecer a equipos y los miembros pueden administrar equipos. Cuando implementamos estas relaciones con
claves externas, las restricciones que se imponen a los datos son bastante simples. Un miembro solo puede estar en un equipo que existe en la
tabla Equipo, y un equipo solo puede ser administrado por alguien en la tabla Miembro.
Es probable que se apliquen otras restricciones en diversas situaciones. Por ejemplo, podríamos tener las restricciones adicionales
de que un equipo no puede tener más de cuatro miembros o que el gerente debe ser miembro del equipo (o no). Estos tipos de restricciones
se conocen comúnmente como reglas comerciales . El modelo de datos de la figura 6-13 podría respaldar una base de datos para dos palos
de golf diferentes. Si bien las reglas básicas de integridad se aplicarán a ambos clubes (p. ej., un miembro no puede estar en un equipo que
no existe), cada club puede tener reglas diferentes sobre el tamaño de los equipos y quién puede administrarlos. Las restricciones de clave
externa no son suficientes para hacer cumplir tales reglas comerciales.

94
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Los productos de bases de datos relacionales generalmente proporcionarán alguna forma de hacer cumplir las reglas
comerciales. Los grandes sistemas como SQL Server y Oracle proporcionan disparadores . Los disparadores son acciones que
tienen lugar cuando ocurre un evento específico (por ejemplo, al insertar o actualizar un registro). El disparador rechazará cualquier
cambio que no obedezca las reglas. En Access y otros productos, no es posible aplicar dichas restricciones a las propias tablas.
Sin embargo, puede adjuntar macros a los formularios de entrada. Estas macros verificarán los datos en el formulario antes de
que se confirme en la base de datos. El problema con este enfoque es que no existe tal verificación si un usuario omite el formulario
e ingresa datos directamente en una tabla con (por ejemplo) un comando de actualización de SQL.
No veremos en detalle cómo se implementan las reglas comerciales en diferentes productos, pero veremos cómo las consultas
pueden ayudar a encontrar instancias en las que no se cumplen las restricciones. Aunque esto es encontrar el problema después de
que haya ocurrido, las variaciones de estas consultas formarían una base para cualquier disparador o macro que necesitaría escribir
para hacer cumplir las restricciones.
Veamos cómo encontrar equipos cuyos gerentes no sean miembros del equipo. Mi mente a menudo se queda en blanco
cuando me enfrento a una consulta como esta, y en ese caso, siempre tomo un enfoque de resultados. Esto significa imaginar las
mesas involucradas e imaginar el tipo de instancia que estoy buscando. Eche un vistazo a la Figura 6-14 .

Figura 6-14. Encontrar equipos cuyos gerentes no sean miembros del equipo

En la Figura 6-14, vemos en la tabla Team que el manager del TeamA es 239, y podemos ver en la tabla Member
ese miembro 239 no es miembro de ningún equipo. Si tuviéramos una restricción de que los gerentes deben pertenecer al
equipo, TeamA no la obedecería.
Para encontrar todos los equipos como este, diríamos:

Encuentre los nombres de los equipos de todas las filas (t ) en la tabla Team donde la fila coincidente
(m ) en la tabla Member para el administrador del equipo (es decir, t.Manager = m.MemberID ) tiene un
equipo (m.team ) que está vacío o es diferente del equipo en la tabla Team (m.Team <> t.TeamName ).

95
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

El SQL equivalente se muestra aquí:

SELECCIONE t.nombre
del equipo DESDE Miembro m,
Equipo t DONDE m.MemberID = t.Gerente
Y (m.Equipo <> t.Nombre del equipo O m.Equipo ES NULO)

Las dos líneas del medio equivalen a una unión entre las dos tablas en m.MemberID = t.Manager , y la línea final encuentra a los
gerentes que están en un equipo diferente o que no están en ningún equipo. La siguiente consulta producirá una salida equivalente pero usa
la notación de unión interna:

SELECT t.teamname

FROM Member m INNER JOIN Team t ON m.MemberID = t.Manager WHERE m.Team


<> t.Teamname O m.Team ES NULO

Solo una nota sobre por qué hemos incluido la condición IS NULL en las dos consultas: quizás recuerde
del Capítulo 2 que si hacemos una comparación con un valor nulo, el resultado no es ni verdadero ni falso. Si queremos encontrar
gerentes que no están en un equipo, debemos incluir específicamente esa posibilidad en nuestra consulta.
Si el requisito hubiera sido solo que un entrenador no debe pertenecer a un equipo diferente, podríamos haber dejado de lado la
verificación de valores nulos, porque un entrenador sin equipo hubiera estado bien. Como siempre, comprender claramente lo que
realmente está tratando de encontrar es la parte más importante de especificar una consulta.
Las dos consultas anteriores encontrarán equipos con entrenadores incorrectos, pero solo después de que se hayan agregado.
a la base de datos. ¿Cómo evitamos que se agreguen en primer lugar? La solución depende de la implementación de la base de datos.
Antes de que los cambios en los datos se confirmen finalmente en una base de datos, generalmente se registran en un búfer de algún tipo.
Por ejemplo, en SQL Server, los registros que se actualizan o agregan se mantienen en una tabla temporal llamada insertada (insert) que
tiene la misma estructura que la tabla Team que .se
Sicrea
agregamos
para contener
o actualizamos
los registros
algunos
nuevos
registros
o actualizados
a la tabla temporalmente.
Equipo, una tabla
Queremos
temporal
realizar una consulta para verificar si algún registro nuevo que se va a agregar a la tabla Equipo tiene administradores que no obedecen la
restricción. Sin embargo, en lugar de mirar la tabla Equipo, queremos mirar los registros en la tabla insertada temporal y contar cuántos de
ellos no son válidos.

La siguiente consulta SQL, que es muy similar a las dos consultas anteriores, contará cuántas de las filas en el búfer insertado
para la tabla Team tienen gerentes que no obedecen la regla comercial sobre los gerentes que pertenecen al equipo que administran:

SELECCIONE CONTEO(*)

FROM Miembro m INNER JOIN insertado i ON m.MemberID = i.Manager WHERE m.Team <>
i.Teamname OR m.Team IS NULL

Si este conteo no es cero entonces hay filas que están por insertarse que no obedecen las reglas. En ese caso, queremos deshacer
la inserción para que las filas no se confirmen en la tabla Equipo. La siguiente instrucción SQL se incluiría en un disparador en SQL. Sería
necesario asignar el activador para que se ejecute al actualizar o insertar filas en la tabla Equipo.

SI
(SELECCIONE CUENTA(*)
FROM Miembro m INNER JOIN insertado i ON m.MemberID = i.Manager WHERE m.Team <>
i.Teamname OR m.Team IS NULL) <> 0)

COMENZAR Rollback Tran


FINAL

96
Machine Translated by Google

CAPÍTULO 6 ÿ RELACIONES MÚLTIPLES ENTRE TABLAS

Este es un enfoque un poco tosco, porque si alguno de los nuevos registros es incorrecto, se rechaza todo el lote.
Deberá consultar la documentación de su producto de base de datos para ver cómo desarrollar disparadores que funcionen
de manera eficiente, pero la idea de usar una consulta para verificar la validez de los nuevos registros es común.
una.
En Access, la comprobación se realiza a nivel de interfaz, normalmente en un formulario. En lugar de verificar la
tabla insertada como en la consulta anterior, crearíamos una macro con una consulta similar para investigar los valores de
los campos en el formulario antes de enviarlos a la base de datos.

Resumen
Puede haber más de una relación entre tablas. Por ejemplo, "un miembro puede pertenecer a un equipo" es una relación. “Un
equipo tiene un miembro del club que es el entrenador” es otra relación. Encontrar información sobre el equipo de un miembro
(incluida la identificación del administrador) requiere una unión entre el miembro y el equipo. Si también queremos encontrar el .
nombre del administrador, debemos unir ese resultado a una segunda copia del miembro.
mesa, así:

SELECCIONAR *
DESDE (Miembro m INNER JOIN Equipo t ON m.Team = t.TeamName)
INNER JOIN Miembro m2 ON t.Manager = m2.MemberID

Puede haber reglas comerciales bastante complejas o restricciones relacionadas con las relaciones entre tablas.
Por ejemplo, podríamos exigir que el gerente sea miembro del equipo que él o ella administra, o que un gerente no sea
miembro de ningún equipo, o que un equipo debe tener menos de seis miembros. Estos a menudo requieren el uso de
disparadores. Los tipos de consultas discutidos en este capítulo serán útiles para formular el código requerido en los disparadores.

97
Machine Translated by Google

CAPÍTULO 7

Establecer operaciones

Una de las grandes fortalezas de la teoría de bases de datos relacionales es que las tablas (o, más formalmente, las relaciones) se
componen de filas distintas y, por lo tanto, pueden considerarse un conjunto . Luego podemos usar operaciones de conjuntos para
ayudar a combinar y extraer información específica. Los tipos de preguntas con las que ayudan las operaciones de conjunto son aquellas
como "¿qué personas están en ambos conjuntos?" o "¿qué personas están en este conjunto pero no en ese?"
En el Apéndice 2 puede encontrar alguna notación formal que es útil para administrar operaciones de conjuntos. En este
capítulo mantendremos las formalidades al mínimo, pero los símbolos para las operaciones con conjuntos son una forma abreviada
útil. La tabla 7-1 muestra las cuatro operaciones de conjuntos que veremos junto con sus símbolos comunes y la palabra clave SQL asociada
(para aquellos que los tienen).

Tabla 7-1. Cuatro operaciones con conjuntos y sus símbolos

Operación Símbolo Palabra clave SQL

Unión ÿ UNIÓN

Intersección ÿ INTERSECARSE

Diferencia ÿ

EXCEPTO

División ÷

No todas las implementaciones de SQL admiten todas las palabras clave de la Tabla 7-1 , así que buscaremos formas alternativas
para lograr el mismo resultado cuando las palabras clave no están disponibles.

Descripción general de las operaciones básicas de conjuntos

Veremos cada una de las operaciones de conjuntos por separado, pero para que sepa hacia dónde nos dirigimos, solo daré una descripción
general muy rápida de las tres operaciones más comunes: unión, intersección y diferencia. Imagina que tenemos tablas de membresía de
dos clubes de golf. Podríamos querer hacer lo siguiente:

• Determinar quién está en ambos clubes.

• Forme una lista grande que combine a todos los miembros.

• Averigüe quién está en un club pero no en el otro.

Las operaciones básicas de set nos permiten llevar a cabo todas estas tareas.
Supongamos que cada club guarda los nombres de sus miembros en una tabla. Las dos tablas tienen exactamente
las mismas columnas (más sobre esto en la siguiente sección) y se muestran en la Figura 7-1 . (OK, ¡son clubes muy pequeños!)

© Clare Churcher 2016 99


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_7
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-1. Dos tablas de nombres de miembros

Las operaciones básicas de conjuntos en estas dos tablas se resumen en la figura 7-2 . Se han superpuesto las
imágenes de dos mesas de club para que los miembros en común queden superpuestos. ClubA es la mesa principal en cada
imagen. Para cada sección de la Figura 7-2 , el cuadro muestra el resultado de la operación de ajuste.

100
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-2. Las operaciones de set básicas en las dos mesas ClubA (arriba) y ClubB (abajo)

El operador de unión (arriba a la izquierda en la Figura 7-2 ) muestra todos los nombres de cada tabla (con los
duplicados eliminados). El operador de intersección (arriba a la derecha) devuelve las dos filas que aparecen en ambas tablas. Los
operadores de diferencia (abajo) devuelven aquellas filas que se encuentran en un club pero no en el otro.

Tablas compatibles con Union


Las operaciones de conjunto unión, intersección y diferencia operan entre dos conjuntos de filas. No tiene ningún sentido tratar
de comparar filas en tablas que tienen estructuras muy diferentes, como las de la figura 7-3 .

101
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-3. No tiene sentido tratar de comparar filas de tablas con estructuras diferentes

Entonces, ¿qué determina si dos conjuntos de filas se pueden comparar usando las operaciones de conjunto
unión, intersección y diferencia? Formalmente, los dos conjuntos deben tener el mismo número de columnas y cada
columna debe tener el mismo dominio. Estrictamente hablando, un dominio es un conjunto de valores posibles. Sin
embargo, en la práctica, el requisito para las operaciones con conjuntos es que las columnas correspondientes (es decir,
1 Los etc.
en orden de izquierda a derecha) en cada conjunto de filas tengan los mismos tipos: ambos caracteres, ambos enteros,
nombres de las columnas no necesitan ser iguales. Las tablas que cumplen con estos requisitos se denominan compatibles
con la unión , aunque el requisito también es necesario para las operaciones de intersección y diferencia.

La figura 7-4 muestra un par de tablas que son compatibles con la unión. Aunque los nombres de las
columnas son diferentes, tienen el mismo número de columnas y las columnas correspondientes tienen los mismos tipos.

1
En la teoría relacional formal, los atributos de una relación no tienen orden, sino que son referenciados por sus nombres. El orden
de las columnas en las tablas es cómo las implementaciones de SQL determinan la compatibilidad de la unión.

102
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-4. Tablas compatibles con la unión, aunque los nombres de las columnas sean diferentes

La figura 7-5 tiene dos tablas con los mismos nombres de columna, pero no son compatibles con la unión porque el orden de las
columnas es tal que la cuarta columna tiene un tipo de número en la tabla superior y un tipo de carácter en la parte inferior, y viceversa.
para la última columna.

103
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-5. Tablas que no son compatibles con union

Diferentes implementaciones de SQL pueden interpretar la rigurosidad de este requisito por la "igualdad" de
dominios o tipos de manera diferente. Estrictamente hablando, dos campos definidos como CHAR(10) y CHAR(12) tienen
dominios diferentes, pero muchas implementaciones de SQL permitirán que se consideren iguales a los efectos de las
operaciones de configuración. Algunas implementaciones también convertirán números en caracteres para permitir que se
lleven a cabo operaciones de conjuntos. Encuentro esto particularmente aterrador y no recomiendo que dejes que tu aplicación
tome este tipo de decisiones por ti. Las siguientes secciones demuestran cómo puede usar SQL para hacer que sus tablas
sean compatibles.

Garantizar la compatibilidad de la unión Cuando las

tablas no son compatibles con la unión, a menudo puede remediar la incompatibilidad en las cláusulas SELECT.
Para el par de tablas de la Figura 7-5 , si solo seleccionamos las columnas de la siguiente manera, el orden de las
columnas evitará que las filas devueltas sean compatibles con la unión:

SELECCIONE * DESDE ClubC;


SELECCIONE * DESDE ClubD;

104
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Sin embargo, podemos especificar el orden de las columnas en la cláusula SELECT:

SELECCIONE MemberID, LastName, FirstName, Handicap, MemberType FROM ClubC;


SELECCIONE MemberID, LastName, FirstName, Handicap, MemberType FROM ClubD;

Los dos conjuntos de filas devueltos por estas consultas ahora son compatibles con la unión.
Otro problema de incompatibilidad ocurre cuando los tipos de las columnas han sido declarados como tipos
diferentes en el diseño original de las tablas. Por ejemplo, la mesa ClubC puede tener el campo Hándicap declarado como INT,
mientras que la mesa ClubD
, puede haber almacenado (imprudentemente) los valores de Hándicap en un campo CHAR.
(Recuerde del Capítulo 2 que si almacenamos valores en un carácter o campo de texto, se ordenarán alfabéticamente y no
podremos realizar funciones como el promedio en ellos). Como se mencionó anteriormente, diferentes implementaciones de
SQL tratarán estos diferentes tipos en una variedad de formas. Muchos intentarán convertir los números a texto o viceversa.
Puede tomar el control de estas conversiones usted mismo (lo que probablemente sea una buena idea) mediante el uso de
funciones de conversión de tipos.
Por ejemplo, en SQL Server, la expresión Convert(INT, Handicap) tomaría un valor de texto en el campo Handicap
("14") y lo convertiría en un valor entero (14). (Si el valor en el campo Handicap no se pudiera convertir a un número entero,
ocurriría un error). Si el campo Handicap en la tabla ClubD fuera un CHAR
escriba entonces podríamos usar la función de conversión en la cláusula SELECT. Los dos conjuntos de filas devueltos por las
siguientes consultas ahora serán compatibles con la unión:

SELECCIONE MemberID, LastName, FirstName, Handicap FROM ClubC;


SELECCIONE MemberID, LastName, FirstName, Convert (INT, Handicap) FROM ClubD;

Unión
La unión nos permite producir una salida que consta de todas las filas únicas de dos conjuntos de filas compatibles con la unión.
Para llevar a cabo una unión en SQL, primero debemos recuperar dos conjuntos de filas usando dos cláusulas SELECT y
luego combinar los dos conjuntos con la palabra clave UNION. El siguiente SQL muestra la unión de todas las filas de las dos
tablas compatibles con la unión (ClubA y ClubB) que se muestran en la Figura 7-4 .

SELECCIONE * DESDE ClubA


UNIÓN
SELECCIONE * DESDE ClubB;

La tabla resultante incluirá todas las filas de ambas tablas sin duplicados, por lo que solo verá una fila para Barbara Olson,
Robert Pollard y Thomas Sexton, como se muestra en la Figura 7-6 . Si desea conservar los
duplicados por algún motivo, puede utilizar la frase clave UNION ALL .

105
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-6. Unión de ClubA y ClubB sin filas duplicadas

Como las tablas compatibles con la unión no necesitan tener los mismos nombres de columna, los nombres de las columnas
en la tabla virtual resultante normalmente será de una de las tablas. En el ejemplo de la Figura 7-6 , los nombres son , de la columna
los mismos que los de la primera tabla mencionada en la consulta de unión.
No importa para el operador de unión en qué orden se especifican las dos tablas. La consulta que sigue devolverá las
mismas filas que la consulta anterior. Las filas pueden aparecer en un orden diferente y los nombres mostrados de las columnas
pueden cambiar, pero los datos serán los mismos.

SELECCIONE * DESDE ClubB


UNIÓN
SELECCIONE * DESDE ClubA;

Selección de las columnas apropiadas Cuando utilice el operador

de unión, debe pensar detenidamente qué es lo que realmente desea. Los ejemplos con los clubes son bastante artificiosos (como sin
duda habrás notado). Es muy poco probable que dos clubes tengan miembros con los mismos números de identificación y tipos de
membresía idénticos. Un escenario más probable es que si Barbara Olson perteneciera a dos clubes, tendría datos diferentes en la
tabla de cada club. En la tabla ClubA, podría ser Senior con un valor de 258 para MemberID. En la tabla ClubB, podría ser una
Asociada con un valor de 4573 para RegNum. .
Si hacemos la unión que se muestra en la tabla , donde seleccionamos todas las columnas de cada
de la Figura 7-6 , las dos filas de Bárbara serán diferentes, por lo que ambas aparecerán en el resultado, como en la Figura 7-7 .

106
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-7. Aparecen dos registros para Barbara Olson en el sindicato porque las filas son diferentes

Tenemos que considerar lo que realmente queremos de tal unión. Si necesitamos una lista de nombres para una
fiesta de Navidad conjunta de los dos clubes, no queremos que todos aparezcan dos veces. La forma de evitar duplicados es
proyectar solo los nombres de cada tabla antes de realizar la unión:

SELECCIONE FamilyName, Name FROM ClubA


UNIÓN
SELECCIONE Apellido, Nombre DEL ClubB;

Con esta consulta, las dos filas para Bárbara serán las mismas y solo aparecerán en la unión una vez, como en la Figura
7-8 .

Figura 7-8. Solo aparece una fila para Barbara Olson si solo las columnas de nombre están en la unión

Por supuesto, hay un problema serio con esta última consulta. Puede haber dos Barbara Olson, una en cada club, y ahora
solo se imprimirá una etiqueta con su nombre para las dos. Lamentablemente, los datos reales están plagados de este tipo de
problemas. Con un poco de suerte, habrá algún número universal de la asociación nacional de golf que podría resolver esto,
pero si no, solo necesita estar alerta. La operación de intersección, discutida en la siguiente sección, produciría los nombres que
aparecen en ambas listas de clubes, y se podría realizar una verificación de cordura manual.

107
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Usos de la Unión
El uso principal de la unión es combinar datos de dos o más tablas, como hemos estado haciendo en las secciones anteriores. Por
ejemplo, si los datos de entrada al torneo para diferentes meses se hubieran almacenado en tablas separadas (¡no es una gran
decisión de diseño!), podríamos usar varias operaciones de unión para combinar los datos de todo el año.
También es posible combinar dos conjuntos de filas de una tabla. Digamos que queríamos saber cuántos
las personas han ingresado al torneo 24 o al torneo 36 desde la tabla Entrada en la Figura 7-9 .

Figura 7-9. Mesa de entrada

Podríamos intentar seleccionar las filas para los miembros que ingresan al torneo 24 y las filas para los miembros que
ingresan al torneo 36, y tomar la unión. ¿Cuántas filas obtendremos si realizamos la siguiente consulta?

SELECCIONE * DESDE Entrada DONDE TourID = 24


UNIÓN
SELECCIONE * DESDE Entrada DONDE TourID = 36;

Obtendremos diez filas de esta consulta, una para cada fila con un 24 o un 36. Debido a que hemos conservado las columnas
TourID y Year, las filas que hemos seleccionado son todas diferentes y todas aparecerán en el resultado de la unión. En realidad, la
consulta devuelve todas las entradas distintas en los torneos 24 y 36 en lugar de todos los miembros distintos que han entrado en
los dos torneos. La consulta que fluye toma la unión de solo los ID de los dos torneos:

SELECCIONE MemberID FROM Entrada DONDE TourID = 24


UNIÓN
SELECCIONE MemberID FROM Entrada DONDE TourID = 36;

108
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Ahora obtendremos las cinco identificaciones (118, 228, 258, 286, 415) que son las identificaciones únicas para quienes ingresan a
uno u otro de los torneos.
Hay una forma mucho más sencilla de recuperar a los que han entrado en el torneo 24 o 36. Simplemente podemos incluir un OR en
la cláusula WHERE:

SELECCIONE ID de miembro DEL Torneo


DONDE TourID = 24 O TourID = 36;

¿Cuántas filas devolverá la consulta anterior? Nuevamente, devolverá diez filas, cada una de las filas con un 24 o un 36 en la
columna TourID. Para obtener las cinco ID únicas, debemos agregar la palabra clave DISTINCT en la cláusula SELECT.

Uniones y uniones exteriores completas

En el Capítulo 3 vimos diferentes operaciones de unión: uniones internas, uniones externas izquierda y derecha y uniones externas completas.
Algunos productos (por ejemplo, Microsoft Access 2013) no admiten la palabra clave FULL OUTER JOIN; sin embargo, podemos realizar una
consulta equivalente utilizando la palabra clave UNION.
Para recapitular, repasemos los diferentes tipos de unión que podemos realizar entre la tabla de miembros (solo una muy
¡pequeño!) y la tabla Tipo que se muestra en la Figura 7-10 .

Figura 7-10. Las (pequeñas) tablas Member y Type

La figura 7-11 muestra la combinación interna entre las dos tablas, con la condición de combinación MemberType = Type . No
obtenemos una fila para Helen Branch porque no tiene ningún valor en MemberType y, por lo tanto, la condición de combinación nunca se
cumplirá para ella. Esto puede ser un problema si alguien que mira la tabla en la Figura 7-11 asume que muestra todos los miembros.

Figura 7-11. La unión interna entre Member y Type en MemberType = Type

109
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Ahora veremos las uniones externas. La unión externa izquierda asegura que veamos todas las filas de la tabla de la izquierda
(Miembro); la unión externa derecha nos da todas las filas de la tabla de la derecha ( Tipo ); y la unión externa completa nos da
todas las filas de ambas tablas. Estas uniones externas, todas con condición de unión MemberType = Type, se muestran en la
Figura 7-12 .

Figura 7-12. Tres uniones externas entre Member y Type en MemberType = Type

La figura 7-12 muestra que, en este caso, la combinación externa completa consta de filas únicas de cada una de las otras dos
combinaciones externas; es decir, un sindicato. Si su implementación de SQL no admite explícitamente una combinación externa
completa, siempre puede lograr el mismo resultado con la siguiente consulta:

SELECCIONE * DE Miembro LEFT JOIN Tipo ON MemberType = Tipo


UNIÓN
SELECT * FROM Miembro RIGHT JOIN Tipo ON MemberType = Tipo;

110
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Intersección
Si toma la intersección de dos tablas compatibles con la unión, recuperará las filas que se encuentran en ambas tablas.
, idénticas
La Figura 7-13 reproduce las dos tablas, ClubA y ClubB de la Figura 7-4 . Podemos ver que hay cuatro
en ambas filas que son
tablas.

Figura 7-13. Las filas en la intersección entre las mesas ClubA y ClubB

La palabra clave para el operador de intersección en SQL es INTERSECT. La expresión para recuperar las cuatro
filas comunes a ambas tablas (es decir, para los miembros Spence, Olson, Pollard y Sexton) es la siguiente:

SELECCIONE * DESDE ClubA


INTERSECARSE

SELECCIONE * DESDE ClubB;

111
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Al igual que con el operador de unión, los dos conjuntos de filas deben ser compatibles con la unión; es decir,
deben tener el mismo número de columnas y las columnas correspondientes deben tener los mismos dominios. Esto
puede significar proyectar las columnas apropiadas de las tablas base de la misma manera que se describe en la sección
"Selección de las columnas apropiadas" anteriormente en este capítulo. No importa cuál de las tablas mencionemos
primero en la consulta, ya que las filas devueltas por la intersección serán las mismas independientemente del orden de
las tablas.

Usos de la intersección
Un uso común de la operación de intersección es el que se muestra en la Figura 7-13 : encontrar filas comunes en dos
tablas con información similar. Otro uso muy común de la intersección es responder preguntas que incluyen la palabra
ambos . Un ejemplo típico es "¿Qué miembros han entrado en los dos torneos 36 y 38?"
La tabla Entrada se reproduce en la Figura 7-14 .

Figura 7-14. La mesa de entrada

112
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

¿Qué se devolverá si recuperamos las filas de cada torneo y tomamos la intersección como en la siguiente consulta?

SELECCIONE * DESDE Entrada DONDE TourID = 36


INTERSECARSE

SELECCIONE * DESDE Entrada DONDE TourID = 38;

No se devolverán filas. La figura 7-15 le ayudará a entender por qué.

Figura 7-15. Dos consultas no tienen filas en común, por lo que ninguna fila resulta de la intersección

Las dos consultas nunca tendrán filas en común porque una siempre tendrá 36 en el TourID
mientras que la otra siempre tendrá 38. Esencialmente, la consulta que intentábamos realizar era encontrar todas las entradas
para el torneo 36 que también son entradas para el torneo 38. El resultado, dada la forma en que gestionamos las entradas, es
ninguna.
Para recuperar los miembros que son comunes en los dos conjuntos de filas en la Figura 7-15 , debemos recuperar solo
la columna MemberID antes de realizar la intersección, como en la consulta aquí:

SELECCIONE MemberID DESDE Entrada DONDE Tourid = 36


INTERSECARSE

SELECCIONE MemberID DESDE Entrada DONDE Tourid = 38;

Esta consulta se ilustra en la Figura 7-16 . Al igual que con una unión, el resultado de la operación de intersección devuelve
filas únicas.

Figura 7-16. Uso de la intersección para encontrar miembros inscritos en ambos torneos 36 y 38

113
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Supongamos que ahora queremos encontrar los nombres de los miembros. Desde el punto de vista del proceso, podemos tomar la
resultado de la intersección y únalo con la tabla de miembros para obtener los nombres, como se muestra en la Figura 7-17 .

Figura 7-17. Unirse a la intersección con la tabla de miembros para encontrar los nombres

Entonces, ¿cómo se ve el SQL para hacer primero la intersección y luego unirse con la tabla de miembros? los
El siguiente es un buen primer intento, pero desafortunadamente no funcionará:

--No funcionará
SELECCIONE Apellido, Nombre DE
Miembro m ÚNASE INTERNAMENTE
(SELECCIONE e1.MemberID DE Entrada e1 DONDE e1.TourID = 36
INTERSECARSE

SELECCIONE e2.MemberID DESDE la entrada e2 DONDE e2.TourID = 38)


ON m.ID de miembro = e1.ID de miembro;

Las tablas que solo aparecen dentro de la consulta interna (la parte entre paréntesis) no pueden ser referenciadas por la consulta
externa (la combinación). Esto se resuelve fácilmente dando un alias a la parte anidada de la consulta. De la misma manera que le hemos
dado a la tabla de Miembros un alias poniendo una m después de Miembro en la cláusula FROM, podemos darle a toda la consulta interna
un alias de NewTable (como ejemplo) poniendo NewTable después del paréntesis final de la consulta interna . Ahora podemos referirnos a
ese alias en la condición de unión como se muestra en la consulta aquí:

SELECCIONE Apellido, Nombre DE


Miembro m INNER JOIN (SELECCIONE
e1.MemberID DE Entrada e1 DONDE e1.TourID = 36

114
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

INTERSECARSE

SELECCIONE e2.MemberID DESDE la entrada e2 DONDE e2.TourID = 36) NewTable


ON m.MemberID = NewTable.MemberID;

Otra forma de recuperar los nombres es utilizar una consulta anidada. Aquí, la consulta interna recupera los ID que
están en la intersección, y la consulta externa encuentra los nombres correspondientes de la tabla Miembro.

SELECCIONE Apellido, Nombre


DE Miembro
DONDE ID DE MIEMBRO EN

(SELECCIONE MemberID DESDE Entrada DONDE TourID = 36


INTERSECARSE

SELECCIONE MemberID DESDE Entrada DONDE TourID = 38);

La importancia de proyectar las columnas apropiadas Es importante pensar con mucho cuidado qué

columnas se incluyen en las tablas involucradas en una operación de intersección. Vimos en la sección anterior cómo la
siguiente consulta no devolverá filas:

SELECCIONE * DESDE Entrada DONDE TourID = 36


INTERSECARSE

SELECCIONE * DESDE Entrada DONDE TourID = 38;

Las filas de la primera consulta siempre tendrán 36 como valor de TourID y las filas de la segunda
la consulta tendrá 38. Nunca habrá filas en común. Recuperar solo el MemberID en cada una de las consultas resuelve este
problema.
Más interesante es que proyectar correctamente diferentes columnas puede proporcionar respuestas a muy diferentes
preguntas. ¿Cómo describiría las filas devueltas por la siguiente consulta?

SELECCIONE MemberID, Año DESDE Entrada DONDE TourID = 25


INTERSECARSE

SELECCIONE MemberID, Año DESDE Entrada DONDE TourID = 36;

La consulta se ilustra en la Figura 7-18 .

Figura 7-18. ¿Qué significa la intersección?

115
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

En la Figura 7-18, estamos encontrando a todos los miembros que ingresaron a los torneos 25 y 36 en el mismo año .
Esta es la razón por la cual no hay una entrada para el miembro 415 en la intersección: ingresó al torneo 25 en 2013 y al
torneo 36 en 2014 y 2015. Aunque su ID de miembro aparece en las dos tablas de contribución, las filas correspondientes son
para años diferentes. No hay ninguna fila para el miembro 415 que sea igual en ambas tablas.
Como puede ver, la elección de las columnas que se proyectan para las tablas de contribución es fundamental para lo que
aparecerá en la intersección. Significa que hay muchas preguntas diferentes que se pueden responder de manera muy elegante,
pero también significa que puede obtener fácilmente respuestas incorrectas si no piensa la consulta detenidamente.

Administración sin la palabra clave INTERSECT No todas las implementaciones de

SQL admiten la intersección explícitamente. Sin embargo, tenemos otras formas de realizar las consultas que involucran
"ambos". La intersección es un enfoque de proceso: estamos diciendo qué operaciones debemos realizar en las tablas
involucradas en la consulta. Si no tenemos éxito con este enfoque, entonces podemos probar el enfoque de resultados. Esto
implica descubrir algunas posibles respuestas al inspeccionar las tablas y no preocuparse por operaciones como intersecciones
y uniones. En la figura 7-19 imaginamos dos dedos recorriendo las filas de la tabla Entrada. Necesitamos encontrar dos filas en la
tabla Entrada con el mismo MemberID: una con TourID = 36 y otra con TourID = 38.

Figura 7-19. Encontrar miembros que hayan ingresado a ambos torneos 36 y 38

La situación que representa la Figura 7-19 se puede describir como:

Devuélveme el MemberID de una fila e1 en la tabla Entry donde TourID =36 si hay otra fila e2 en
la tabla Entry que tiene el mismo MemberID y TourID =38 .

La expresión SQL equivalente a esta descripción y la Figura 7-19 es:

SELECCIONE DISTINTO e1.MemberID


DE Entrada e1, Entrada e2 DONDE
e1.MemberID = e2.MemberID Y e1.TourID =
36 Y e2.TourID = 38;

116
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

¿Qué pasa con la consulta para encontrar las filas que aparecen en las tablas ClubA y ClubB? Las mesas de club se vuelven
a mostrar en la Figura 7-20 . Para encontrar las filas que son iguales en ambas tablas, debemos verificar cada uno de los valores en
las columnas correspondientes para asegurarnos de que sean iguales.

Figura 7-20. Encontrar la intersección entre ClubA y ClubB

La situación representada en la Figura 7-20 se puede describir como:

Devolveré la fila a de la tabla ClubA si hay una fila b en ClubB que tiene valores idénticos en todos los campos (es decir,
a.RegNum = b.MemberID, a.FamilyName = b.LastName y a.Name = b. Primer nombre).

El SQL para la intersección que se muestra en la Figura 7-20 es:

SELECCIONE a.RegNum, a.FamilyName, a.Name


FROM ClubA a, ClubB b WHERE a.RegNum =
b.MemberID AND a.FamilyName = b.LastName AND
a.Name = b.FirstName;

117
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Diferencia
Tomando la diferencia entre dos tablas encuentra aquellas filas que están en la primera tabla pero no en la segunda y viceversa. Para
nuestros dos pequeños clubes, reproduje los resultados del operador de diferencia en la figura 7-21 .

Figura 7-21. El operador de diferencia encuentra filas en una tabla que no aparecen en la otra.

La palabra clave en SQL estándar para el operador de diferencia es EXCEPT. Oracle se diferencia del ISO SQL
estándar, y de la mayoría de los otros sistemas de bases de datos, en su uso de la palabra clave MINUS en lugar de EXCEPT .
Al igual que con los operadores de unión e intersección, las tablas involucradas en una operación de diferencia deben ser
compatibles con la unión. A diferencia de los operadores de unión e intersección, el orden de las tablas es importante para el operador
de diferencia; los resultados para ClubA - ClubB son diferentes de los de ClubB - ClubA (como se muestra en la Figura 7-21 ).

El SQL para encontrar los nombres de personas en la tabla ClubA que no aparecen en la tabla ClubB es:

SELECCIONE Apellido, Nombre DEL ClubA


EXCEPTO

SELECCIONE Apellido, Nombre DEL ClubB;

Usos de la diferencia
Siempre que tenga una consulta que tenga la palabra "no", debe considerar la posibilidad de que el operador de diferencia sea útil. Por
ejemplo, ¿cómo encontramos miembros que no han ingresado al torneo 25?
Recuerde del Capítulo 5 por qué la siguiente consulta no devuelve aquellos miembros que no han ingresado al torneo 25:

SELECCIONE ID de miembro DESDE la entrada


DONDE TourID <> 25;

118
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

La consulta anterior selecciona todas las filas en la tabla Entrada que no son para el torneo 25. Esencialmente encuentra
un miembro que ha entrado en cualquier torneo que no sea el 25 (aunque también podría haber entrado en el 25).
Observando la Figura 7-15 , , vemos que la consulta devolvería la fila marcada e1 para el miembro 415 ingresando
torneo 36 (TourID <> 25). Sin embargo, dos filas arriba, vemos que el miembro 415 también ingresó al torneo 25. Es difícil pensar
en una razón por la que podría querer usar esta consulta.
Un enfoque de proceso para este tipo de consulta es usar la diferencia. Necesitamos recuperar un conjunto de ID de todos los
miembros y otro conjunto de ID de todos los miembros que han ingresado al torneo 25. Luego queremos la diferencia; es decir, aquellas
identificaciones que están en el primer conjunto pero no en el último.
Encontrar el conjunto de todos los miembros que han ingresado al torneo 25 es simple:

SELECCIONE MemberID FROM Entrada DONDE TourID = 25;

Podríamos pensar que una consulta similar también nos encontrará todos los ID de miembro:

SELECCIONE MemberID DE Entrada;

Sin embargo, la consulta anterior solo nos encuentra un conjunto de miembros que han ingresado a torneos. Para obtener un conjunto de
ID de todos los miembros, debemos consultar la tabla de miembros.
La figura 7-22 es una ilustración de cómo se puede usar el operador de diferencia para encontrar los ID de miembro que necesitamos.

Figura 7-22. Miembros que no han ingresado al torneo 25

119
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

La expresión SQL para recuperar los ID de los miembros que no han ingresado al torneo 25 es la siguiente:

SELECCIONE ID de miembro DE miembro


EXCEPTO

SELECCIONE MemberID FROM Entrada DONDE TourID = 25;

Al igual que con las operaciones de intersección y unión, es importante que proyectemos las columnas apropiadas. Hemos
antes de usar el operador de diferencia. En las tablas de la Figura , recuperado los ID de Member y Entry.
7-22 . Si queremos incluir los nombres de los miembros, podemos usar uno de los métodos explicados en la sección "Usos de la intersección"
anteriormente en este capítulo.
Sin embargo, en este ejemplo de diferencias, ya teníamos los nombres de los miembros en la tabla de miembros.
antes de quitarlos para obtener el conjunto de filas en el lado izquierdo de la Figura 7-22 . Parece un poco perverso quitar los nombres
y luego volver a colocarlos. Lo importante es que los dos conjuntos de filas involucrados en la diferencia sean compatibles en la unión; es
decir, las columnas correspondientes deben tener los mismos dominios. Ambos conjuntos tienen solo ID o ambos conjuntos tienen ID y nombres.
En la operación del lado izquierdo de la Figura 7-22 , tomó la primera opción y eliminó los nombres de Member . Podríamos haber dejado, nosotros
los
nombres en la tabla de miembros y agregado los nombres a las filas en el medio
comodese
lamuestra
figura 7-22
en la
uniendo
figura 7-23
las tablas
. Entonces
de entradas
podríamos
y miembros,
tomar la
diferencia entre estos dos conjuntos de filas.

Figura 7-23. Incluir nombres de miembros en ambos conjuntos de filas antes de tomar la diferencia

120
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

El equivalente SQL de las operaciones que se muestran en la Figura 7-23 es el siguiente:

SELECT MemberID, LastName, FirstName FROM Member EXCEPT


SELECT m.MemberID, m.LastName, m.FirstName FROM Entry e
inner join Member m on e.MemberID = m.MemberID WHERE TourID
= 25;

Administración sin la palabra clave EXCEPT No todas las versiones de SQL

admiten la palabra clave EXCEPT (o MINUS). Como siempre, suele haber otra forma de formular una consulta. En el Capítulo
4, analizamos un enfoque de resultados para responder preguntas relacionadas con la palabra no . La figura 7-24 repasa los
procesos de pensamiento utilizados para encontrar los nombres de los miembros que no han ingresado al torneo 25.

Figura 7-24. Decidir que el miembro 258 no ha entrado en el torneo 25

121
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

El proceso de pensamiento detrás de la Figura 7-24 es:

Escriba los nombres de la fila m de la tabla de miembros si no existe una fila e en la tabla de entradas para ese miembro (es
decir, m.MemberID = e.MemberID) donde TourID=25.

El SQL que refleja la Figura 7-24 es:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m DONDE NO EXISTE
(SELECCIONE * DE Entrada e DONDE
e.MemberID = m.MemberID Y e.TourID
= 25);

¿Qué tipo de consulta debe usar para las preguntas que involucran la palabra no ? El que usa el enfoque de proceso y la palabra
clave EXCEPTO o el que usa el enfoque de resultado con las palabras clave NO EXISTE
o NO EN? Por lo general, diría que realmente no importa, ya que su motor de base de datos probablemente será lo suficientemente
inteligente como para reconocerlos como iguales. Sin embargo, la versión de SQL Server que estoy usando en este momento (2013)
realiza la consulta usando NOT EXISTS de manera más eficiente que la consulta correspondiente usando EXCEPT. ¡Tienes que
preguntarte
si te importa! Las consultas en bases de datos pequeñas suelen ser tan rápidas que realmente no importa si se ejecutan un poco más
lentamente. Sin embargo, si tiene muchos datos, entonces todo cambia. La eficiencia de las consultas puede volverse extremadamente
importante y, en ese caso, también deberá considerar otros aspectos del diseño de su base de datos, como los índices. Hablaré un poco
más sobre esto en el Capítulo 9.

División
El último operador de conjuntos que veremos en este capítulo es la división. La división es útil para consultas que involucran la palabra
todo o cada . Un ejemplo es "¿Qué miembros han entrado en todos los torneos?" SQL estándar no tiene una palabra clave para
la operación de división, y puede ser un poco incómodo descifrar el SQL para consultas que involucran división.

En el Apéndice 2 encontrarás la notación algebraica formal para realizar la división y cómo representar
usando otros operadores si es necesario. En el apartado “Cuantificador universal y SQL” del Apéndice 2, también encontrará una
forma alternativa de realizar consultas de tipo división utilizando expresiones de cálculo (o resultado).
Ambos métodos lo ayudan a construir declaraciones SQL que son análogas al operador de división. En el Capítulo 8, veremos los
agregados y veremos cuál creo que es la forma más sencilla de escribir un equivalente SQL del operador de división.

Por ahora veremos qué hace el operador de división y cómo usarlo para responder diferentes tipos de
preguntas que involucran a todos y cada uno .
La forma más fácil de entender la operación de división es con un ejemplo. Si queremos saber qué miembros han entrado en
cada torneo, necesitamos dos bits de información. Primero, necesitamos información sobre los miembros y los torneos en los que han
ingresado, que podemos obtener de la tabla de Entrada. También necesitamos una lista de todos los torneos, que debe provenir de la tabla
de Torneos, ya que es posible que no todos los torneos estén representados en la tabla de Entrada.

La figura 7-25 ilustra cómo funciona la división. He proyectado solo las columnas MemberID y TourID de
la tabla Entrada y la columna TourID de la tabla Torneo. Es importante qué columnas proyecta, y volveré a eso en un momento.

122
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-25. Usar la división para encontrar miembros que hayan ingresado a todos los torneos

Mirando la Figura 7-25 , tenemos en el medio una tabla con todos los valores de TourID (he etiquetado eso
Comprobar ). La operación de división comprueba la tabla de la izquierda para encontrar los valores de MemberID que tienen
que tienen todosuna
los fila
TourID
paraingresados
. La respuesta
en cada
(a la torneo.
derechaElde
miembro
la figura)
415
contiene
se puede
los encontrar
valores deemparejado
ID de miembro
con para
cadalos
unomiembros
de los
cinco torneos en la Entrada
tabla, y así aparece en el resultado de la división. El miembro 228 no aparece en el resultado porque no hay filas en la tabla
Entrada con 228 emparejado con 38 o 40.
Es importante obtener las columnas correctas en las dos tablas involucradas en la división. Me gusta pensar en
configurar la operación de división de esta manera:

• Decidir qué atributo quiero conocer. Llamemos a esto Respuesta . En este caso,
.
quiero encontrar valores de MemberID, nuestro atributo de Respuesta es MemberID

• En el lado derecho del operador de división, el(los) atributo(s) en la tabla debería ser lo que quiero
comparar. Llamemos a este(s) atributo(s) Check . En este caso, el atributo
Check es TourID. Podemos obtener todos los valores de TourID de la tabla de Torneos.

123
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

• En el lado izquierdo de la división, quiero una tabla que contenga solo los dos conjuntos de como se
atributos Responder y Verificar , muestra en la Figura 7-25 . Necesitamos MemberID y TourID
(en este caso, qué miembros han ingresado a qué torneo, y estos provienen de la tabla de Entrada).
Es importante que estas sean las únicas dos columnas en la tabla de la izquierda. Si se agregan
columnas adicionales, haremos preguntas diferentes, como se explica en la siguiente sección.

Como un pequeño aparte, muchas personas se preguntan por qué esta operación se , como no parece relacionarse
llama división particularmente bien a algo como 4 dividido por 2. La división es el inverso (o anulación) de la multiplicación
en la aritmética normal. Para operaciones con conjuntos, la división es como el inverso del producto cartesiano. Si piensa
en tomar el producto cartesiano de las dos tablas en el centro y en el extremo derecho de la Figura 7-25 , obtendrá
tabla con las una
mismas
columnas (pero no filas) que en el extremo izquierdo de la Figura 7-25 .
Podemos responder una serie de preguntas cambiando lo que está en el lado derecho del operador de división.
Por ejemplo, si quisiéramos saber quién ingresó a todos los torneos abiertos, reemplazaríamos la tabla en el medio de la
Figura 7-25 con solo las filas para los torneos abiertos:

SELECCIONE TourID
DESDE Tour
DONDE TourType = 'Abrir';

Proyección de columnas apropiadas Al igual que con las

operaciones de intersección y diferencia, la proyección de diferentes columnas en las operaciones de división le dará respuestas
, un
a diferentes preguntas. Una vez más, un ejemplo es la forma más fácil de entender esto. En la Figura 7-26 , se ha recuperado
una columna adicional de la tabla Entrada. ¿Puedes entender lo que está encontrando esta consulta?

124
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Figura 7-26. ¿Cuál es el resultado de la operación de división?

La división está buscando un conjunto de atributos de respuesta en la tabla de la izquierda que estén emparejados
con cada atributo de la tabla de verificación. En este caso, la operación busca un par MemberID y Year en la tabla de la izquierda
que aparece con cada uno de los torneos. Este ejemplo de división es encontrar aquellos miembros que hayan ingresado a
todos los torneos en el mismo año.

SQL para División


Usando un enfoque de salida, la consulta que queremos se puede expresar de esta manera:

Escriba el valor de m.Apellido, m.Nombre de las filas m en la tabla de Miembros donde por cada fila t en la tabla de
Torneos existe una fila e en la tabla de Entrada con e.MemberID = m.MemberID y e.TourID = t.TourID.

125
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Tenemos una palabra clave de SQL para existe, pero no para todos . Podemos deshacernos de cada palabra en la declaración
anterior usando la siguiente lógica ligeramente alucinante. La frase

por cada fila t en la tabla de Torneo existe una fila e en la tabla de Entrada …

es equivalente a decir

no hay una fila t en la tabla del Torneo donde no existe una fila e en la Entrada
mesa…

El Apéndice 2 brinda una explicación más formal de cómo derivar estas expresiones, pero por ahora solo reescribiremos
la descripción original de cómo recuperar los nombres de los miembros que han ingresado a todos los torneos usando la
equivalencia que acabamos de discutir.

Escriba el valor de m.Apellido, m.Nombre de las filas m en la tabla de Miembros donde por cada
fila que no hay fila t en la tabla de Torneo existe una fila donde no existe una fila e en la tabla de
Entrada con e .MemberID = m.MemberID y e.TourID = t.TourID .

El SQL correspondiente es:

SELECCIONE m.Apellido, m.Nombre DESDE Miembro m


DONDE NO EXISTE
(
SELECCIONE * DESDE Torneo t
DONDE NO EXISTE

( SELECCIONE * DESDE
Entrada e DONDE e.MemberID = m.MemberID AND e.TourID = t.TourID )

);

Los dobles negativos pueden ser un poco intimidantes, pero como dije al comienzo del capítulo, prometo un
Método conceptualmente más fácil para encontrar miembros que hayan ingresado a todos los torneos en el siguiente capítulo.

Resumen
Debido a que las tablas en una base de datos relacional tienen filas únicas (¡si están correctamente ingresadas!), pueden tratarse
como conjuntos matemáticos. Esto nos permite usar las operaciones de conjuntos unión, intersección, diferencia y división.
Unión, intersección y diferencia son operaciones que actúan entre tablas compatibles con la unión. Esto significa que la
tabla a cada lado del operador debe tener el mismo número de columnas y las columnas deben tener los mismos dominios (comúnmente
interpretados como los mismos tipos). Puede obtener tablas compatibles con la unión proyectando columnas con sensatez.

SQL tiene palabras clave para representar unión, intersección y diferencia, aunque no todas las implementaciones
admite las palabras clave para todas estas operaciones. Si su producto SQL no admite palabras clave para intersección o
diferencia, puede encontrar otras formas de expresar la consulta. Debe formular sus consultas de la manera que le resulte más
natural. Cuando tenga grandes cantidades de datos y la velocidad sea importante, es posible que deba investigar las eficiencias de
las diferentes formas de formular algunas consultas.

126
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

Aquí hay un resumen de las operaciones de conjuntos y formas alternativas de representarlas con SQL. A y B son
dos tablas compatibles con union con (para simplificar) solo una columna llamada atributo .

Unión
Una operación de unión devuelve todas las filas únicas que están en la tabla A o en la tabla B:

SELECCIONA atributo DE A
UNIÓN
SELECCIONE el atributo DE B;

Intersección
Una operación de intersección devuelve todas las filas que están tanto en la tabla A como en la tabla B:

SELECCIONA atributo DE A
INTERSECARSE

SELECCIONE el atributo DE B;

Una forma alternativa de representar la intersección es:

SELECCIONA A.atributo
DESDE UN DONDE EXISTE

(SELECCIONE B.atributo DESDE B


DONDE A.atributo = B.atributo);

Diferencia
La diferencia devuelve todas las filas que están en la primera tabla (A) que no están en la segunda tabla (B). Algunas
implementaciones usan la palabra clave MINUS en lugar de EXCEPT:

SELECCIONA atributo DE A
EXCEPTO

SELECCIONE el atributo DE B;

Una forma alternativa de representar la diferencia es:

SELECCIONE A.atributo
DESDE A DONDE NO
EXISTE (SELECCIONE

B.atributo DESDE B DONDE A.atributo


= B.atributo);

127
Machine Translated by Google

CAPÍTULO 7 ÿ OPERACIONES DE CONJUNTO

División
La operación de división ayuda con consultas con las palabras every o all . Las versiones actuales de SQL no admiten la
división directamente. Consulte las secciones "División" y "Cuantificador universal y SQL" en el Apéndice 2 para obtener
detalles sobre cómo expresar consultas que involucran división.
Para completar, repetimos la siguiente consulta, que devuelve los valores de MemberID para esos miembros
que han entrado en todos los torneos:

SELECCIONE m.Apellido, m.Nombre DESDE Miembro m


DONDE NO EXISTE
(
SELECCIONE * DESDE Torneo t
DONDE NO EXISTE

( SELECCIONE * DE Entrada
e DONDE e.MemberID = m.MemberID AND e.TourID = t.TourID
)
);

128
Machine Translated by Google

CAPÍTULO 8

Operaciones Agregadas

SQL tiene una serie de funciones para contar, sumar, promediar y realizar otras operaciones de agregación en una tabla. Estas
funciones nos permiten realizar una variedad de consultas. Por ejemplo, podemos contar el número de socios del club o encontrar
el hándicap medio. Podemos agrupar los datos de diferentes maneras para encontrar agregados. Por ejemplo, podríamos querer
contar el número de entradas a torneos en cada año individual, o podríamos querer encontrar el número de entradas en cada torneo
en particular.
En este capítulo, veremos agregados simples y cómo aprovechar al máximo las capacidades de agrupación de SQL.
En el próximo capítulo veremos las funciones de ventana, que brindan soluciones elegantes en situaciones que pueden ser
difíciles de abordar con solo la funcionalidad agregada básica.

Funciones agregadas simples


Los agregados simples incluyen promedios, totales y conteos. Estas son ideas sencillas, pero, como siempre, debe asegurarse de
comprender cómo funcionan cuando se trata de valores nulos y duplicados.

La función COUNT() La función


COUNT() calcula el número de filas que se devuelven de una consulta. El ejemplo más simple es contar
todas las filas devueltas por una consulta, lo que podemos hacer agregando un asterisco entre paréntesis.
La siguiente consulta devolverá el número de filas en la tabla Miembro:

SELECCIONE CONTEO(*)
DE Miembro;

Una sola función agregada como COUNT() en la consulta anterior devolverá una tabla con una columna
y una fila, como se muestra en la Figura 8-1 .

Figura 8-1. Resultado de la función CONTAR()

© Clare Churcher 2016 129


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_8
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

El resultado de la Figura 8-1 se produjo en Access y, como puede ver, la columna está etiquetada con un nombre
predeterminado. Podemos proporcionar un mejor nombre dando a la columna un alias. En la siguiente consulta, agregamos una
cláusula WHERE para contar el subconjunto de filas que satisfacen la condición Género = 'F' y usamos un AS
cláusula para que la columna tenga un encabezado más informativo:

SELECCIONE CONTEO(*) COMO NúmeroMujeres


DE Miembro
DONDE Género = 'F';

El resultado de esta consulta se muestra en la Figura 8-2 .

Figura 8-2. Resultado de la función COUNT() con un alias

Proporcionar las columnas devueltas por los agregados con un alias es una buena idea para que el lector tenga alguna
idea de lo que significan los números.

Gestión de valores nulos

La consulta anterior devuelve un recuento del número de miembros con un género de 'F' . Ahora considere la
consulta aquí:

SELECCIONE CONTEO(*)
DE Miembro
DONDE Género <> 'F';

A primera vista, podríamos pensar que los dos recuentos de las dos consultas anteriores deberían sumar el
número total de miembros. Pero tenemos que tener cuidado. En el Capítulo 2, vimos cómo operan las condiciones DONDE
cuando hacemos comparaciones con un valor nulo (o vacío). Si no hay valor para el atributo, entonces no podemos decir si cumple o
no una condición. ¡No lo sabemos! En SQL, si el valor que estamos comparando es nulo, el resultado de la comparación siempre será
falso. Las filas de la tabla con un valor nulo en la columna Género no se incluirán en ninguna de las dos consultas anteriores.

Podríamos argumentar que el atributo debería haberse declarado como NOT NULL en el diseño de la tabla. En el Capítulo 2
discutimos por qué esto podría no ser una buena idea. Si estamos tratando de ingresar los detalles de un nuevo miembro que no ha
proporcionado su género, no podremos guardar los detalles que conocemos o tendremos que adivinar el género. Ninguna opción es
satisfactoria. Es mejor guardar los detalles y hacer un seguimiento de los datos faltantes más adelante.

Podemos encontrar explícitamente cuántas de las filas no tienen un valor para Género con la consulta:

SELECCIONE CONTEO(*)
DE Miembro
DONDE EL GÉNERO ES NULO;

Los números de las tres consultas anteriores con condiciones Género = 'F' , Género <> 'F' , y Género
IS NULL ahora sumará el número total de miembros en el club. Consultas como la anterior pueden ser muy útiles para verificar si hay
valores nulos en columnas donde idealmente esperaríamos tener valores.
La función COUNT() también puede devolver el número de valores en una columna particular de una tabla o consulta.
Veamos algunas de las columnas de la tabla de miembros, como se muestra en la figura 8-3 .

130
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Figura 8-3. Algunas columnas de la tabla de miembros

Digamos que queremos encontrar el número de miembros que tienen un entrenador. Tenemos dos opciones. Una forma es
formule una consulta para devolver solo aquellos miembros que tengan un valor no nulo para Coach y cuente esos:

SELECCIONE CONTEO(*)
DE Miembro
DONDE Coach NO ES NULO;

La otra opción es pedirle a la función COUNT() que cuente específicamente el número de valores no nulos en la columna
Coach usando COUNT(Coach) :

SELECCIONE CONTEO (Entrenador)

DE Miembro;

Para recapitular: si solo queremos encontrar el número de filas devueltas por una consulta (o una tabla completa), usamos
SELECT COUNT(*) . Si queremos encontrar el número de filas que tienen un valor en una columna en particular, use SELECT
COUNT(<Column_Name>) . Las opciones COUNT(*) y COUNT(<Column_Name>) nos permiten ser específicos sobre cómo queremos
que se traten los valores nulos.

131
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Gestión de duplicados Los valores de la

columna Entrenador de la tabla Miembro (Figura 8-3 ) están duplicados. Solo hay dos valores distintos (153 y 235). Por lo tanto, tenemos dos
preguntas bastante diferentes que pueden responderse contando los valores en la columna Entrenador: "¿Cuántas personas tienen un entrenador?"
y "¿Cuántos entrenadores hay?" La respuesta a la primera pregunta requiere que incluyamos todos los valores. La respuesta a la segunda pregunta
requiere que solo contemos los valores distintos. Esto se puede hacer incluyendo la palabra clave DISTINCT como en la consulta aquí:

--No funcionará en Access (2016)


SELECCIONAR CONTEO (Entrenador DISTINTO)
DE Miembro;

Si bien trato de no ser específico de un producto en este libro, me siento obligado (dada la cantidad de copias de Access que hay en el
mundo) a señalar que Access actualmente no es compatible con COUNT(DISTINCT) . Sin embargo, puede obtener el resultado equivalente en
Access con la consulta anidada que se muestra a continuación. (Tenga en cuenta que SQL Server no permite una subconsulta en la cláusula FROM
para un agregado).

--No funcionará en SQL Server (2012)


SELECCIONE CONTEO(*)
FROM (SELECCIONE DISTINCT Coach FROM Miembro DONDE Coach NO ES NULO);

También puede utilizar la palabra clave TODOS. Esto solo refuerza que desea contar todos los valores, en lugar de solo valores distintos. Si
no incluye DISTINCT o ALL, todos los valores se incluyen de forma predeterminada.
Se pueden aplicar tipos de consultas similares a las otras tablas en la base de datos de palos de golf. Por ejemplo, podríamos querer
saber cuántos miembros de torneos ingresaron en 2015 (11) o cuántos miembros de torneos diferentes ingresaron en 2015 (5). Las dos
consultas que siguen proporcionarán las respuestas a las preguntas respectivas:

-- En cuantos torneos se inscribieron


SELECCIONE CONTEO (TourID)
DESDE Entrada
DONDE Año = 2015;

-- ¿Cuántos torneos diferentes se inscribieron?


SELECCIONE CONTEO (DISTINTO TourID)
DESDE Entrada
DONDE Año = 2015;

La función AVG() Para encontrar


promedios, usamos la función AVG() . El parámetro que va entre paréntesis (. . .), es la expresión
que queremos promediar. La expresión tiene que ser un valor numérico. Si intenta promediar un campo
de texto como Apellido obtendrá un error.
Como ejemplo, podemos encontrar el hándicap promedio para los socios de nuestro club al incluir el hándicap
columna como parámetro para la función AVG():

SELECCIONAR PROMEDIO (Hándicap)

DE Miembro;

132
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

La expresión podría ser simplemente el nombre de una de las columnas con valores numéricos como en la consulta anterior
o podría ser el resultado de un cálculo. Digamos que en otra base de datos tenemos una tabla de pedidos que incluye las columnas Precio y Cantidad
para cada artículo pedido. El valor neto de cada pedido se puede encontrar multiplicando el Precio y la Cantidad.
Si queremos encontrar el valor neto promedio para todos nuestros pedidos, podemos poner la expresión Precio *
Cantidad entre paréntesis como se ve aquí:

SELECCIONE PROMEDIO (Precio * Cantidad)


DESDE Orden;

Gestión de valores nulos Al

igual que con la función COUNT(), la función AVG() no incluye filas donde el valor de la expresión es nulo. En la tabla de miembros tenemos 20
miembros en total y 17 miembros con discapacidades. Si sumamos todos los handicaps, obtenemos 287. La función AVG() tomará el total de los
handicaps (287) y lo dividirá por el número de filas que tienen un valor no nulo en la columna Handicap (17). Esto es lo que queremos. Si incluyéramos
a los miembros sin handicap (dividiendo por el número total de filas, 20), básicamente estaríamos diciendo que estos miembros tienen un handicap de 0
por defecto. Esto sesgaría seriamente los resultados.

No siempre es tan obvio si desea que se consideren los valores nulos. Por ejemplo, supongamos que tenemos Si ingresamos puntajes de
otra base de datos con una tabla llamada Student y una columna llamada TestScore Students, y algunos de . exámenes para

los estudiantes no toman el examen, entonces tendremos un valor nulo en la columna TestScore para esos estudiantes. ¿Qué es lo que realmente
queremos para el promedio? Podríamos tomar el promedio de todos los estudiantes (dividir el puntaje total por el conteo de todos los estudiantes), lo
que significa que los estudiantes que no pasaron la prueba se cuentan efectivamente como si tuvieran un puntaje de 0. Por otro lado, podríamos tomar
el promedio de solo aquellos que participaron en la prueba (dividir por el número que tomó la prueba). AVG (TestScore) siempre nos dará solo el promedio
de aquellos que tomaron la prueba. De ninguna manera es trivial determinar cuál de las dos opciones necesita.

Existe un debate constante en las escuelas sobre si las tasas de aprobación (de las cuales puede depender la financiación) deben incluir a los
estudiantes que han abandonado los estudios en el camino.
Si queremos el promedio de todos los estudiantes, incluidos aquellos con una calificación nula (contados como 0), podemos calcular eso en
la consulta sumando las calificaciones (usando la función SUM()) y dividiendo por el número total de estudiantes como visto aquí:

SELECCIONE SUMA (Marca de prueba)/CONTAR(*)


DESDE Estudiante;

Podríamos haber introducido una nota de 0 para aquellos alumnos que no realizaron la prueba, ahorrándonos esta complicación.
Sin embargo, si hacemos eso, ya no podremos distinguir a los estudiantes que tomaron el examen y obtuvieron 0 de los estudiantes que no lo hicieron.
Independientemente de si eso es un problema o no, siempre es útil ser consciente de las implicaciones.

Gestión de duplicados Al igual que con la

función COUNT(), la función AVG() también puede incorporar las palabras clave ALL y DISTINCT. Solo tenga en cuenta que ALL (que es el valor
predeterminado) significa todos los valores no nulos, incluidos los duplicados, en lugar de solo los distintos valores no nulos. No significa
promediotomar
de todas
un las
filas (incluidas las que son nulas), como en nuestra discusión en la sección anterior. Me resulta bastante difícil encontrar ejemplos de cuándo desearía
promediar solo los valores distintos, ciertamente ninguno que se aplique a nuestra base de datos del club.

133
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Gestión de tipos y resultados La función

AVG() solo aceptará expresiones numéricas como parámetro. No podemos promediar con éxito FirstName o
JoinDate (aunque podríamos usar funciones para promediar la longitud de los nombres de los miembros o la
cantidad de días desde su fecha de ingreso).
¿Qué resultado esperamos obtener cuando promediamos las desventajas de nuestros miembros? El total de discapacidades
es 287 y el número de personas con discapacidades es 17. El resultado de la función AVG (Handicap) en SQL Server 2012 es 16. El resultado
en Access 2016 es 16.8823529411765. ¿Por qué?
En SQL Server (y algunas otras implementaciones de SQL), la función promedio devuelve el mismo tipo que los números que se
promedian. En este caso, la columna Hándicap es de tipo entero, por lo que AVG(Hándicap)
en SQL Server devuelve un número entero. También realiza una división de enteros (lo que significa que el resultado se trunca a 16 en
lugar de redondearse a 17). En Access, el promedio devuelve un número de punto flotante (es decir, uno con una parte fraccionaria).

Podemos controlar cómo se calcula el resultado. Si queremos un resultado con una parte fraccionaria para el promedio, podemos
convertir el valor de Handicap en un número de punto flotante antes de hacer el promedio. Para hacer esto podemos usar la función
1
CONVERT() que mencionamos en el Capítulo 7:

SELECCIONE PROMEDIO (CONVERTIR (FLOTANTE, Hándicap))

DE Miembro;

Otra forma de hacer esto es simplemente multiplicar el hándicap por 1,0, lo que lo convierte efectivamente en un punto flotante:

SELECCIONAR PROMEDIO (Hándicap * 1.0)


DE Miembro;

La función REDONDA()
Si bien no es estrictamente hablando una función agregada, vale la pena tomarse un momento para ver cómo realizar el redondeo.
Debido a que el promedio implica una división por el número de elementos involucrados, el AVG()
La función a menudo devolverá un resultado con muchos lugares decimales. Usamos una función de redondeo para especificar el
número de lugares decimales que nos gustaría incluir en la salida de AVG() y otras expresiones que dan como resultado números de punto
flotante. Proporcionamos a la función ROUND() dos parámetros: la expresión a redondear y el número de lugares decimales a devolver.
La siguiente declaración devuelve el hándicap promedio redondeado a dos decimales:

SELECCIONA RONDA(PROMEDIO(Hándicap * 1.0), 2)


DE Miembro;

El redondeo puede comportarse de manera diferente en diferentes implementaciones de SQL. En Access, la consulta anterior devolverá
16,88, mientras que en SQL Server devolverá 16,880000. Si bien es posible eliminar los ceros finales en SQL Server, a menudo es mejor
dejar ese tipo de formato para las herramientas de front-end, como los escritores de informes.
Hay muchas formas diferentes de realizar el redondeo, por lo que es una buena idea consultar la documentación para comprender
cómo funciona su implementación de SQL. El método tradicional de redondear hacia arriba todo lo que termina en 5 o más (por ejemplo, 4,5
se redondea a 5) genera un sesgo hacia números más altos. Para eliminar este sesgo, algunas implementaciones de funciones de redondeo
redondean al número par más cercano . Por ejemplo, 3,5 y 4,5 se redondearían a 4. Esto iguala las cosas, pero puede sorprenderte si no te
lo esperas. La función ROUND() de SQL Server redondea todos los 5 hacia arriba, mientras que Access redondea los 5 al número par más
cercano.

1 Las diferentes versiones de SQL tendrán diferentes funciones para hacer esto. En Oracle, podría considerar usar la función CAST.

134
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Otras funciones agregadas SQL también


proporciona otras funciones agregadas comunes, como SUM(), MAX() y MIN(), que son muy fáciles de
usar. Similar a la función AVG(), los argumentos de la función SUM() deben ser una expresión numérica
(ya sea un atributo numérico o alguna expresión con un resultado numérico, como Precio * Cantidad). Los
argumentos de MAX() y MIN() pueden ser de tipo numérico, de texto o de fecha. Para los tipos de texto, el orden
es alfabético. Para las fechas, el orden es cronológico. Por ejemplo, MIN(LastName) devolvería el primer valor
de LastName en orden alfabético, mientras que MAX(JoinDate) devolvería el valor más reciente de JoinDate
.

Es posible combinar varias funciones agregadas en una consulta. La siguiente consulta devuelve el
valores máximo, mínimo y promedio para Handicap.

SELECCIONE MAX(Handicap) COMO máximo, MIN(Handicap) COMO mínimo,


ROUND(AVG(Handicap * 1.0),2) COMO promedio DE Miembro;

Proporcionar un alias para el resultado de cada columna con una cláusula AS ayuda a que el resultado sea más fácil de
comprender. La Figura 8-4 muestra una salida típica.

Figura 8-4. Resultado típico de una consulta con varias funciones agregadas

Agrupamiento
Si queremos saber cuantas veces un miembro en particular ha ingresado a torneos podemos consultar la Entrada
mesa. Por ejemplo, si quisiéramos saber cuántas veces el miembro 235 ha ingresado a torneos, podríamos seleccionar todas las filas en la tabla Entrada
para ese miembro y contarlas como en la siguiente consulta:

SELECCIONE CONTEO(*) COMO NúmEntradas


DESDE Entrada
DONDE ID miembro = 235;

Si queremos encontrar el número de entradas para un miembro diferente, necesitaríamos reescribir la consulta con
una cláusula WHERE diferente. Si queremos encontrar los recuentos de todos los miembros, sería muy tedioso.
La agrupación nos permite encontrar los recuentos de todos los miembros utilizando una instrucción SQL. La frase clave GROUP BY se usa
para hacer esto. Echa un vistazo a la siguiente consulta:

SELECCIONE CONTEO(*) COMO NúmEntradas


DESDE Entrada
GRUPO POR ID de miembro;

La cláusula adicional GROUP BY dice: "En lugar de solo contar todas las filas en la tabla Entry, cuente todos los subconjuntos o grupos con
el mismo ID de miembro". La figura 8-5 ilustra cómo podemos visualizar lo que está sucediendo.

135
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

También podemos incluir los campos por los que estamos agrupando en la cláusula SELECT para que podamos ver qué cuenta
pertenecen a qué entradas, como en la consulta aquí:

SELECCIONE MemberID, COUNT(*) AS NumEntries


DESDE Entrada
GRUPO POR ID de miembro;

Figura 8-5. Contando las filas en la tabla de entrada agrupadas por MemberID

El resultado de esta consulta se muestra en la Figura 8-6 .

136
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Figura 8-6. Incluyendo el MemberID en la salida

Podríamos preferir ver los nombres de los miembros en la salida de la Figura 8-6 . En este caso, tenemos que
primero únase a la tabla de Entrada con la tabla de Miembros, y luego agrupe y cuente.

SELECT m.MemberID, m.LastName, m.FirstName, COUNT(*) AS NumEntries FROM Entry


e INNER JOIN Member m ON m.MemberID = e.MemberID GROUP BY m.MemberID,
m.LastName, m.FirstName;

La salida se muestra en la Figura 8-7 .

Figura 8-7. Unirse a las tablas Entrada y Miembro y agruparse por ID y nombres

Quizás se pregunte por qué hemos incluido Apellido y Nombre en la cláusula GROUP BY en la cláusula SELECT
consulta anterior. Cuando está utilizando GROUP BY , solo puede incluir los campos que está
agrupando por o los agregados. Si queremos ver los nombres en la salida, debemos incluirlos en los campos por los que
estamos agrupando. Esto protege contra los casos en los que puede haber diferentes nombres para un ID de miembro
(claramente imposible en este caso, ya que el ID de miembro es la clave principal de la tabla de miembros). Dejando esto de
, sería
lado por ahora, si hubiera dos filas con un MemberID de 118 con dos nombres diferentes, entonces si agrupamos solo por
MemberID no sería posible determinar qué nombre asociar con el conteo en la salida de la Figura 8-7 .
Podemos obtener una variedad de información diferente de nuestros datos usando GROUP BY si incluimos cláusulas
WHERE y diferentes atributos en la cláusula GROUP BY. Echemos otro vistazo a la tabla Entrada. Si queremos encontrar el
número de entradas para cada torneo, imaginamos agrupar todas las filas con el mismo TourID y luego contar las filas en cada
conjunto, como en la consulta aquí:

SELECCIONE TourID, COUNT(*) AS NumEntries


DESDE Entrada
GRUPO POR TourID;

137
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

La salida se muestra en la Figura 8-8 .

Figura 8-8. Contando el número de entradas en cada torneo

No siempre necesitamos contar todas las filas de la tabla. Quizá nos gustaría seleccionar un subconjunto de las filas primero.
Por ejemplo, es posible que solo queramos recopilar las estadísticas de la Figura 8-8 solo para el año 2014. La siguiente consulta
muestra el SQL para hacer esto. Tenga en cuenta que la cláusula WHERE (que encuentra el subconjunto de las filas que queremos
considerar) debe ir antes de la cláusula GROUP BY:

SELECCIONE TourID, COUNT(*) AS NumEntries


DESDE Entrada
DONDE Año = 2014
GRUPO POR TourID;

Al agregar más campos en la cláusula GROUP BY, podemos obtener información más detallada. Si queremos repetir esta
consulta para cada torneo de cada año, podemos eliminar la cláusula WHERE y agrupar por Año
y TourID:

SELECCIONE TourID, Año, COUNT(*) AS NumEntries


DESDE Entrada
GRUPO POR TourID, Año;

La Figura 8-9 muestra cómo funciona la agrupación en ambos campos.

138
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Figura 8-9. Agrupación por TourID y Año

Podemos usar la agrupación con funciones agregadas que no sean COUNT() . Por ejemplo, si queremos ver el hándicap máximo, mínimo
y promedio para mujeres y hombres, podríamos usar una consulta como la siguiente:

SELECCIONE Género, MIN(Hándicap) como Mínimo, Max(Hándicap) como Máximo,


RONDA(PROMEDIO(Hándicap),1) AS Promedio
DE Miembro

AGRUPAR POR Género;

139
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

El resultado de esta consulta se muestra en la Figura 8-10 .

Figura 8-10. Agrupación de agregados para Handicap por Género

Filtrado del resultado de una consulta agregada


Una vez que hayamos calculado algunos agregados para grupos de filas, es posible que queramos hacer algunas preguntas sobre los
resultados. Por ejemplo, en la Figura 8-9 la ,pregunta
tenemoses
el número
"¿Qué torneos
de entradas
tuvieron
en tres
cadaotorneo
más entradas?"
en cada año.
Mirando
un probable
la tabla de resultados en
, nosotroscon
la Figura 8-9 , queremos seleccionar solo aquellas filas en la salida agregada con un conteo mayor o igual a 3. Podemos hacer esto
la palabra clave HAVING. Echa un vistazo a la siguiente consulta:

SELECCIONE TourID, Año, COUNT(*) AS NumEntries


DESDE Entrada
GRUPO POR TourID, Año
TENIENDO CUENTA(*) >= 3;

La cláusula HAVING siempre viene después de una cláusula GROUP BY. Primero se realiza el agregado y la agrupación, y
se seleccionan las filas de salida que cumplan alguna condición (en este caso, COUNT(*) >= 3 ). Es como tener una cláusula WHERE
que actúa sobre los números agregados. Como un pequeño aparte, debemos usar COUNT(*) en la cláusula HAVING; no podemos usar
el alias NumEntries de la primera línea de la declaración. Este alias solo se usa al final de la consulta para etiquetar la columna de salida.

Veamos otro ejemplo. Digamos que queremos encontrar aquellos miembros que hayan ingresado a cuatro o más torneos.
En primer lugar, construya un conjunto de filas con los miembros y los recuentos de los torneos en los que han participado, como en
las primeras tres líneas de la consulta que sigue. Luego usamos la cláusula HAVING para seleccionar solo esas filas del resultado con
COUNT(*) >= 4 :

SELECCIONE MemberID, COUNT(*) AS NumEntries


DESDE Entrada
GRUPO POR ID de miembro

TENIENDO CUENTA(*) >= 4;

Tenemos dos oportunidades para seleccionar un subconjunto de filas en consultas que involucran agregados. Si tomamos el
subconjunto antes de hacer la agregación, usamos una cláusula WHERE. Cuando queremos seleccionar solo algunas filas después
la agregación, usamos una cláusula HAVING. Por ejemplo, cambiemos la consulta anterior para averiguar qué miembros han
ingresado a más de cuatro torneos abiertos. Para encontrar los torneos abiertos, necesitamos hacer lo siguiente:

1. Une la mesa de Entrada con la mesa de Torneo.

2. Tome solo el subconjunto de entradas para torneos abiertos (con una cláusula WHERE).

3. Agrupa las entradas de cada miembro y cuéntalas.

4. Tome la tabla agregada resultante y recupere solo esas filas con un conteo
mayor que 4 (con cláusula HAVING).

140
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

El proceso se ilustra en la Figura 8-11 .

Figura 8-11. Encontrar miembros que hayan ingresado a más de cuatro torneos abiertos

La consulta para el proceso en la Figura 8-8 es:

SELECT MemberID, COUNT(*) AS NumEntries FROM


Entry e INNER JOIN Tournament t ON e.TourID = t.TourID WHERE t.TourType =
'Open'
GRUPO POR ID de miembro

TENIENDO CUENTA(*) > 4;

En el Capítulo 2 vimos cómo ordenar la salida de una consulta. También podemos ordenar la salida por el agregado.
Si quisiéramos ver los resultados de la consulta anterior en orden descendente de la cantidad de torneos ingresados,
podríamos agregar una cláusula ORDER BY COUNT(*) DESC al final de la consulta.

141
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Uso de agregados para realizar operaciones de división


En el Capítulo 7, vimos la división de operaciones de álgebra. En resumen, la división nos permite responder muchas
preguntas que contienen las palabras todos o todos . Por ejemplo, digamos que queremos encontrar a los miembros
que han ingresado a todos los torneos. La figura 8-12 repasa cómo podemos usar la división para hacer esto. El atributo
que queremos devolver en nuestra Respuesta es de
conjunto MemberID. En verificar
cosas para el lado derecho
(en estedel operador
caso, de de
una lista división, tenemos
todos los el de
valores
TourID proyectados desde la tabla de torneos). En el lado izquierdo del operador de división hay una tabla que tiene los
atributos de Respuesta y Verificación (en este caso, las columnas MemberID y TourID de la tabla Entrada). El resultado
de la división es una lista de los valores de MemberID que aparecen con cada torneo (en este caso, solo el miembro con ID
415).
Esta figura es la misma que la Figura 7-25.

Figura 8-12. Usando la división para encontrar los miembros que han ingresado a cada torneo

Actualmente, las implementaciones estándar de SQL no tienen una palabra clave para la operación de división,
por lo que necesitamos encontrar otras formas de expresar una consulta como la que se muestra en la figura 8-12.
Vimos una forma en el Capítulo 7 y algunas otras en el Apéndice 2. Aquí, veremos un método que usa agregados.
La tabla de torneos enumera cinco torneos diferentes. Si podemos encontrar un miembro que haya ingresado
cinco torneos diferentes , entonces él o ella debe haber entrado en todos ellos. Ahora tenemos la capacidad de usar
agregados y agrupaciones para construir el equivalente de una operación de división.

142
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Ya hemos visto consultas para contar en cuántos torneos ha entrado cada miembro. Sin embargo,
ahora queremos contar solo los diferentes torneos en los que participó cada miembro. Agregando el DISTINTO
La palabra clave en la función COUNT() logrará esto:

SELECCIONE MemberID, COUNT (DISTINCT TourID) AS NumTours


DESDE Entrada e
GRUPO POR ID de miembro;

El resultado de esta consulta se muestra en la Figura 8-13 .

Figura 8-13. Encontrar el número de torneos distintos inscritos por cada miembro

De la tabla resultante en la Figura 8-13 , ahora queremos solo aquellas filas donde NumTours es igual al número de torneos
distintos, que es 5 en este caso. Podemos usar la cláusula HAVING para encontrar aquellos miembros que hayan ingresado a cinco torneos diferentes:

SELECCIONE ID de miembro

DESDE Entrada e
GRUPO POR ID de miembro

TENIENDO CONTEO (DISTINTO TourID) = 5;

Podemos hacer esta consulta más general reemplazando 5 con una expresión para calcular el número de
distintos torneos sobre la marcha:

SELECCIONE ID de miembro

DESDE Entrada e
GRUPO POR ID de miembro

TENIENDO CONTEO (DISTINTO TourID) =


(SELECCIONE CONTEO (DISTINTO TourID) DEL Torneo);

Esta consulta es equivalente a la operación de división de álgebra como se muestra en la Figura 8-12. Devuelve las identificaciones de los
miembros que han ingresado a cada torneo. Para resumir, contamos el número de torneos distintos en los que ha participado cada miembro y luego,
utilizando la cláusula HAVING, retenemos solo aquellos cuyo recuento es igual al número de torneos posibles (un recuento distinto de la tabla de Torneos).
Encuentro este método de hacer una división conceptualmente más sencillo que los del Capítulo 7 y el Apéndice 2. Sin embargo, todos los métodos
logran el mismo objetivo.

143
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Consultas anidadas y agregados


Ya hemos cubierto ligeramente las consultas anidadas y los agregados en el Capítulo 4. Es útil revisar esta idea aquí. En este
capítulo, hemos visto cómo encontrar promedios, totales, conteos, etc. Ahora podemos usar estos resultados agregados en otras
consultas. Por ejemplo, podríamos querer encontrar a todas las personas con una discapacidad mayor que la discapacidad promedio.
Considere la siguiente consulta:

SELECCIONE * DE Miembro
DONDE Hándicap >
(SELECCIONE PROMEDIO (Hándicap)
DE miembro);

La parte interna de la consulta devuelve el hándicap promedio y la parte externa de la consulta compara el
hándicap de cada miembro con esa media.
Probemos otra cosa. ¿Qué hay de encontrar miembros que hayan ingresado a más de tres torneos?
Si su mente se queda en blanco, puede volver al enfoque de resultados de imaginar las tablas y averiguar cómo se verán las
filas que desea que se devuelvan. La figura 8-14 muestra cómo puede visualizar la consulta.

Figura 8-14. ¿Qué miembros tienen más de tres entradas en torneos?

Podemos describir los miembros que queremos que se devuelvan así:

Encuentre todas las filas m de la tabla de miembros donde si contamos el número de filas e de la
tabla de entrada para ese miembro (m.MemberID = e.MemberID) el conteo es > 3.

Esto se convierte en SQL de una manera sencilla, como se muestra aquí:

SELECCIONE * DE Miembro m
DÓNDE

(SELECCIONE CONTEO (*)


DESDE Entrada
e DONDE e.MemberID = m.MemberID) > 3;

144
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

¿Qué tal algo un poco más complejo? ¿Cómo encontramos el número promedio de torneos en los que participan los miembros?
Necesitaremos la función AVG(), pero ¿qué estamos tratando de promediar? Queremos contar el número de torneos de cada miembro y luego
promediar esos recuentos.
Podemos usar la agrupación, como se describe en la sección anterior, para encontrar el número de torneos inscritos por cada miembro:

SELECT MemberID, COUNT (*) AS CountEntries FROM Entrada


GRUPO POR ID de miembro

El resultado de la consulta anterior se muestra en la Figura 8-15 .

Figura 8-15. Número de entradas para cada miembro

Ahora queremos encontrar el promedio de la columna CountEntries. Como primer intento, parece razonable usar
nuestra consulta anterior como la parte interna de una consulta anidada y luego intentar encontrar el promedio:

--No funcionará en algunas implementaciones


SELECCIONAR PROMEDIO (CountEntries) DESDE
(SELECCIONE MemberID, COUNT (*) AS CountEntries FROM Entrada
GRUPO POR ID de miembro);

Sin embargo, muchas versiones de SQL no admiten una consulta anidada en una cláusula FROM. La consulta anterior funciona bien en
Access 2013, pero no en otras implementaciones de SQL.
Encontramos un problema similar en el capítulo anterior cuando queríamos unir una tabla con el
resultado de una unión. Simplemente le damos al resultado de la consulta interna un alias (por ejemplo, NewTable). Esto crea una tabla
virtual temporal (a menudo denominada tabla derivada ). Luego podemos acceder a los atributos de la nueva tabla virtual como aquí:

SELECT PROMEDIO (NewTable.CountEntries) FROM


(SELECT MemberID, COUNT (*) AS CountEntries FROM Entry GROUP BY
MemberID)AS NewTable;

145
Machine Translated by Google

CAPÍTULO 8 ÿ OPERACIONES AGREGADAS

Resumen
Las funciones agregadas nos brindan los medios para responder una gran variedad de preguntas sobre nuestros datos. He aquí un resumen
de algunos de los puntos principales de este capítulo.
La mayoría de las versiones de SQL ofrecerán las funciones agregadas simples MIN() , MAX() , COUNT() , SUM() y AVG() .

• Para COUNT() , a menudo solo desea contar las filas devueltas por una consulta. Esto se puede hacer incluyendo
un asterisco entre paréntesis: COUNT(*) . Si incluye un nombre de columna entre
paréntesis (p. ej., COUNT(Handicap) ), solo los valores no nulos de esa columna se incluirán en el recuento.

• Para los otros agregados comunes, debe incluir un nombre de campo. Para AVG() y SUM(), debe ser una
expresión numérica, como AVG(Handicap) .

Nulos y duplicados:

• Los valores nulos no se incluyen al calcular los agregados. Por ejemplo, AVG(Handicap) es
la suma de los handicaps dividida por el número de filas que tienen un valor no nulo para Handicap .

• De forma predeterminada, todos los valores no nulos se incluyen en los agregados. Puede incluir la palabra
clave DISTINCT para eliminar los duplicados. Por ejemplo, COUNT(DISTINCT Handicap)
contará el número de valores diferentes que aparecen en la columna Hándicap.

Agrupamiento:

• La cláusula GROUP BY se puede utilizar para recopilar filas con el mismo valor de algunos
expresión juntos y luego aplicar los agregados a esos grupos. Por ejemplo, podemos encontrar el número
de torneos en los que ha participado cada miembro agrupando todas las filas de la tabla Entrada con el
mismo valor de MemberID (p. ej., SELECT MemberID, COUNT(*) FROM Entry GROUP BY MemberID ).

• Después de haber agrupado y realizado un agregado, puede seleccionar filas de la tabla resultante utilizando
la palabra clave HAVING. Por ejemplo, podemos encontrar
torneosmiembros
agregando
quelahayan
cláusula
ingresado
HAVINGa COUNT(*)
tres o más
>= 3 a la expresión del ítem anterior.

• Use DONDE para seleccionar un subconjunto de filas antes de agrupar y agregar. Usar TENER
para seleccionar un subconjunto de filas después de agrupar y agregar.

Agregados más complejos:

• Use tablas derivadas donde desee anidar agregados, como para encontrar el promedio de
cuenta Simplemente asigne un alias a la consulta interna.

• Comparar conteos de filas para hacer el equivalente de la división relacional.

146
Machine Translated by Google

CAPÍTULO 9

Funciones de ventana

Las funciones de ventana se agregaron a SQL estándar en 2003 y brindan una capacidad adicional considerable para
manejar la agregación. Las funciones de ventana nos permiten realizar agregados en una "ventana" de datos en función de la
fila actual. Esto proporciona una forma elegante de especificar consultas para realizar acciones como clasificación, totales
acumulados y promedios móviles. Las funciones de ventana también brindan una flexibilidad considerable cuando se trata de
agrupar datos para su agregación, ya que permiten que una sola consulta tenga varios grupos o particiones diferentes.
También es posible hacer referencia a los datos que contribuyen al agregado desde dentro de la consulta. Esto permite que los
datos subyacentes se comparen con el agregado.
Oracle y Postgres han admitido funciones de ventana durante muchos años, mientras que SQL Server las introdujo en 2012.
Access y MySQL actualmente no admiten estas funciones. Este capítulo describe cómo utilizar algunas de las funciones de ventana
más comunes.

Agregados simples
Para comenzar con las funciones de ventana, las usaremos para escribir consultas alternativas para algunos de los
agregados simples que encontramos en el Capítulo 8. Reconsideremos una consulta agregada simple para contar y promediar
las desventajas de los miembros:

SELECCIONE COUNT(Hándicap) COMO Recuento, AVG(Hándicap * 1.0) como


Promedio DE Miembro;

El resultado de la consulta se muestra en la Figura 9-1 .

Figura 9-1. Salida para conteo simple y promedio de handicaps

Con agregados simples, los únicos atributos permitidos en la cláusula SELECT son el agregado y los atributos incluidos
en una cláusula GROUP BY. Esto significa que ya no tenemos acceso a las desventajas individuales que contribuyen a los
resultados.
Las funciones de ventana nos permiten recuperar los datos subyacentes junto con los agregados. La palabra clave para
funciones de ventana es OVER() ; a veces también se las denomina funciones superiores .
Aquí hay una consulta similar a la anterior usando la función OVER():

SELECT MemberID, LastName, FirstName, Handicap,


COUNT(Handicap) OVER() AS Count, AVG(Handicap *
1.0) OVER() as Average FROM Member;

© Clare Churcher 2016 147


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_9
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

A diferencia de la función simple COUNT(), con la función OVER() podemos incluir campos adicionales
en la cláusula SELECT. En la consulta anterior, hemos incluido cuatro campos de datos detallados sobre cada miembro
junto con los dos agregados (que están sangrados en líneas nuevas para que sean más fáciles de leer).
Los agregados son iguales a los agregados simples pero incluyen la función OVER().
Parte del resultado de la consulta anterior se muestra en la Figura 9-2 . El recuento y la media de los handicaps
aparecen con los datos detallados de cada miembro.

Figura 9-2. Salida cuando se usa OVER() para contar y promediar handicaps

Si bien no parece particularmente útil en el ejemplo de la Figura 9-2 tener los agregados devueltos para cada fila, abre
la puerta a algunas consultas nuevas. Ahora podemos comparar fácilmente el hándicap de cada individuo con el promedio,
algo que no era nada sencillo sin las funciones de ventana. En la tercera línea de la siguiente consulta, restamos el promedio
del hándicap del hándicap de cada miembro y lo incluimos en la cláusula SELECT:

SELECCIONE MemberID, LastName, FirstName, Handicap,


AVG(Handicap * 1.0) OVER() COMO Promedio, Handicap
- AVG(Handicap *1.0) OVER() COMO Diferencia DE Miembro;

El resultado se muestra en la Figura 9-3 .

Figura 9-3. Las funciones de ventana nos permiten comparar agregados con valores de detalle

148
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Particiones
La función OVER() también se puede usar para producir consultas similares a las consultas GROUP BY que vimos en el capítulo anterior. La
frase clave que necesitamos aquí es PARTITION BY . Probemos algunos recuentos diferentes
función
en lasOVER()
filas decon
la tabla
nuestra
Entrada.
función
Si COUNT(*),
usamos solo la
contaremos todas las filas, mientras que si usamos OVER(PARTITION BY TourID) contará las filas para cada valor diferente de TourID
.

El verdadero poder de la partición es que, a diferencia de la cláusula GROUP BY para agregados simples, es posible tener varias
particiones diferentes en una sola consulta. Esto se explica mejor con un ejemplo. La siguiente consulta incluye tres recuentos diferentes:

SELECCIONE MemberID, TourID, Año,


COUNT(*) OVER() como CountAll,
CONTAR(*) SOBRE(PARTICIÓN POR TourID) COMO CountTour,
COUNT(*) OVER(PARTITION BY TourID, Year) AS CountTourYear
DESDE Entrada;

La salida se muestra en la Figura 9-4 .

Figura 9-4. Usando diferentes particiones en una sola consulta

149
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

En la Figura 9-4 , la columna CountAll muestra el resultado de COUNT(*) OVER() , que cuenta cada fila en la tabla Entry (24).

La columna CountTour es el resultado de COUNT(*) OVER(TourID) , que divide (o agrupa) las filas
con el mismo valor de TourID y luego los cuenta. Los tres conjuntos superiores de cuadros sólidos en la Figura 9-4 muestran las filas que
contribuyen a CountTour para TourID de 24, 25 y 36.
La columna CountTourYear es el resultado de COUNT(*) OVER(TourID, Year) y divide todas las filas con los mismos valores para
TourID y Year . El conjunto de cuadros discontinuos hacia la parte inferior de la Figura 9-4 muestra ejemplos de cómo se evalúan estos
recuentos.

Ordenar por cláusula


La función OVER() puede incluir una cláusula ORDER BY. Esto especifica un orden para que se visiten las filas cuando se evalúan los
agregados. Tener un orden para las filas proporciona un mecanismo para realizar operaciones de clasificación y totales acumulados.

Agregados acumulativos Si se incluye una

cláusula ORDER BY en la función OVER() entonces, por defecto, el agregado se lleva a cabo desde el comienzo de la partición hasta la fila
actual (pero vea más abajo para una definición más precisa).
Echa un vistazo a la siguiente consulta:

SELECCIONE MemberID, TourID, Año,


COUNT(*) OVER(ORDER BY Year) COMO Acumulativo
DESDE Entrada;

En la tabla Entrada tenemos varias filas con el mismo valor de Año (como puede ver en la Figura 9-5 ). En lo que respecta al orden,
estas filas son equivalentes, por lo que si una de ellas se incluye en un conteo, entonces deberíamos incluirlas todas. Ahora corregiré la
definición de qué filas se incluyen en el agregado.
Si se incluye una cláusula ORDER BY en la función OVER() entonces, por defecto, el agregado se lleva a cabo desde el comienzo
de la partición hasta la fila actual e incluye las filas siguientes con el mismo valor de la expresión de ordenación.

El resultado de la figura 9-5 ilustra lo que esto significa.

150
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Figura 9-5. Usar ORDER BY para producir un recuento acumulativo para cada año

Figura 9-5 , las filas están ordenadas por filas de año.. Para
Veamos
la primera
cómo funciona
fila, si contamos
este conteo
desde
acumulativo
el principiopara
de la
lostabla
primeros.
tenemos
En 1
la fila.
Sin embargo, las siguientes cinco filas tienen el mismo valor para nuestra expresión de ordenación Año, por lo que las incluimos en el
conteo, lo que nos da un total de 6. ,
Ahora bajemos a la primera fila para el miembro 258. Contando desde el comienzo de la tabla, tenemos 10 filas, pero las siguientes
3 filas tienen el mismo valor de Year . Esto hace un total de 13.
Esencialmente, tenemos un recuento acumulativo de entradas para cada año. Tenemos 6 entradas en el primer año (cuadros
sólidos), y para el segundo año tenemos 7 entradas adicionales para hacer un total de 13 (cuadros discontinuos).
La función SUM() funciona de la misma manera si hay una cláusula ORDER BY en la función OVER() para darnos totales
acumulados.
Digamos que el club recopila datos sobre los ingresos de la recaudación de fondos y los torneos en una tabla llamada Ingresos .
La figura 9-6 muestra los ingresos de los primeros seis meses.

151
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Figura 9-6. Tabla de ingresos

Podemos encontrar un total acumulado de los ingresos realizando una SUMA (Ingresos) con una cláusula ORDEN POR Mes
en la función OVER(), como en la siguiente consulta:

SELECCIONE Mes, Ingresos,


SUMA (Ingresos) SOBRE (ORDEN POR Mes) COMO RunningTotal
DE Ingresos;

Los ingresos se suman desde el comienzo de la tabla hasta la fila actual (cuando se ordenan por el valor
del mes), como se muestra en la Figura 9-7 .

Figura 9-7. Totales acumulados de ingresos mensuales ordenados por mes

Clasificación Otro

uso más de la cláusula ORDER BY es con la función RANK(). Como ejemplo, clasificaremos a los miembros del club por su handicap.
Echa un vistazo a la siguiente consulta:

SELECCIONE ID de miembro, discapacidad,


RANK() SOBRE (ORDEN POR Hándicap) AS Rank
DE Miembro

DONDE Hándicap NO ES NULO;

152
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

La cláusula ORDER BY en la función OVER() especifica el orden de las filas al determinar el rango; en este caso, el valor de
Handicap. Cada vez que cambia el valor de Handicap, el rango se convierte en el número de fila en la partición (en este caso, toda la
tabla ordenada por Handicap). El rango permanece igual hasta que cambia el valor de Handicap, como se muestra en la Figura 9-8 .
(Algunas de las desventajas se han cambiado para ilustrar el proceso más claramente. Se han delineado filas con el mismo valor de
desventaja y, por lo tanto, rango).

Figura 9-8. Resultado de la función RANK() ordenado por hándicap. Las filas con el mismo valor de hándicap tienen el mismo rango

La primera fila de la figura 9-8 tiene rango 1 (¡es la primera fila!). La segunda fila tiene el mismo valor de la orden
expresión ( Handicap ) como la fila anterior, por lo que también tiene el rango 1. En la siguiente fila, el valor de Handicap ha cambiado,
por lo que el rango se convierte en el número de fila (3).
Los valores nulos se incluirán en el ranking, por lo que se han excluido explícitamente en la consulta anterior. Sin la cláusula
WHERE, los valores nulos se habrían incluido en la parte superior de la clasificación (o en la parte inferior si el orden hubiera sido DESC).

Combinación de pedidos con particiones


En las secciones anteriores sobre pedidos, no incluí ninguna partición en las consultas. Ahora que (con suerte) entendemos el concepto,
podemos ver algunos ejemplos más.
Consideremos una tabla de ingresos más detallada que tiene montos mensuales para cada una de las tres áreas donde el
club de golf lleva a cabo la recaudación de fondos. Los datos de los primeros cinco meses del año se muestran en la Figura 9-9 .

153
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Figura 9-9. Tabla de ingresos incluyendo áreas

Construiremos algunas consultas lentamente.


Primero, solo calcularemos el ingreso total de la tabla. Podríamos usar un agregado SUM() simple, pero
incluirá una función OVER() para que podamos mantener los detalles en la salida:

SELECCIONE Mes, Área, Ingreso,


SUMA (Ingresos) SOBRE () COMO Total
DE Ingresos;

Esto producirá una tabla igual a la de la Figura 9-9 pero con una columna adicional, Total tiene el total general , que lo hará
para cada fila.
Ahora vamos a cambiar esto a un total acumulado. Hacemos esto incluyendo una cláusula ORDER BY en OVER()
función. De forma predeterminada, esto calcula el total de los valores desde el comienzo de la tabla hasta la fila actual y las siguientes filas
con el mismo valor de Mes. la consulta es:

SELECCIONE Mes, Área, Ingreso,


SUMA (Ingresos) SOBRE (PEDIDO POR MES) COMO RunningTotal
DE Ingresos;

Los ingresos se suman desde la parte superior de la tabla hasta la fila actual, incluidas las siguientes filas con el mismo valor
de Mes (el atributo por el que estamos ordenando). Esencialmente, la salida suma todos los valores de cada mes y luego acumula los
totales mes a mes. La salida se muestra en la Figura 9-10 . Los diferentes meses han sido delineados.

154
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Figura 9-10. Total acumulado al ordenar por mes

Ahora veamos las áreas de forma independiente. Esto requiere una cláusula PARTITION BY. Considere esta consulta:

SELECCIONE Mes, Área, Ingreso,


SUMA(Ingreso) SOBRE(
PARTICIÓN Por Área
ORDEN POR MES) COMO AreaRunningTotal
DE Ingresos;

La cláusula PARTITION BY debe ir antes de la cláusula ORDER BY, que refleja lo que está sucediendo.
Primero particionamos los datos y luego ordenamos dentro de las particiones. El agregado se calcula para las filas desde el
comienzo de la partición actual hasta la fila actual. La figura 9-11 muestra el resultado de solo los primeros cinco meses del año.
Las tres particiones se han delineado para que sea más fácil ver lo que está sucediendo.

155
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Figura 9-11. Totales acumulados de ingresos divididos por área y ordenados por mes

Enmarcado
La última característica de las funciones de ventana que veremos es la capacidad de especificar qué filas se incluyen en
un agregado. Así es como surgieron las funciones de la ventana de nombres . Proporcionan una ventana
o marco en la sección de datos que nos interesa. La forma general de la función OVER() tiene tres cláusulas, como se
muestra aquí:

SOBRE(
[ <cláusula PARTICIÓN POR> ]
[ <cláusula ORDER BY> ]
[ <cláusula ROWS> ] );

Ya hemos visto dos de estas cláusulas: La cláusula PARTITION BY nos permite agrupar los datos por alguna expresión
antes de agregarlos. La cláusula ORDER BY nos permite determinar el orden en que la función agregada atraviesa las filas
dentro de una partición y nos permite realizar clasificaciones y totales acumulados.
Una cláusula ROWS nos permite reducir el conjunto de filas, en relación con la fila actual, que se incluirán en el agregado.

De forma predeterminada, una consulta con una cláusula OVER(ORDER BY) calcula el agregado de los valores de la
desde el principio de la partición actual hasta la fila actual inclusive. Recapitulemos con una consulta que calcula un
promedio móvil para cada área:

SELECCIONE Mes, Área, Ingreso,


PROMEDIO (Ingresos) SOBRE (
PARTICIÓN POR ÁREA

ORDEN POR Mes) COMO AreaRunningAverage


DE Ingresos;

156
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

El resultado de la figura 9-12 es solo para el área de Halswell. Los cuadros sólidos muestran qué filas están incluidas en el promedio
de la tercera fila desde arriba. Los cuadros discontinuos muestran las filas que contribuyen al promedio de la tercera fila desde la parte inferior
de la imagen. Si no hay una cláusula ROWS después de una cláusula ORDER BY, este es el comportamiento predeterminado.

Figura 9-12. Promedio móvil para la tabla de ingresos

La sintaxis de la cláusula ROWS es:

FILAS ENTRE <comienzo del cuadro> Y <final del cuadro>

La Tabla 9-1 muestra algunas expresiones para especificar <start of frame> y/o <end of frame> .
Recuerde que siempre tenemos que tener una cláusula ORDER BY si estamos usando la cláusula ROWS.

Tabla 9-1. Especificación de filas de una ventana

Expresión Sentido
PRECEDENTE ILIMITADO Empezar al principio de la partición actual

<n> PRECEDENTE Empezar n filas antes de la fila actual

FILA ACTUAL Se puede utilizar para el inicio o el final del cuadro.

<m> SIGUIENTE Terminar m filas después de la fila actual

SIGUIENTE ILIMITADO Fin al final de la partición actual

Aquí está la consulta anterior con la ventana (predeterminada) de filas requeridas detalladas:

SELECCIONE Mes, Área, Ingreso,


PROMEDIO(Ingreso) SOBRE(
PARTICIÓN POR ÁREA
ORDENAR POR Mes
FILAS ENTRE LA FILA ACTUAL Y LA PRECEDENTE ILIMITADA

) AS AreaRunningAverage
DE Ingresos;

157
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

La cláusula ROWS de la consulta anterior es la predeterminada si no se especifica ninguna cláusula ROWS después de una cláusula
ORDER BY.

Ahora podemos cambiar qué filas se incluirán en el promedio. Digamos que nos gustaría ver rodar tres
promedios mensuales. Esto significa que para cada mes tomamos un promedio que incluye el mes actual, el anterior y el siguiente. La
siguiente consulta muestra cómo podemos agregar otra cláusula ROWS a la consulta anterior para ver tanto el promedio móvil como el
promedio móvil de tres meses:

SELECCIONE Mes, Área, Ingreso,


PROMEDIO(Ingreso) SOBRE(
PARTICIÓN POR ÁREA
ORDENAR POR Mes
FILAS ENTRE LA FILA ACTUAL Y LA PRECEDENTE ILIMITADA

) AS AreaRunningAverage,
PROMEDIO(Ingreso) SOBRE(
PARTICIÓN POR ÁREA
ORDENAR POR Mes
FILAS ENTRE 1 PRECEDENTE Y 1 SIGUIENTE

) AS Area3MesPromedio
DE Ingresos;

La figura 9-13 muestra el resultado de esta consulta. Los cuadros muestran qué valores contribuyen a los promedios en las filas
del mes 4 (cuadros sólidos) y el mes 9 (cuadros discontinuos).

Figura 9-13. Promedios móviles y promedios móviles de tres meses

El promedio móvil en la fila del mes 4 incluye todos los valores desde el principio hasta el mes 4 y, de manera similar, el promedio móvil
en la fila del mes 9 incluye todos los ingresos hasta el mes 9 inclusive.
El Rolling3MonthAverage en la fila 4 incluye los meses 3 a 5 (un mes anterior y un mes posterior a la fila actual). En la fila 9, Rolling3MonthAverage
promedia los meses 8 a 10 (es decir, un mes a cada lado del mes 9).

158
Machine Translated by Google

CAPÍTULO 9 ÿ FUNCIONES DE VENTANA

Los diferentes promedios brindan información diferente sobre cómo le está yendo al negocio. El promedio móvil
proporciona el ingreso promedio hasta la fecha para el año. El promedio móvil de tres meses da una mejor idea de cómo
se están registrando los ingresos en este momento. Los últimos valores en la columna de promedio móvil son más altos
que sus contrapartes de promedio móvil porque no incluyen los valores más bajos en los primeros meses.

Resumen
Las funciones de ventana brindan una forma elegante de realizar particiones, ejecutar y rotar agregados y permitir
que tanto el detalle como el agregado estén disponibles en la misma consulta.
Aquí hay un breve resumen de la funcionalidad cubierta en este capítulo. He usado la tabla de palabras en las
descripciones, pero la funcionalidad se aplica igualmente al resultado de una consulta.

SOBRE()
Utilice la función OVER() sin cláusulas entre paréntesis para calcular el agregado de toda la tabla.
A diferencia de los agregados simples, es posible incluir otros atributos en la cláusula SELECT, conservando así el
acceso al detalle así como al valor agregado.

SOBRE(PARTICIÓN POR <…>)


Si se incluye PARTITION BY en la función OVER(), las filas se separan en grupos que tienen el mismo valor para la
expresión de partición. Los agregados se realizan para cada partición. Esto es similar a GROUP BY para un agregado
simple, pero tiene la ventaja de que se pueden incluir varias particiones diferentes en una sola consulta.

SOBRE(ORDENAR POR <…>)


Cuando se incluye ORDER BY en la función OVER(), la tabla se ordena (prácticamente) por orden por expresión. A
continuación, se evalúa el agregado para las filas desde el principio de la tabla hasta la fila actual (y las filas
siguientes con el mismo valor para la expresión de orden). Esto se utiliza para ejecutar agregados.

SOBRE(PARTICIÓN POR <…> ORDEN POR <…>)


La tabla primero se divide en diferentes grupos con el mismo valor para la expresión de partición, y luego las filas se
ordenan por la expresión de ordenación dentro de esos grupos. A continuación, se evalúa el agregado para las filas
desde el principio de la tabla hasta la fila actual (y las filas siguientes con el mismo valor para la expresión de orden).

SOBRE(FILAS ENTRE <…> Y <…>)


Se puede agregar una cláusula ROWS BETWEEN a una función OVER() con una cláusula ORDER BY. Esto
restringe el agregado a un conjunto o filas en relación con la fila actual, normalmente un número de filas que preceden o
siguen a la fila actual. Es útil para el cálculo de agregados rodantes.

159
Machine Translated by Google

CAPÍTULO 10

Consideraciones de eficiencia

¡Es posible que no necesite leer este capítulo! Los sistemas de administración de bases de datos (DBMS) son muy eficientes, y
si tiene una cantidad modesta de datos, la mayoría de sus consultas probablemente se llevarán a cabo en un abrir y cerrar de ojos.
Complicarte la vida en un intento de hacer tus consultas un poco más rápidas no tiene mucho sentido.
Sin embargo, si tiene (o podría tener) una gran cantidad de datos y la velocidad es absolutamente crítica, necesitará más habilidad
y experiencia de la que probablemente obtendrá al leer un capítulo en un libro para principiantes. Habiendo dicho eso, es probable
que la gente le diga que es importante cómo expresa sus consultas o que debe indexar sus tablas, por lo que es útil tener una idea
de lo que sucede detrás de escena y comprender algunos de los terminología.

A lo largo de este libro, he enfatizado que a menudo hay formas alternativas de formular una consulta en
SQL. Es posible que la implementación de SQL que está utilizando no admita algunas construcciones, por lo que sus opciones
pueden ser limitadas. Incluso entonces, generalmente tiene alternativas para la mayoría de las consultas. ¿Importa cuál usas?
Una consideración es la transportabilidad de sus consultas. Si no está seguro de dónde se puede usar su consulta, puede optar por
evitar las palabras clave y las operaciones que no son ampliamente compatibles (todavía). Sin embargo, normalmente escribirá
consultas para una base de datos con una implementación específica de SQL. En ese caso, sus preguntas principales son "¿Cómo
afectarán las diferentes construcciones de una consulta al rendimiento?" y "¿Hay algo que pueda hacer para mejorar el rendimiento?"

Qué sucede con una consulta


Hasta este punto, nos hemos concentrado en tomar una pregunta que necesitamos respuesta y construir una consulta que
devuelva información adecuada y precisa de la base de datos. Conceptualmente, el escritor de consultas piensa en la base de
datos como una colección de tablas. Una declaración SQL es una expresión que describe qué datos deben recuperarse de esas
tablas y qué restricciones deben obedecer esos datos (el enfoque de resultados). 1

También hemos visto que se puede especificar una consulta describiendo operaciones de conjunto, como uniones e
intersecciones, lo que daría como resultado que se devuelvan los datos apropiados (el enfoque de proceso). El uso de operaciones
de conjuntos hace que la formación de una consulta sea muy elegante, pero las operaciones son puramente conceptuales. Si bien
podemos especificar la consulta en términos de, digamos, una combinación seguida de una intersección, el DBMS interpretará esto
como una descripción de los datos que se devolverán, no como un método para recuperar los datos.
Las ideas de tablas y modelos de datos son una forma útil para que entendamos cómo se relacionan lógicamente los
datos. Dejamos que el DBMS se encargue de cómo se almacenan y recuperan físicamente los datos.
La figura 10-1 es un esquema simplificado de los diferentes niveles de abstracción que pueden ayudarnos a comprender una
base de datos.

1
SQL se basa en el cálculo relacional, que proporciona una descripción de los datos que se recuperarán. Consulte el Apéndice 2 para obtener
más información.

© Clare Churcher 2016 C. 161


Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_10
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Figura 10-1. Niveles de un sistema de gestión de base de datos

En la parte superior del diagrama de la Figura 10-1 tenemos el nivel de usuario. Aquí es donde tenemos las diferentes
aplicaciones y dispositivos que forman la interfaz entre los humanos y los datos. Es aquí donde un usuario (o aplicación)
construirá una instrucción SQL (arriba a la izquierda del diagrama).
La capa intermedia es una vista conceptual de la base de datos. Podemos pensar en los datos como si estuvieran en
tablas con varias claves y restricciones de validación. También es donde podemos formar modelos de cómo la base de datos
debe tratar con usuarios simultáneos y las reglas para permitir el acceso a diferentes datos. Las sentencias SQL que construimos
son en términos de todos estos conceptos.
Los datos reales se almacenan en el nivel físico, que se muestra en la parte inferior de la Figura 10-1 . Lo que consideramos
una tabla pueden ser segmentos de datos almacenados en servidores posiblemente diferentes, tal vez en diferentes países. En
este nivel habrá índices que permitan un rápido acceso a los diferentes registros; hablaremos de ellos en secciones posteriores
de este capítulo.
Cómo se ubican y ensamblan las piezas de información relevantes para producir el resultado de la consulta es un trabajo
para el optimizador de consultas . La instrucción SQL construida por el usuario se pasa al optimizador, que tiene acceso a
información sobre el número de filas en una tabla (conceptual), la cantidad de datos en cada fila, los atributos sobre los que se han
creado índices, etc. . Utiliza toda esta información para crear un plan de ejecución . El plan de ejecución es una secuencia eficiente
de pasos para buscar, comparar y ensamblar los datos en el resultado especificado por la consulta. Los datos se recuperan del
almacenamiento físico y se ensamblan de acuerdo con el plan de ejecución, y el resultado se devuelve al usuario, generalmente
en forma de tabla.

162
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

La mayoría del software de base de datos comercial proporciona herramientas para mostrar los planes de ejecución
propuestos y los tiempos estimados para llevar a cabo cada paso. Esto proporciona información sobre cómo se ejecuta una consulta y
dónde se gasta el tiempo. Un buen administrador de base de datos podrá utilizar este tipo de información para ajustar la base de datos;
por ejemplo, agregando nuevos índices.
En las siguientes secciones, veremos brevemente cómo se almacenan los registros, cómo pueden mejorar los índices
eficiencia y algunas de las cosas que suceden detrás de escena cuando se lleva a cabo una consulta.

Encontrar un registro
La mayoría de las consultas en una base de datos implicarán, en algún momento, la búsqueda de registros que coincidan
con una condición particular. Por ejemplo, es posible que deseemos buscar registros en una sola tabla (p. ej., WHERE LastName =
'Smith'), buscar registros de dos tablas que coincidan con una condición de combinación (p. ej., WHERE m.MemberID = e.MemberID),
o buscar el existencia o no de valores (por ejemplo, DONDE NO EXISTE...).
Buscar y encontrar datos parece bastante fácil en estos días cuando todo se almacena electrónicamente.
Si queremos encontrar un tema en un libro en línea, simplemente abrimos un cuadro de búsqueda y escribimos algunas palabras
clave. Con un libro físico es una historia muy diferente. Tenemos que escanear cada página o esperar que haya un índice útil o una
tabla de contenido. Aquellos que recuerdan las guías telefónicas físicas recordarán que fue fácil encontrar a Jim Smith pero imposible
encontrar quién vive en 16 Murray Place.
Detrás de escena en una base de datos, los problemas son los mismos que para los libros físicos. Los datos solo se pueden
almacenar en un orden, pero es posible que queramos buscarlos de varias maneras. Es útil saber qué está pasando realmente
para que tengamos una comprensión de lo que afecta el rendimiento de localizar un registro específico.
Una forma de encontrar registros que coincidan con una condición es simplemente buscar secuencialmente en cada fila de
la tabla. Esta es la forma más lenta y costosa de encontrar lo que está buscando. Habiendo dicho eso, puede que no sea un
problema. No tardaría mucho en escanear la tabla Tipo de palos de golf para encontrar la cuota de membresía para personas
mayores. Por otro lado, sería un trabajo más grande escanear cada fila en una tabla de entrada (realista) para encontrar miembros
que hayan ingresado al torneo 38 en los últimos cuarenta años.

Almacenamiento de registros en orden Si

consideramos la teoría relacional, las filas de una tabla 2 no tienen orden. Esto nos permite considerar una tabla como un conjunto y
aplicar todas las operaciones de conjunto. Esto es útil desde un punto de vista conceptual, pero en la práctica, la forma en que se
almacenan los registros marcará la diferencia en la rapidez con la que podemos encontrar lo que estamos buscando.
Si los registros de una tabla se almacenan en un orden aleatorio (quizás el orden en que se crearon), esto se conoce como
una tabla de almacenamiento dinámico . La única forma de encontrar un registro en una tabla de montón es escanear toda la tabla. En
general, los registros se almacenarán ordenados por algún atributo, a menudo la clave principal. Hay todo tipo de algoritmos para
encontrar rápidamente un registro en particular en una tabla ordenada, pero la mayoría se basará en la idea de una búsqueda binaria .
Cuando tratamos de encontrar un nombre en una guía telefónica o una palabra en un diccionario, empleamos un tipo de
búsqueda binaria. En el escenario más simple, inspeccionamos una página en el medio del libro y decidimos si la palabra objetivo
está antes o después de las palabras en esa página. Luego comenzamos de nuevo e inspeccionamos una página a la mitad de la
parte que es de interés. Muy rápidamente nos concentramos en la página requerida. Si los registros de una tabla están ordenados
por un campo en particular, las búsquedas en ese campo serán más eficientes que las búsquedas en campos sin índice.
Cuando hablamos de registros en una tabla almacenados en orden, no queremos decir que estén físicamente uno tras otro
en un disco. Si este fuera el caso, si quisiéramos insertar un nuevo registro cerca del principio, tendríamos que mover todos los
demás. Se puede pensar que los registros están en una estructura similar a un árbol.
Un tipo común de árbol es un B-Tree . La figura 10-2 muestra una representación muy simple de una estructura B-Tree para almacenar
letras del alfabeto.

2
Más formalmente, las tuplas en una relación no tienen orden.

163
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Figura 10-2. Representación de datos en un B-Tree

Si está buscando una letra en el árbol de la Figura 10-2 , debería comenzar en el nodo o cuadro superior. En el
nodo superior, o encuentra lo que está buscando o sigue el camino apropiado. Por ejemplo, si está buscando H, seguiría el camino
entre G y O. La estructura permite agregar y eliminar registros con la mínima interrupción. Podemos agregar fácilmente R al cuadro
que contiene P y Q sin alterar ninguna de las otras letras. Mantener un B-Tree no es trivial. A medida que se agregan y eliminan datos,
será necesario reorganizar el árbol para mantenerlo equilibrado y para agregar y eliminar nodos y niveles. Afortunadamente, todo esto
sucede bajo el capó.

Habiendo dicho todo eso, cuando pensamos en que los registros están en orden, generalmente es más fácil imaginar
todos ellos en una sola línea. Eso es lo que haré durante el resto de este capítulo.

Índice agrupado

Si los registros se almacenan físicamente en algún orden, esto se denomina índice agrupado . Si pensáramos
que es una buena idea almacenar nuestros registros de miembros en orden de nombres, podríamos crear específicamente un índice
agrupado para que los registros se almacenen en el orden del valor de LastName . Podemos crear un índice con una sentencia SQL.
Necesitamos proporcionar un nombre para el índice (por ejemplo, Clustered_Name) y especificar los campos en los que ordenar el
índice, como en la consulta aquí:

CREAR ÍNDICE AGRUPADO Nombre_agrupado ON Miembro (Apellido);

De forma predeterminada, el orden de un índice agrupado suele ser el valor de la clave principal. Si bien es posible
especificar un orden diferente para el índice agrupado, debe tener una buena razón para hacerlo.
Con un índice agrupado en su lugar, ahora hay dos formas de ubicar un registro. Considere ejecutar el
siguiente consulta en la tabla de miembros con el índice agrupado en LastName:

SELECCIONE *

DE MIEMBRO
DONDE Apellido = 'Smith';

Debido a que la tabla está en orden de Apellido, podemos navegar rápidamente al registro correcto haciendo un binario
búsqueda. Esto se conoce como búsqueda de tabla .

Ahora considere la siguiente consulta:

SELECCIONE *

DE MIEMBRO
DONDE Teléfono = '03-567-123';

No tenemos otra opción ahora que verificar cada registro en la tabla. Ni siquiera podemos detenernos cuando llegamos a un
registro coincidente, ya que puede haber varios registros con el mismo número de teléfono. Esto se conoce como exploración de tabla .

164
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Índices no agrupados

Los registros de una tabla solo se pueden almacenar en un orden físico, por lo que solo puede haber un índice agrupado, que
generalmente se encuentra en la clave principal. Si el índice agrupado para la tabla de miembros está en el campo de clave
principal ID de miembro
, podemos buscar un valor particular de MemberID y encontrar la fila completa con todos los detalles para
ese miembro Si queremos encontrar una fila con un apellido en particular, tendríamos que escanear toda la tabla.
Afortunadamente, podemos configurar índices no agrupados adicionales en la tabla. Me referiré a estos índices no agrupados
como simples índices a partir de ahora. Aquí está el SQL para crear un índice en LastName para la tabla de miembros:

CREAR ÍNDICE idx_Name ON Miembro (Apellido);

Lo que esto hace es crear una lista de todos los valores de LastName en orden. Cada entrada incluirá una referencia a
el índice agrupado para que se pueda encontrar la fila completa con el resto de la información. La figura 10-3 ilustra cómo las
primeras entradas en el índice LastName se refieren al índice agrupado.

Figura 10-3. Index on LastName tiene referencias al índice agrupado para obtener información completa

165
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Debido a que el índice está ordenado por apellido, es posible realizar una búsqueda de índice para encontrar la entrada que necesitamos.
y luego use la referencia para buscar la fila asociada en el índice agrupado para recuperar el resto de la información.

En la práctica, si el optimizador de consultas usa un índice en particular depende de muchos factores: el número de filas en la
tabla, el tamaño de cada fila en el índice y la tabla, si se ha accedido a los registros recientemente y se han almacenado en caché,
etc. en.

Índice agrupado en una clave compuesta Consideremos la tabla de

entrada. Recuerde que la tabla Entrada tiene tres campos: Las preguntas de MemberID que podríamos, TourID y Año . Dos
hacer sobre los datos de esta tabla son:

• ¿A qué torneo ha ingresado un miembro en particular (por ejemplo, el miembro 235)?

• ¿Quién ha entrado en un torneo en particular (por ejemplo, el torneo 40)?

Sería sensato tener dos índices: uno en TourID y otro en MemberID. Sin embargo, cada uno de
estos se referirían al índice agrupado. ¿Qué orden será ese para la tabla de entrada?
De forma predeterminada, la tabla se agrupará en la clave principal, que para la tabla Entrada es una combinación de los tres
campos. El orden de los registros dependerá de cómo especificamos la clave principal. digamos la entrada
La tabla se creó con la siguiente instrucción SQL:

CREAR entrada de tabla (


ID de miembro INT,
Tour ID INT,
Año INT,
CLAVE PRIMARIA (ID de Miembro, ID de Tour, Año);

El orden de las filas en el índice agrupado será como en la Figura 10-4 . Primero, se ordenan por el primer campo especificado
en la cláusula PRIMARY KEY (MemberID). Aquellas filas con el mismo valor de MemberID serán ordenadas por el segundo campo
(TourID) y así sucesivamente.

Figura 10-4. Orden de datos en el índice agrupado predeterminado para la tabla de entrada

166
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

El sistema puede encontrar fácilmente los torneos en los que ha entrado el miembro 235 porque las entradas están en orden de
MemberID y se puede realizar una búsqueda de mesas. No necesariamente necesitamos un índice adicional en MemberID. Por otro lado,
encontrar quién ingresó al torneo 40 requeriría un escaneo para investigar cada fila. En esta situación, un índice en TourID sería una mejora.

Por lo tanto, el orden en que especificamos los campos de la clave principal puede afectar la forma en que se realizan las consultas.
out y puede influir en qué otros índices podrían ser útiles. Si el orden de los campos de clave principal se hubiera especificado con
PRIMARY KEY (TourID, MemberID, Year), el índice agrupado estaría en el orden de TourID En ese caso, se debe considerar un índice
en MemberID
. si necesitamos encontrar filas regularmente para un miembro particular.

Tuve cuidado de decir para la situación en la Figura 10-4 que no necesariamente necesitamos un índice en MemberID. El
optimizador tendrá
índice.en cuenta
Cada muchas
entrada cosas.
en un índiceUno que espor
formado importante es eldetamaño
dos campos o número
texto, como de bytes
Apellido de unaserá
y Nombre, entrada
más típica
grandeenque
el un índice

en un solo campo de texto, que a su vez será más grande que un índice en un campo numérico. Cada vez que se visita una entrada en un
índice hay que recuperarla, por lo que hay un coste de IO (entrada/salida) que dependerá del tamaño de la entrada.

Si el índice agrupado en la Figura 10-3 tuviera significativamente más datos en cada fila (por ejemplo, algún texto descriptivo
campos), entonces el costo de recuperar una fila sería más alto que recuperar solo los tres campos numéricos.
En ese caso, valdría la pena considerar tener un índice en MemberID. No alteraría la cantidad de entradas de índice necesarias para
investigar, pero tendría un costo de IO menor ya que cada entrada es más pequeña. La desventaja es que una vez que se encuentra el
MemberID correcto mediante una búsqueda de índice, el sistema deberá buscar el índice agrupado para encontrar el resto de la información.
Dependiendo de toda la información a la que pueda acceder, el optimizador determinará si es más eficiente usar el índice en MemberID y
buscar el resto de la información, o simplemente escanear todos los registros en el índice agrupado.

Actualización de índices Los índices

son claramente maravillosamente útiles. ¿Por qué no indexamos simplemente todo lo que es probable que busquemos?
Esto es ciertamente posible. La desventaja es que hay que mantener los índices. Cada vez que agreguemos o eliminemos un registro
en una tabla, todos los índices de esa tabla también deberán actualizarse. Por lo tanto, tenemos una compensación.
Muchos índices significarán una recuperación rápida pero una actualización más lenta. Menos índices significarán una actualización más rápida pero
posiblemente una recuperación más lenta.
La gestión de estas compensaciones es trabajo para un administrador de base de datos experimentado con un conocimiento excelente
del dominio Hay muchas herramientas disponibles que monitorearán la base de datos y proporcionarán estadísticas sobre el uso de índices
y otra información sobre los datos. Si los datos son relativamente estables con pocas actualizaciones, tener varios índices hará que la
recuperación sea más rápida. Si los datos se actualizan constantemente, los índices pueden ser contraproducentes.

En situaciones en las que hay muchas actualizaciones, puede ser práctico realizar actualizaciones masivas de datos. Con una
actualización masiva, puede eliminar los índices. La siguiente consulta muestra cómo eliminar el índice que creamos en la tabla de miembros
anteriormente en el capítulo:

Soltar idx_Name en Miembro;

Todas las adiciones, eliminaciones y modificaciones a la tabla se pueden llevar a cabo sin la sobrecarga
de actualizar los índices. Al final de las actualizaciones en la tabla, los índices se pueden volver a crear. Esto puede o no ser más eficiente
que actualizar cada índice para cada cambio.

167
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Índices de cobertura
Agregar más campos a algunos índices también puede ser efectivo. Considere la siguiente consulta:

SELECCIONE Nombre
DE Miembro
DONDE Apellido = 'Smith'

Si la tabla de miembros tiene un índice en LastName , entonces la consulta anterior requeriría una búsqueda de índice para
encuentre a Smith y luego una búsqueda del índice agrupado para encontrar el primer nombre. Si el índice estaba en el índice
compuesto (Apellido, Nombre), toda la información requerida para la consulta está contenida en el índice y no se requiere búsqueda.
Esto se conoce como índice de cobertura . Nuevamente, existe la compensación de tener un costo de IO mayor para las filas más
grandes en el índice versus el costo de la búsqueda.

Selectividad de índices
Los índices son más útiles cuando el número de filas que devuelve una búsqueda de índice es pequeño en comparación con
el número de filas de la tabla.
Por ejemplo, consideremos buscar información sobre un miembro con un apellido particular. Un índice
on LastName en la tabla de miembros probablemente devuelva solo un pequeño porcentaje de sus elementos si buscamos un
nombre específico. Luego, el sistema puede buscar el índice agrupado para cada uno de esos elementos devueltos para
recuperar el resto de la información sobre los miembros.
Por el contrario, qué pasa si queremos encontrar información sobre mujeres. Un índice de Género devolverá alrededor de la
mitad de sus entradas si buscamos 'F' en la tabla de Miembros del club de golf. Entonces, el DBMS tendría que buscar los registros
correspondientes en el índice agrupado. En esta situación, probablemente sea más eficiente simplemente escanear el índice
agrupado, que contiene toda la información que necesitamos, y no molestarnos en absoluto con el índice.
A veces, la selectividad de un índice no es obvia. Por ejemplo, un índice en un campo Ciudad no será útil si la mayoría de
los registros de la tabla tienen el mismo valor para ciudad y la mayoría de las consultas son para esa ciudad.
El software de base de datos a menudo proporciona herramientas que pueden ayudarnos. Las herramientas pueden proporcionar estadísticas sobre el estado actual

distribución de datos en los campos de una tabla; por ejemplo, qué porcentaje de la tabla tiene los mismos valores en un campo,
como Ciudad . Esto ayudará a determinar si un índice podría ser útil. A menudo, se pueden recopilar estadísticas sobre la
frecuencia con la que se utiliza un índice. Si el optimizador hace poco uso de un índice, es mejor eliminarlo en lugar de actualizarlo
constantemente.

Técnicas de Unión
Si consideramos la tabla Entrada en la Figura 10-4 , , la mayoría de las consultas requerirán una combinación en la tabla de miembros para encontrar el

los nombres de los participantes y/o una combinación en la tabla Torneo para encontrar los nombres y otra información sobre los
torneos. Cada una de estas uniones compara una clave foránea en la tabla Entrada con la clave principal de la tabla Miembro o
Torneo. Consulte el Capítulo 1 para revisar lo que queremos decir con una clave externa. Esta unión de una clave externa con una
clave principal es un escenario tan común que vale la pena entender cómo se pueden realizar las uniones. Usaremos las tablas
Member y Entry como ejemplo, pero las ideas tienen una amplia aplicación.
Hay una serie de enfoques diferentes que se pueden tomar cuando se lleva a cabo una unión. El enfoque más
eficiente dependerá de muchas cosas, incluidos los tamaños relativos de las tablas, los índices que se han creado, si la consulta
también incluye la proyección de columnas específicas o la selección de filas, si se ha especificado un orden de salida, etc. en. No
tiene que preocuparse por la elección del enfoque, ya que eso lo decidirá el optimizador. Sin embargo, la creación de índices
particulares puede influir en el enfoque adoptado.

168
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Bucles anidados
Un enfoque para unir tablas se llama bucles anidados . Esto significa que el sistema escanea una tabla y, para cada fila de esa
tabla, examina todas las filas de la otra tabla para encontrar coincidencias para la condición de combinación. El enfoque de bucle
anidado se ilustra en la figura 10-5 para la condición de combinación Entry.MemberID = Member.MemberID .

Figura 10-5. Enfoque de bucles anidados para encontrar filas con ID de miembro coincidentes

En la figura 10-5 , el bucle exterior está en la mesa de entrada. Para cada fila en la tabla Entrada, el sistema necesitará
encontrar las filas coincidentes en la tabla Miembro (interna). Las tablas que se muestran en la figura 10-5 no están ordenadas,
lo que significa que será necesario visitar cada fila de la tabla Miembro para cada fila de la tabla Entrada para encontrar los
registros coincidentes (una exploración de la tabla).

169
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Si hay un índice en el campo coincidente en el ciclo interno (MiembroID en la tabla Miembro, en este caso), será más eficiente encontrar
el campo coincidente. El índice se puede usar para encontrar rápidamente los registros coincidentes sin tener que visitar cada fila. En la práctica,
la tabla de miembros probablemente tendrá un índice agrupado en su clave principal. El bucle de ID de miembro será más efectivo si hay un
índice en el ID de miembro de la tabla .de
Sientrada. Elestán
las tablas optimizador tendrá
anidadas con en cuenta
la tabla deesta información
entrada para decidir
en el interior, si la
entonces elopción
internode bucles anidados
es eficiente para realizar la unión y qué tablas deben ser las tablas internas y externas.

La mayoría de los sistemas de bases de datos comerciales proporcionarán herramientas para ver el plan de ejecución de una consulta. Figura 10-6
muestra una captura de pantalla de SQL Server que muestra el plan de ejecución para la combinación en las tablas Miembro y Entrada en la
siguiente consulta:

SELECT *

FROM Miembro m INNER JOIN Entrada e en m.MemberID = e.MemberID;

Figura 10-6. Plan de ejecución que muestra bucles anidados

En la Figura 10-6 vemos en la parte superior derecha un escaneo de tabla de la tabla Entrada. Este es el bucle exterior del bucle
anidado (como se muestra en la Figura 10-5 ). El ícono en la parte inferior derecha muestra que para cada fila en la Entrada
se realizará una búsqueda en el índice agrupado de la tabla de miembros para encontrar la fila con un ID de miembro
coincidente.
. ¿Importa en qué orden especificamos las tablas en una consulta de combinación? Si ponemos la tabla de

entrada primero en la expresión SQL, ¿hará alguna diferencia? Érase una vez que puede tener. En estos días casi
seguro que no.
Expresar la consulta con la tabla en un orden diferente da como resultado el mismo plan de ejecución en SQL Server que el plan de la figura
10-6 . Sin embargo, si cambiamos los campos que se seleccionan, o agregamos otros índices, o elegimos ordenar la salida, es muy probable
que el plan de ejecución cambie.

Combinación de

combinación Otro enfoque para realizar una combinación es ordenar primero ambas tablas por el campo de combinación. Entonces es
muy fácil encontrar filas coincidentes. Esto se denomina unión por fusión y se muestra en la Figura 10-7 .

170
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Figura 10-7. La combinación de combinación requiere que cada tabla se ordene por el campo que se compara

Clasificar tablas es una operación costosa. Sin embargo, si las tablas tienen índices en los campos de combinación, se puede acceder a las
filas en orden mediante un escaneo de índice, lo que hace que la combinación de combinación sea una opción.
Tanto la combinación de fusión como la combinación de bucles anidados serán más efectivas si uno o ambos campos en el
la condición de unión tiene índices.

Diferentes expresiones SQL para combinaciones En la sección anterior,

mencioné brevemente si el orden de las tablas en una combinación afectaría la ejecución. La respuesta fue no para la consulta de la
Figura 10-6 . Sin embargo, tenemos otras formas de expresar uniones.
Las dos consultas que siguen tienen exactamente los mismos planes de ejecución en SQL Server:

SELECCIONE Apellido DE Miembro m, Entrada e DONDE m.MemberID = e.MemberID;

SELECT LastName FROM Entrada e INNER JOIN Miembro m ON m.MemberID = e.MemberID;

Las siguientes dos sentencias SQL especifican la combinación en términos de consultas anidadas. tienen diferentes
planes de ejecución de las consultas anteriores pero son iguales entre sí:

SELECT LastName FROM Member m DONDE m.memberID IN (SELECT

MemberID FROM Entry);

SELECCIONE Apellido del miembro m DONDE EXISTE

(SELECCIONE * DE la entrada e DONDE m.MemberID = e.MemberID);

Entonces, ¿deberíamos usar o evitar consultas anidadas? La respuesta, como siempre, es “depende”.

171
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Antes de comparar los dos pares anteriores de declaraciones SQL, debemos tener en cuenta que la salida de ellos es
diferente. El primer par producirá nombres duplicados (repitiendo el nombre de un miembro para cada torneo en el que haya
ingresado). El segundo par de consultas producirá nombres únicos. Para ser justos en la comparación, compararemos las
siguientes dos consultas, que tienen resultados idénticos:

SELECCIONE DISTINCT LastName


FROM Entry e INNER JOIN Member m ON m.MemberID = e.MemberID;

SELECCIONE Apellido DE Miembro m DONDE m.ID de miembro EN


(SELECCIONE ID de miembro DE Entrada);

Puede ver los dos planes en la Figura 10-8 .

Figura 10-8. El mismo resultado pero planes de ejecución y costos muy diferentes

La figura 10-8 muestra el plan para la consulta utilizando la palabra clave INNER JOIN en la parte superior y el plan para la
consulta anidada debajo. Los porcentajes indican que si ambas consultas se ejecutaran en un lote, la superior representaría el 74 por
ciento del tiempo y la inferior el 26 por ciento. Es decir, la consulta INNER JOIN tarda tres veces más que la consulta anidada.

La adición de la palabra clave DISTINCT en las principales cuentas de consulta durante gran parte del tiempo. El optimizador
ha optado por ordenar los registros para preparar la eliminación de los nombres duplicados. Esta operación de clasificación
representa más de la mitad del costo total de la primera consulta. Al ver este plan, podría considerar agregar un índice en LastName
para que se pueda acceder a los registros de la tabla Member en el orden de LastName, eliminando así la necesidad de la
ordenación que requiere mucho tiempo.
A menos que tenga un conocimiento interno real, es casi imposible adivinar lo que se le ocurrirá al optimizador. A la larga,
probablemente no importe, a menos que las tablas tengan un gran número de filas o una consulta sea particularmente crítica en
cuanto al tiempo. Lo importante que debe recordar es que si sospecha que una consulta crítica está causando un cuello de botella,
existen herramientas que pueden ayudarlo a comprender lo que está sucediendo. Luego puede experimentar con índices o las
formas en que se expresa la consulta para ver si eso puede acelerar las cosas.

172
Machine Translated by Google

CAPÍTULO 10 ÿ CONSIDERACIONES DE EFICIENCIA

Resumen
Los índices pueden marcar una diferencia considerable en el rendimiento de muchas consultas. Sin embargo, existe la
desventaja de que tienen que ser mantenidos. Con cualquier puesta a punto de una base de datos es importante saber cuáles
son los procesos importantes. No tiene sentido hacer todo lo posible para mejorar una consulta que rara vez se lleva a cabo, y
es contraproducente mejorar el rendimiento de la recuperación si la mayor parte del trabajo crítico en su base de datos es la
actualización de registros.
Las herramientas proporcionadas por muchos sistemas de bases de datos pueden proporcionar información valiosa. Los
planes de ejecución pueden dar una idea de dónde se gasta el tiempo en una consulta. Se pueden recopilar estadísticas sobre el
uso de índices o la distribución de datos en un campo. Toda esta información es útil para decidir si vale la pena investigar la adición
de un nuevo índice.
Aquí hay algunas reglas generales para crear índices.

Clave principal Necesita

una muy buena razón para no tener un índice en los campos de clave principal de una tabla. En general, se colocará un índice
agrupado en la clave principal de forma predeterminada.

Llaves extranjeras
Las uniones donde la condición de unión es entre una clave externa y una clave principal son muy comunes. Por esta razón,
normalmente vale la pena considerar un índice de claves foráneas.

DONDE Condiciones

Si tiene consultas que usan con frecuencia campos particulares en una condición DONDE, entonces es útil indexar esos campos.
Esto permite una búsqueda de índice en lugar de tener que hacer un escaneo de tabla para encontrar las filas relevantes.
Esto es más útil cuando la condición WHERE es selectiva, lo que significa que recuperará solo un pequeño subconjunto de las
filas.

ORDENAR POR, AGRUPAR POR y DISTINTO


La clasificación puede ser una operación muy costosa si no hay índices en los campos involucrados en la condición de
clasificación. Claramente, ORDER BY requiere que se ordenen las filas. Las consultas que contienen DISTINCT o GROUP BY
a menudo ordenan los registros para eliminar los duplicados o agregar los datos. Con los índices apropiados, se puede usar un
escaneo de índice para recuperar las filas en orden, eliminando así la necesidad de una costosa operación de clasificación.

Usa las herramientas

Los optimizadores de consultas son muy sofisticados. Mantienen estadísticas sobre sus tablas (número de filas, tamaño de
columnas, distribución de datos, etc.) y las utilizan para ayudar a determinar un plan de ejecución eficiente para una consulta. Si
tiene una consulta crítica que desea que sea lo más eficiente posible, verifique los planes de ejecución para ver dónde se gasta
el tiempo. Luego puede experimentar con los efectos de reafirmar la consulta o agregar índices adicionales.

173
Machine Translated by Google

CAPÍTULO 11

Cómo abordar una consulta

En los capítulos anteriores, vimos diferentes formas de expresar una consulta. Examinamos el enfoque de proceso , que describe
cómo se pueden manipular las tablas y los datos para producir el resultado requerido. Estas consultas se expresan mediante
palabras clave que describen operaciones como INNER JOIN e INTERSECTION. También analizamos cómo
consultas
expresar
en términos
las
del enfoque de resultado , que describe los criterios que deben satisfacer
el resultado.
los datos resultantes en lugar del proceso para recuperar

Sin embargo, a veces, cuando se me presenta una descripción complicada en lenguaje natural de una consulta, no es raro
encontrar que mi mente se queda en blanco. Tengo mucha munición a mano, pero por un momento o dos, no tengo idea de qué
armas elegir.
Por lo general, es solo una cuestión de estar confiado y relajado. Las consultas grandes y complicadas siempre se
pueden dividir en una serie de consultas más pequeñas y simples que se pueden combinar más adelante. En este capítulo se
describe cómo hacerlo.

Comprender los datos


Puede parecer obvio, pero no puede recuperar información de una base de datos sin comprender dónde se
almacenan todos los diferentes elementos de datos y cómo se interrelacionan las tablas relevantes. La mayor parte
del tiempo consultará una base de datos diseñada por otra persona, y probablemente mantenida y modificada con el
tiempo por varias personas. Además de comprender las tablas y las relaciones que se han implementado, también es necesario
tener una idea del escenario real subyacente. También debe estar alerta ante la desafortunada realidad de que la base de
datos puede haber sido mal diseñada. Esto podría significar que no puede recuperar la información requerida con precisión.
Consideraremos un poco más este problema de trabajar contra un mal diseño en el Capítulo 12.

Determinar las relaciones entre tablas


La mejor manera de obtener una visión general de la implementación de una base de datos es mirar un esquema de
las relaciones entre las tablas. La mayoría del software de administración de bases de datos proporciona una forma de ver los
campos en las tablas y las relaciones de clave externa entre las tablas. Las Figuras 11-1 y 11-2 muestran los diagramas de
relaciones de claves foráneas para nuestra base de datos del club tal como lo muestran SQL Server y Microsoft Access.

© Clare Churcher 2016 175


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_11
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Figura 11-1. Un diagrama de base de datos de SQL Server

176
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Figura 11-2. Un diagrama de relaciones de Microsoft Access

En la superficie, los diagramas de las Figuras 11-1 y 11-2 se ven un poco diferentes, pero representan exactamente
la misma base de datos. El esquema de acceso de la figura 11-2 muestra una copia adicional de las tablas de miembros
y equipos. Las dos copias de la tabla de miembros surgen de la propia relación entre los miembros (es decir, un
miembro puede entrenar a otros miembros). La copia adicional de la tabla Equipo se debe a las dos relaciones entre
Miembro y Equipo: un miembro puede ser el administrador de un equipo y un miembro puede pertenecer
a un equipo Estas relaciones se representan en el diagrama de SQL Server en la Figura 11-1 mostrando dos líneas entre
las tablas para que las tablas no se muestren dos veces. Las diferentes representaciones esquemáticas son solo una
peculiaridad de los diferentes sistemas de gestión. Ambos esquemas representan el mismo conjunto de tablas y relaciones.

177
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Las líneas en los dos diagramas de las Figuras 11-1 y 11-2 representan las claves foráneas que se configuraron cuando
se crearon las tablas. Por ejemplo, la instrucción para crear la tabla de miembros contiene dos restricciones de clave externa:

CREAR miembro de la TABLA (


MemberID Int CLAVE PRIMARIA,
Apellido CHAR(20),
Nombre CHAR (20),
MemberType CHAR (20) REFERENCIAS DE CLAVE EXTRANJERA Tipo,
Teléfono CHAR (20),
Hándicap INT,
Fecha de ingreso DATETIME,
Entrenador INT EXTRANJERO REFERENCIAS CLAVE Miembro,
Equipo CHAR (20),
Género CHAR (1));

Recuerde del Capítulo 1 que esta línea de código:

MemberType CHAR (20) REFERENCIAS DE CLAVE EXTRANJERA Tipo

significa que si hay un valor en el campo MemberType, ese valor debe existir en el campo de clave principal en la tabla Tipo. En las Figuras 11-1 y
11-2 se puede ver una línea que representa esta relación de clave externa entre la tabla Miembro y la tabla Tipo. Esta línea de código:
.

Entrenador INT EXTRANJERO REFERENCIAS CLAVE Miembro

significa que los valores en el campo Entrenador ya deben existir en el campo de clave principal en la tabla Miembro; es decir, hay una relación
propia en la tabla Miembro. Esta relación se expresa en la figura 11-1 con el bucle que conecta la tabla de miembros consigo misma. En la Figura
11-2 , la relación se representa mostrando una segunda copia de la tabla Miembro.

Mundo real frente a implementación Los diagramas de la base de datos

de la sección anterior representan cómo se ha implementado la base de datos y, en particular, qué claves externas se han configurado.
Cuando la base de datos se configura por primera vez, el diseño se basará en un modelo de datos conceptual que describe cómo se interrelacionan
las tablas para un problema en particular. Existen varios métodos para representar un modelo de datos, como los diagramas entidad-relación (ER)
y los diagramas de clases UML que hemos estado usando en este libro. La figura 11-3 muestra el diagrama de clases del palo de golf. Consulte el
Capítulo 1 si necesita un repaso sobre cómo interpretar las líneas y los números.

178
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Figura 11-3. Diagrama de clases que representa el modelo conceptual

El diagrama de clases de la figura 11-3 no muestra campos de clave foránea en las clases. Puede ver esto comparando la tabla
Entrada de la figura 11-3 con las de los dos diagramas de base de datos anteriores. Las claves externas MemberID y TourID faltan como
atributos en el diagrama de clases. Las claves foráneas son simplemente una forma de representar la relación entre clases si elegimos
implementar el modelo de datos en una base de datos relacional.
Si decidimos implementarlo en una base de datos orientada a objetos, es posible que no necesitemos ningún campo de clave externa.
Un diagrama de clases con relaciones bien etiquetadas nos brinda una comprensión mucho mayor de la situación del mundo
real que los diagramas de implementación de las figuras 11-1 y 11-2 . Eche un vistazo a las relaciones entre Miembro
ver ay qué
Equipo
me para
refiero.

Los diagramas de base de datos presentados por el software de base de datos relacional le muestran las claves foráneas que tienen
en realidad se ha establecido. Estos pueden no contar toda la historia. Es posible que el desarrollador no haya implementado la relación
para el entrenamiento (por ejemplo) con una restricción de clave externa en el campo Entrenador. Él o ella puede haber pasado por alto el
requisito o puede haber decidido aplicar la restricción de que un entrenador debe ser un miembro existente de alguna otra manera (con un
activador o a través de la interfaz). Sin embargo, incluso si no hay una restricción de clave externa en el campo Entrenador en la tabla
Miembro, aún debemos entender que los miembros entrenan a otros miembros si queremos diseñar consultas confiables sobre el entrenamiento.

En algunos casos, la base de datos implementada puede no tener mucho en común con un modelo de datos preciso.
Por ejemplo, si la base de datos del club de golf contiene tablas separadas para socios, entrenadores y gerentes, o si no se implementó una de las
relaciones entre las tablas de socios y equipos, el diagrama de la base de datos y el modelo de datos se verían bastante diferentes. La probabilidad
de obtener información confiable sería baja.
El Capítulo 12 analiza problemas como este, aunque, aparte de un rediseño importante, a veces no hay mucho que puedas hacer.

179
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

¿Qué tablas están involucradas?


Una vez que tengamos una comprensión de las tablas en la base de datos y cómo están relacionadas (tanto conceptualmente
como por la existencia de claves externas), podemos ver qué tablas necesitará para extraer el subconjunto de datos
requerido. Considere una consulta como "Encuentre a todos los hombres que han ingresado a un torneo de Leeston".
Esta frase contiene algunas palabras clave. Los sustantivos son a menudo una pista de qué tablas o campos vamos a necesitar.
Los verbos a menudo nos ayudan a encontrar relaciones. Veamos los sustantivos. “Torneo” es una gran pista, y tenemos una
mesa de Torneo, así que es un comienzo. La palabra "hombres" es otro sustantivo en la descripción de la consulta. No tenemos una
tabla de hombres, pero sí tenemos una tabla de miembros con un campo de género.
Está bastante claro entonces que las tablas de Miembros y Torneos van a jugar un papel en nuestra consulta. Ahora
necesitamos tener una idea de cómo se relacionan estas dos tablas. La figura 11-4 muestra la parte del diagrama de la base de
datos de SQL Server que contiene estas dos tablas. Vemos que no están directamente relacionados, sino que están conectados a
través de la tabla de entrada. Eso tiene sentido, porque el verbo "ingresar" está en la descripción de nuestra consulta.

Figura 11-4. Parte del diagrama de la base de datos que muestra las tablas de miembros y torneos

Entonces, parece que al menos tres tablas estarán involucradas en nuestra consulta: Miembro, Torneo , y Entrada. Luego
usamos nuestra comprensión de los operadores relacionales para determinar cómo se pueden combinar estas tablas.
¿Necesitamos una combinación o una unión, o alguna combinación de estos y otros operadores relacionales? Veremos
formas de ayudar a decidir las operaciones apropiadas en secciones posteriores de este capítulo.

Mire algunos valores de datos


Las solicitudes de información de una base de datos generalmente se expresan en un lenguaje natural bastante informal e
impreciso. Incluso una solicitud simple, como "Buscar a todos los hombres que han ingresado a un torneo de Leeston", tiene
algunas cosas que debemos aclarar. Echar un vistazo a los datos reales en las tablas a veces puede ayudar.
Nuestra consulta en realidad no "encuentra" a los hombres, sino que devuelve información sobre ellos. Buscando
Los valores de los datos en la tabla nos ayudarán a decidir qué información podría ser útil. Presumiblemente, al
interrogador le gustaría ver los nombres de los hombres. ¿Necesitamos las identificaciones también? Necesitaremos ID si
queremos distinguir dos miembros con el mismo nombre.

180
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Puede que no siempre esté claro a qué se refieren algunas de las palabras de la pregunta. ¿Qué es un torneo
“Leeston”? ¿Es Leeston el nombre de un torneo, un tipo de torneo o una ubicación? Mirar algunas filas de la tabla del Torneo
puede ayudarnos. Vemos que el campo TourName tiene el valor "Leeston" aquí y allá. A veces puede no ser tan fácil determinar a
qué palabras imprecisas se refieren en la descripción de la consulta. Puede ser necesario hablar con el desarrollador o los usuarios
para comprender mejor qué información están tratando de recuperar.

¿Cómo determinamos qué miembros son hombres? Afortunadamente, la tabla Miembro tiene una columna Sexo y parece
que queremos valores de M . ¿Será suficiente seleccionar
como valores?filas con valores
Veremos cómo de M?con
lidiar ¿Puede haberde
problemas algunas filas que tengan
datos inconsistentes enmelo Male
próximo capítulo. Por ahora, supongamos que los hombres se denotan por M Para la consulta simple en este ejemplo, ahora tenemos
.
una descripción más precisa. Es algo así como "Recuperar el MemberID, LastName y FirstName de los hombres (Género = 'M') que
han ingresado al torneo donde TourName = 'Leeston'".

Puede pensar en algunos otros detalles que necesitan aclararse. A menudo es una buena idea preguntar por qué se requiere
esta información. ¿Queremos saber qué hombres han estado alguna vez en Leeston (porque queremos hacerles algunas preguntas
sobre el campo de golf) o queremos saber cuántas veces los miembros masculinos de nuestro club han participado en torneos de
Leeston (porque están interesados en saber qué tan popular es el torneo entre los miembros del club)? Estas preguntas pueden
tener diferentes respuestas, como verá en la sección "Conservar las columnas apropiadas" próximamente.

Método de imagen grande


Mi primer intento de consulta rara vez es elegante o completo. Para una consulta como "Encontrar a todos los hombres que han
ingresado a un torneo de Leeston", hay dos formas de abordarla, dependiendo de cómo funcionen mis musas.
Una forma es el panorama general. Hago esto si tengo una idea de cómo combinar las tablas. Cubriré otra táctica en la sección "¿No
tienes idea de por dónde empezar?", que utilizo cuando no tengo idea de por dónde empezar.
En el método del panorama general, me gusta combinar todas las tablas que necesitaré y conservar todas las columnas para
poder ver lo que sucede. Por lo general, me resulta más fácil tener una ventana de SQL de algún tipo abierta para poder probar
pequeñas consultas para ver si los resultados intermedios parecen prometedores para responder la pregunta general.
Veamos el enfoque general de la consulta "Encuentra a todos los hombres que han ingresado al torneo de Leeston".
Decidimos que necesitábamos tres tablas: Member , Entry y Tournamentforáneas,
. Todas estas
estotablas
yserán útiles.
a menudoestán conectadas
Si nosugiere
lecombinación
queda poruniones
queclaro
las claves
que
es louna
que se
requiere para la consulta, entonces recurra a los métodos en la sección "¿No tiene idea de por dónde empezar?" sección más
adelante en este capítulo.

Combina las tablas


Supongamos que pensamos que unirse a las mesas parece un enfoque prometedor para la consulta sobre los hombres que ingresan
al torneo de Leeston. No tienes que hacer todo a la vez. Comience lentamente con algunas consultas pequeñas para ver cómo se
desarrollan las cosas.
Para llevar a cabo una unión, necesitamos encontrar los campos en los que unirse. Revise el Capítulo 3 si necesita actualizar
su comprensión de los campos compatibles con la combinación. La tabla Entrada es fundamental para esta consulta, ya que
, a la que
conecta las tablas Miembro y Torneo. La tabla de entrada tiene un campo de clave externa denominado TourID conpodemos
la claveunirnos
principal de la tabla de torneos. Haz eso primero.

SELECT * FROM
Torneo t INNER JOIN Entrada e ON t.TourID = e.TourID;

La figura 11-5 muestra algunas filas de la tabla virtual resultante.

181
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Figura 11-5. Parte del resultado de unirse a las mesas de Torneo y Entrada

El resultado que se muestra en la figura 11-5 es ciertamente útil. Podemos ver las entradas y los nombres de los torneos
correspondientes. Podemos ver en las dos primeras filas que los miembros 118 y 228 han entrado en un torneo de Leeston. Ahora
necesitamos averiguar si 118, 228 y otros miembros que ingresan al torneo son hombres y encontrar sus nombres. Podemos obtener
esta información adicional uniendo la tabla virtual de la Figura 11-5 a la tabla de miembros en los campos MemberID:

SELECCIONAR DE
(Torneo t INNER JOIN Entrada e ON t.TourID=e.TourID)
INNER JOIN Miembro m ON m.MemberID = e.MemberID;

La figura 11-6 muestra el resultado. No he incluido todas las columnas en la figura 11-6 porque hay muchas. Verá en breve por qué
me gusta dejar todas las columnas lo más largas posible.

Figura 11-6. Parte del resultado de unirse a las mesas de Torneo, Entrada y Miembros (solo algunas columnas)

182
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

La tabla virtual que se muestra en la Figura 11-6 tiene toda la información que necesitamos para encontrar los datos
requeridos. Las primeras dos filas muestran que los miembros 118 y 228 son mujeres. La fila del miembro 286 (con los círculos)
parece más prometedora. ¿Cómo modificamos la consulta para encontrar el subconjunto apropiado de filas y columnas?

Encuentra el subconjunto de filas


En la Figura 11-6 podemos ver que las filas que queremos retener del resultado de la unión están donde el campo Género
tiene el valor M y el campo TourName tiene el valor Leeston . Podemos seleccionar estas filas agregando
WHERE apropiadauna
a lacláusula
consulta
anterior:

SELECCIONAR *
DESDE (Entrada e INNER JOIN Torneo t ON t.TourID=e.TourID)
INNER JOIN Miembro m ON m.MemberID = e.MemberID
WHERE m.Gender = 'M' AND t.TourName = 'Leeston';

La figura 11-7 muestra solo algunas de las columnas del resultado de la consulta anterior. Tiene cuatro filas: tres
para Robert Pollard y otra para William Taylor.

Figura 11-7. Hombres que han entrado en torneos de Leeston (solo algunas columnas)

¿Por qué tenemos tres filas para Robert Pollard? Las filas son idénticas excepto por el valor del Año
campo. Robert ha entrado en el torneo de Leeston en tres años diferentes. Podemos ver esto claramente en la Figura 11-6
porque hemos dejado la columna Año en la salida. Si hubiéramos conservado solo las columnas de nombres, inicialmente
podríamos estar un poco desconcertados al tener a Robert Pollard repetido tres veces. Lo que hagamos con la repetición de
Robert Pollard depende de entender un poco más claramente la pregunta inicial, como verá en la siguiente sección.

Conservar las columnas apropiadas


Tenemos el subconjunto apropiado de filas de nuestra combinación grande. Ahora debemos conservar solo las columnas
que necesitamos modificando la cláusula SELECT, que actualmente devuelve todas las columnas ( SELECT * ). Esto no siempre
es tan simple como podría parecer. Las tres filas de Robert Pollard nos dan una pista de que las cosas pueden no ser tan
sencillas. Tenemos dos posibilidades.
Si solo queremos saber quién ingresó al torneo en cualquier año, entonces solo queremos los nombres distintos de
Robert Pollard y William Taylor y quizás sus números de identificación. Modificar la cláusula SELECT como en la siguiente
consulta proporcionará ese resultado:

SELECCIONE DISTINTO m.MemberID, m.LastName, m.FirstName DE...

183
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Si el objetivo de la pregunta es averiguar con qué frecuencia los hombres participan en los torneos de Leeston, entonces
queremos conservar todas las entradas. En ese caso, podría ser útil retener el año también para distinguir las filas como se muestra
a continuación:

SELECCIONE m.MemberID, m.LastName, m.FirstName, e.Year


FROM ...

Considere una vista intermedia


Es probable que el SQL para unirse a las tablas Entrada, Miembro y Torneo sea la base de muchas consultas sobre entradas
en torneos. Por ejemplo, las siguientes preguntas requerirán una unión de las tablas Entrada de miembro y Torneo: ,

• ¿Participan los miembros junior en torneos abiertos?

• ¿En qué torneos participó William Taylor en 2015?

• ¿Cuál es el número promedio de torneos sociales en los que los miembros participaron en 2013?

Como es probable que usemos esta combinación grande muchas veces, puede ser conveniente hacer una vista . Una vista es
una instrucción sobre cómo crear una tabla temporal que podemos usar en otras consultas. El siguiente es un primer intento
en el SQL para crear una vista que conserve todos los campos de las uniones:

--Primer intento (fallido)


CREAR VER AllTournamentInfo COMO
SELECCIONAR * DESDE (Entrada e INNER
JOIN Torneo t ON t.TourID=e.TourID)
INNER JOIN Miembro m ON m.MemberID = e.MemberID;

Tal como está, esta consulta no se ejecutará en la mayoría de las versiones de SQL. Esto se debe a que la vista tendría
campos con el mismo nombre; por ejemplo, habrá dos campos llamados MemberID: uno de la tabla Entrada y otro de la tabla
Miembro.
Cuando crea una vista, todos los nombres de campo deben ser distintos. La vista no usará los alias para diferenciar
las columnas en la tabla resultante. El * en la cláusula SELECT debe modificarse para enumerar todos los nombres de campo.
Necesitamos incluir solo uno de los campos con nombres duplicados ( MemberID y TourID ) o cambiar el nombre de aquellos que
están duplicados (p. ej., SELECCIONE m.MemberID AS MMember un poco tedioso, pero , e.MemberID AS EMember
si está creando ). Esto
una vista que es un
es probable
que use muchos veces, vale la pena el esfuerzo, se puede utilizar de la misma manera que cualquier otra mesa en nuestro
Una vez que tenemos la vista AllTournamentInfo ,
consultas Para encontrar los nombres de los hombres que han ingresado a un torneo de Leeston, podemos usar la vista que se muestra aquí:

SELECCIONE DISTINTO Apellido, Nombre


DESDE AllTournamentInfo
WHERE Género = 'M' AND TourName = 'Leeston';

Detectar palabras clave en preguntas


El enfoque general asume que hemos decidido cómo combinar las tablas que contribuirán a la consulta. A veces, será obvio
que, por ejemplo, es necesario unir ciertas tablas. Otras veces, puede que no esté del todo claro al principio. En esta sección,
veremos algunas palabras clave que a menudo aparecen en las preguntas y que pueden proporcionar una pista sobre qué
operaciones relacionales se necesitan. Si ninguno de estos ayuda, recuerde que todavía tenemos la pregunta "¿No tiene idea de
por dónde empezar?" subiendo sección!

184
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Y, Ambos, También
Y y también son palabras que pueden ser engañosas cuando se trata de interpretar consultas, y consideraremos esto más a
fondo en el próximo capítulo. En esta sección, veremos las consultas que tienen la idea de que se deben cumplir dos condiciones
simultáneamente. Las consultas que requieren el cumplimiento de dos condiciones se dividen en dos categorías: las que se
pueden realizar con una cláusula WHERE simple que contiene un operador booleano AND y las que requierenouna
unaintersección
unión
automática.
Para decidir si una consulta realmente necesita que se cumplan dos condiciones, generalmente miro una declaración en lenguaje natural
y ver si puedo reformularlo con la palabra ambos conectando las condiciones. Considere estos ejemplos:

• Encuentra a los jóvenes. ( ¿ Tanto un hombre como un junior? Sí.)

• Encuentra a los miembros que participaron en los torneos 24 y 38. ( ¿ Ambos torneos? Sí).

• Encuentra a las mujeres y los niños. ( ¿ Tanto una mujer como un niño? No.)

La última consulta es la que a veces puede hacer tropezar a la gente. Aunque contiene la palabra y la interpretación, la
común de “mujeres y niños”, no se refiere a alguien que es a la vez una mujer y un niño (es decir, una niña). Más bien, la
frase significa cualquiera que sea mujer o niño (especialmente cuando se trata de botes salvavidas).

El diagrama de la figura 11-8 es una forma útil de visualizar si la palabra del lenguaje natural ambos realmente significa
ambos o cualquiera de los dos. Los círculos representan los dos conjuntos: mujer e hijos. La figura 11-8a muestra la unión (solo
se debe cumplir una condición) y la figura 11-8b la intersección (se deben cumplir ambas condiciones).

Mujeres Niños Mujeres Niños

a) O una mujer o un niño b) Tanto una mujer como un niño


Mujeres UNIÓN Niños Mujeres INTERSECCIÓN Niños

("Mujeres y Niños") ("Muchachas")

Figura 11-8. Visualizar si se necesita una unión o una intersección

Cuando se deben cumplir dos condiciones, estamos viendo la intersección de dos grupos de datos, como en la
figura 11-8b . Esto no significa necesariamente que debamos usar la palabra clave INTERSECT. Considero que la
siguiente pregunta es útil para decidir qué hacer a continuación:

¿Necesito mirar más de una fila para decidir si se cumplen ambas condiciones?

Considere la consulta para encontrar jóvenes. Esto va a necesitar la tabla de miembros. ¿Podemos mirar una sola fila y
determinar si el miembro es un joven y un niño? Podemos ver en la Figura 11-9 que ambas piezas de información están
disponibles en una sola fila.

185
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Figura 11-9. La información sobre el tipo de membresía y el género están disponibles en una sola fila

En esta situación, podemos usar una operación SELECCIONAR simple con el booleano AND para verificar ambas
condiciones, como se explica en el Capítulo 2:

SELECCIONE * DE Miembro m
WHERE m.Gender = 'M' AND m.MemberType = 'Junior';

Ahora considere un tipo diferente de consulta. ¿Qué pasa con la búsqueda de los miembros que han ingresado tanto
torneos 24 y 36? Para hacer esto, debemos mirar la tabla Entrada (probablemente unida a la tabla Miembro si queremos los
nombres). Como podemos ver en la Figura 11-10 , no podemos ,verificar queaun
ingresado miembro,
ambos pormirando
torneos ejemplo,una
el miembro
sola fila. 228, haya

Figura 11-10. Necesitamos investigar más de una fila para verificar ambos torneos

186
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Donde necesitamos satisfacer ambas condiciones y necesitamos mirar más de una fila en la tabla,
podemos usar una unión propia (discutida en el Capítulo 5) o una intersección (discutida en el Capítulo 7).
Si usamos el self join entonces la consulta es:

SELECCIONE DISTINCT e1.MemberID

FROM Entrada e1 INNER JOIN Entrada e2 ON e1.MemberID = e2.MemberID DONDE e1.TourID


= 24 Y e2.TourID = 36;

Una consulta que produce el mismo resultado pero que usa la palabra clave INTERSECT es:

SELECCIONE MemberID FROM Entrada DONDE TourID = 24


INTERSECARSE

SELECCIONE MemberID FROM Entrada DONDE TourID = 36;

No nunca
Estos son algunos ejemplos de consultas que implican las palabras no o nunca :

• Encuentre los miembros que no son personas mayores.

• Encuentre miembros que no estén en un equipo.

• Encuentra miembros que nunca hayan participado en un torneo.

A menudo, cuando las personas ven un no en la descripción de una consulta, inmediatamente piensan en usar un NO booleano.
o un operador <> en una cláusula WHERE. Esto está bien para algunas consultas, pero fallará para otras. Al igual que en la sección
anterior, la siguiente prueba me parece útil para comprender la categoría de la consulta.

¿Necesito mirar más de una fila para decidir si una condición no es verdadera?

Para las dos primeras consultas de la lista con viñetas anterior, podemos mirar una sola fila en la tabla de miembros y decidir si ese
miembro cumple la condición. En la primera consulta, la condición en la cláusula WHERE sería NO MemberType = 'Senior' o MemberType <>
'Senior' . Para encontrar miembros que no están en

un equipo, queremos que el campo Equipo esté vacío, por lo que una cláusula como DONDE EL EQUIPO ES NULO sería suficiente.
Para encontrar a los miembros que nunca han entrado en un torneo, ¿qué mesas necesitamos? Sin duda vamos a necesitar la mesa
Entry. Podemos decidir si un miembro ha entrado en un torneo encontrando solo una fila con su valor de MemberID. Para ver si él o ella no ha
ingresado a un torneo, debemos mirar cada miembros
fila en la tabla
que de
no Entrada. También
hayan entrado debemos
en un fijarnos
torneo no en la tabla
aparecerán de Miembros,
en absoluto porque
en la tabla deaquellos
Entrada.

En situaciones como esta, puede ser útil pensar en términos de conjuntos como en la Figura 11-11 .

187
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Figura 11-11. Encontrar miembros que no han entrado en torneos considerando sets

En el Capítulo 7 vimos cómo representar la diferencia entre dos conjuntos usando el enfoque de proceso y la palabra clave
EXCEPT. La siguiente consulta devolverá las identificaciones de los miembros que no han ingresado a un torneo:

SELECCIONE ID de miembro DE miembro


EXCEPTO

SELECCIONE MemberID DE Entrada;

Si pensamos en términos del enfoque de resultado, podemos describir los criterios para devolver un MemberID en particular. La
siguiente consulta es un ejemplo del uso de NOT IN para encontrar los ID de los miembros que nunca han ingresado a un torneo:

SELECCIONE m.MemberID DE Miembro m


DONDE m.MemberID NO EN (SELECCIONE

e.MemberID DE Entrada e);

El Capítulo 7 tiene muchos ejemplos de cómo usar consultas anidadas como esta.

Todo, Cada Siempre

que vea las palabras todo o cada en una descripción de una consulta, debe pensar inmediatamente en el operador de división. Estos son
algunos ejemplos de este tipo de consultas:

• Encuentra miembros que hayan ingresado a todos los torneos abiertos.

• ¿Alguien ha entrenado a todos los juveniles?

En el Capítulo 7 se explican en detalle ejemplos del SQL para realizar este tipo de consultas .

188
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

¿No tienes idea de por dónde empezar?

Ahora veamos el caso en el que tenemos una buena comprensión de la intención de la consulta en lenguaje natural y tenemos una idea
de qué tablas están involucradas. Hemos buscado algunas palabras clave, pero aún nos sentimos confusos. ¿Ahora que? Esto no es raro
(a mí me pasa regularmente), así que relájate.
Cuando no tengo idea de por dónde empezar, me olvido de las operaciones de configuración y SQL. Dejo de pensar en tablas,
claves foráneas, uniones, etc. En cambio, abro las tablas que creo que serán necesarias para responder la pregunta y miro algunos de los datos.
Trato de encontrar ejemplos que deberían ser recuperados por la consulta. Luego trato de escribir las condiciones que hacen que esos datos en
particular sean aceptables.
Este es el enfoque de resultado que describe qué condiciones deben obedecer las filas devueltas por la consulta.
Es una excelente manera de proceder si tiene problemas para decidir las operaciones que podrían estar involucradas en la manipulación de
las tablas (el enfoque de proceso).
Intentemos una consulta que me dejó un poco perplejo cuando lo pensé por primera vez: "¿Qué equipos tienen un entrenador como su
¿gerente?" Los pasos descritos aquí realmente pueden ayudar.

Encuentre algunas tablas útiles


Veamos las palabras clave en la consulta "¿Qué equipos tienen un entrenador como entrenador?" Tenemos los sustantivos "equipo",
, y Entrenador
"entrenador" y "gerente". Tenemos una tabla llamada Team y Team tables, respectivamente. Por lo tanto, son
y Gerente las tablas
camposTeam
en ely Miembro
Member
parecen un buen lugar para comenzar.

Trate de responder la pregunta a mano


A continuación, eche un vistazo a los datos de las tablas y vea cómo decidiría si un equipo tuviera un entrenador como director técnico.
La Figura 11-12 muestra algunas columnas relevantes de las dos tablas. ¿Puedes encontrar un equipo que satisfaga la condición?

Figura 11-12. ¿Cómo sabemos si un equipo tiene un entrenador como gerente?

Podemos encontrar las identificaciones de los dos directores de equipo con bastante facilidad. Son los valores de la columna Manager de
la tabla Team (239 y 153). Ahora, ¿cómo verificamos si estos miembros son entrenadores? Mirando la tabla Miembro, vemos que los
entrenadores están en la columna Entrenador. Necesitamos verificar si alguno de nuestros dos gerentes aparece en la columna Entrenador. El
miembro 153 sí aparece en la columna Entrenador, por lo que (TeamB) es administrado por un entrenador.

189
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Escriba una descripción del resultado obtenido


La figura 11-12 ilustra cómo determinamos que el equipo B tiene un entrenador como gerente. Ahora necesitamos escribir
una descripción de la lógica que lleva a esa conclusión. Aquí es donde me gusta usar mis dedos para señalar las filas
relevantes para que sea más fácil describir la consulta, como en la Figura 11-13 .

Figura 11-13. Nombrar las filas para ayudar a describir los datos requeridos

Vamos a revisar cada equipo para decidir si se debe recuperar. En la figura 11-13 , esto se representa con el dedo
que visitará
denominado t criterio de la,siguiente cada fila a su vez. Podemos describir si la fila actual cumple con el
manera:

Escribiré el nombre del equipo de la fila t en la tabla Equipo, si existe una fila m en la tabla
Miembro donde el valor del entrenador m.Coach es el mismo que el del gerente del equipo t.Manager.

Ahora podemos traducir esto casi directamente a SQL usando una consulta anidada (discutida en el Capítulo 4). Una
posible consulta sería:

SELECCIONE t.Nombre del equipo DESDE el equipo t

DONDE EXISTE
(SELECCIONE * FROM Miembro m DONDE m.Coach = t.Manager);

¿Hay alternativas?
Los primeros intentos de consultas no son necesariamente los más elegantes. Después de todo, estamos siguiendo esta ruta
porque estábamos perplejos en primer lugar. Esto puede no ser un problema para la ejecución de la consulta, ya que es probable
que el optimizador encuentre un proceso eficiente. Sin embargo, una declaración SQL poco elegante puede ser difícil de entender
para usted y para otros en un momento posterior. Seguir la técnica de resolver la consulta a mano y describir las condiciones a
menudo lo ayuda a comprender lo que está tratando de hacer. Eso a menudo hace que la consulta parezca mucho más fácil de lo
que pensaba al principio.

190
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Habiendo hecho un primer intento con la consulta descrita en la sección anterior, podríamos darnos cuenta de que podríamos haberlo
pensado de esta manera: “El gerente solo tiene que estar en el grupo de entrenadores”. Podemos encontrar fácilmente los ID de los entrenadores
con la consulta:

SELECCIONA m.Coach FROM Miembro m;

Luego podemos usar eso en una consulta anidada, como se muestra aquí:

SELECCIONE t.Nombre del equipo DESDE

Equipo t DONDE t.Gerente EN (SELECCIONE


m.Coach DESDE Miembro m);

Para mí, la consulta anterior es más simple y más fácil de entender que la anterior a pesar de que tienen resultados equivalentes.

Podríamos haber expresado la condición ilustrada en la figura 11-13 de esta manera:

Si tengo filas t en la tabla Team y m en la tabla Member, escribiré TeamName


de la fila t en la tabla Equipo, si t.Manager = m.Coach

Aquí está la oración anterior traducida a SQL:

SELECCIONE t.TeamName FROM Team t, Member m DONDE


t.Manager = m.Coach;

La consulta anterior se puede reformular como una combinación:

SELECCIONE t.TeamName

FROM Team t INNER JOIN Miembro m ON t.Manager = m.Coach;

Personalmente, no encuentro la combinación particularmente intuitiva para esta consulta. Dudo si alguien más mirando
la consulta entendería rápidamente su propósito.
Dado que hay varias opciones para formular esta consulta, puede ser útil verificar sus eficiencias relativas (como se discutió en el
Capítulo 10) si cree que eso podría ser importante (poco probable en este caso). Si agregamos una frase DISTINCT en la cláusula SELECT para las
consultas de combinación, las cuatro alternativas producirán el mismo resultado. Para SQL Server 2012, cada una de las consultas tenía el mismo plan
de ejecución, por lo que todas se llevaron a cabo exactamente de la misma manera.

Comprobación de consultas
Escribimos una consulta, la ejecutamos y obtuvimos algunos resultados. ¿Está todo bien y bien? No necesariamente. Así como los primeros intentos
de una consulta pueden no ser elegantes, tampoco pueden ser correctos. Los errores pueden surgir de errores simples en la sintaxis de la consulta.
Suelen ser fáciles de detectar y corregir. Sin embargo, los errores que resultan de malentendidos sutiles de la pregunta o de los datos pueden ser más
difíciles de encontrar.
No puedo ofrecer una forma infalible de verificar que su consulta sea correcta, pero puedo darle algunas ideas para detectar posibles
errores. Básicamente, se reducen a verificar que no tenga filas adicionales incorrectas en su resultado y verificar que no falte ninguna fila. En esta
sección, veremos formas de detectar que su consulta podría tener un problema. En el próximo capítulo, veremos algunos de los errores comunes que
podrían estar detrás de los errores.

191
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Marque una fila que debe devolverse


Es una buena idea tener una idea aproximada de cuántas filas debe devolver su consulta: ninguna, una, algunas o muchas. Si
obtiene un número sorprendente, eso puede ser una pista de que algo podría estar mal. A continuación, eche un vistazo a sus
datos y determine un registro o fila que la consulta debería devolver. En nuestro ejemplo sobre equipos con gerentes como
entrenadores, podemos revisar las tablas y encontrar un equipo que satisfaga la consulta. En la Figura 11-13 Recuerde que algunas
, vemos
consultas pueden legítimamente noque TeamB
tener cumple
resultados. con
Por las condiciones,
ejemplo, así que verifica
es perfectamente razonablequeque,
estecon
equipo esté en
los datos quelatenemos
salida. en
un momento determinado, ningún equipo esté dirigido por un entrenador. Sin embargo, su consulta debe funcionar en todas
las situaciones. Si es posible, haga una copia de las tablas, modifique los datos para que una fila cumpla la condición y verifique
que se devuelva correctamente.

Marque una fila que no debe devolverse


Similar a verificar una fila que debe devolverse, revise los datos y encuentre un equipo que no
tener un entrenador como un pesebre. El gerente del equipo A (miembro 239) no aparece como entrenador en la tabla de
miembros, así que asegúrese de que ese equipo no esté incluido en su salida. Una vez más, es una buena idea usar algunos datos
ficticios para verificar esto si los datos reales no cubren todas las eventualidades.

Comprobar condiciones de contorno


Si una consulta tiene algún tipo de comparación numérica,además de verificar los datos de ejemplo que deben devolverse y los
que no, también debemos verificar los casos extremos. Considere una consulta en la que queremos encontrar personas que hayan
sido miembros de nuestro club durante más de diez años. Para estar seguros de la corrección, necesitamos comprobar tres
posibilidades:

• Asegúrese de que no se devuelva ningún registro de alguien con menos de 10 años de membresía (por
ejemplo, 8 años de membresía).

• Asegúrese de que alguien que ha pertenecido al club durante 12 años obtenga su registro.

• Busque a alguien que haya sido miembro durante exactamente 10 años.

La última condición de contorno siempre es complicada. Todo se reduce a una interpretación del lenguaje natural.
pregunta. ¿“Más de diez años” incluye a las personas que se unieron a la temporada hace exactamente diez años? Bueno,
probablemente sí, dado que una sola temporada cubre todo un año. Con comparaciones numéricas de este tipo, la decisión es si
usamos > o >= en la condición seleccionada. Es importante consultar con los usuarios si existe alguna duda sobre la intención de la
consulta.
Encontrar datos en las tablas que están exactamente en los límites no siempre es fácil. Sin embargo, generalmente es
posible cambiar el valor numérico en su consulta para que coincida con los datos. Encuentre un miembro en particular y cambie el
valor con el que está comprobando en la consulta para que coincida con sus años de membresía. Si Harry se unió hace 16 años,
cambie la consulta para comparar con el valor 16 y ver si Harry está incluido (o no) como esperaba.
Otra condición de contorno importante, especialmente para agregados y conteos (tratados en el Capítulo 8),
es el valor 0. Considere una consulta como "Buscar miembros que hayan ingresado a menos de seis torneos".
Hacer un conteo agrupado por en la tabla Entrada seguramente devolverá algunas filas, y podemos verificar aquellas que tienen
menos de, más de o exactamente seis entradas. Sin embargo, ¿qué pasa con los miembros que nunca han participado en un
torneo? No aparecerán en la tabla Entrada y no aparecerán en los resultados. Por lo tanto, cada vez que se trate de agregados,
siempre verifique lo que sucede para un conteo de 0. Por ejemplo, ¿su consulta devuelve miembros que no han ingresado a torneos?

192
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Comprobar valores nulos


Tenga en cuenta que algunos de los valores con los que está comprobando pueden ser nulos (discutidos en el Capítulo 2). ¿Cómo aborda su consulta sobre
los administradores de equipo la situación en la que el campo Administrador es nulo? Pruébelo en algunos datos ficticios y vea. ¿Qué esperamos (o queremos)
que suceda si hay un valor nulo en el campo JoinDate cuando ejecutamos la consulta sobre la duración de la membresía?

Resumen
La primera regla para iniciar una consulta es no entrar en pánico. La siguiente regla es dar pequeños pasos y mirar el resultado intermedio para ver si
lo que has hecho hasta ahora te está ayudando. Retenga tantas columnas como sea posible en las consultas iniciales para que pueda verificar que comprende
lo que está sucediendo.
La figura 11-14 ofrece un resumen de algunos de los pasos que puede seguir cuando comienza una consulta por primera vez. El diagrama no cubre
todo el proceso, pero debería poder hacer un comienzo razonable con estos pasos.
Consulte los capítulos correspondientes para obtener más ayuda.

193
Machine Translated by Google

CAPÍTULO 11 ÿ CÓMO ABORDAR UNA CONSULTA

Encuentra las mesas que necesitas.


Mirar datos de ejemplo

palabras clave

sin ayuda
¿Aún no

buscar palabras clave tienes idea?

¿Ambas cosas? ¿No? ¿Cada?


Determinar cómo se relacionan
las tablas a partir del modelo de
datos

¿Necesitas mirar 2
División
No filas?
(Capítulo 7)

No Sí Sí
¿Crees que las
uniones estarán bien?

(Ambas cosas) (No)


Unirse a uno mismo Diferencia

(Capítulo 5) (Capítulo 7)
Únete a las mesas

(Capítulo 3)
Intersección

(Capítulo 7)

Seleccione las filas requeridas

(Capitulo 2)
Pruebe el enfoque de resultados.

Consulte este capítulo para obtener pistas.

Columnas requeridas del proyecto


(Capitulo 2)

Controlar

Comprobar los datos que deben devolverse

Comprobar si hay datos que no deben devolverse

Compruebe las condiciones de contorno y 0 para comparaciones numéricas


Compruebe lo que sucede con los valores nulos

Figura 11-14. Algunos pasos para ayudarlo a comenzar con una consulta complicada

194
Machine Translated by Google

CAPÍTULO 12

Problemas comunes

En este libro, hemos analizado diferentes formas de abordar una variedad de categorías de consultas. Sin embargo,
incluso si una consulta recupera algunas filas que parecen válidas, es posible que no todo esté bien. En el capítulo anterior,
analizamos la importancia de verificar la salida para confirmar que (al menos algunas) de las filas esperadas se recuperan, así
como verificar que (al menos algunas) filas incorrectas (o irrelevantes) no se recuperan. siendo devuelto.
Los problemas que pueden ocurrir en las consultas no son solo una cuestión de tener una sintaxis incorrecta en las
declaraciones SQL, aunque eso ciertamente puede suceder. Los problemas con el diseño de las tablas o con los valores de los
datos también pueden afectar la precisión de las consultas. En este capítulo, veremos algunos problemas comunes de diseño y
datos, y también algunos de los errores sintácticos más comunes.

Diseño deficiente de la base de datos

Un buen diseño de base de datos es absolutamente esencial para poder extraer información precisa. Desafortunadamente, a
veces se enfrentará a bases de datos mal diseñadas y mantenidas. A menudo no hay mucho que puedas hacer. A veces,
puede extraer algo que se parece a la información requerida, pero debe presentarse con la advertencia de que los datos
subyacentes probablemente no sean consistentes. En esta sección, analizamos algunos problemas comunes y cómo se pueden
mitigar.

Datos que no están normalizados


Uno de los errores de diseño de datos más comunes es tener tablas que no están normalizadas. Vimos un ejemplo de esto
en el Capítulo 1. En lugar de tener dos tablas, una para los miembros y otra para la información de los miembros, como las
, esto tiene la
cuotas, todos estos datos se almacenaron en una sola tabla. Como se puede ver en la Figura 12-1 , el efecto de almacenar
la información de la tarifa varias veces.

© Clare Churcher 2016 195


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_12
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Figura 12-1. Una tabla de miembros no normalizada que contiene información sobre tarifas

¿Qué sucede ahora si se nos pide encontrar la tarifa para los miembros senior? La consulta aquí dará como resultado dos
valores: 300 y 250.

SELECCIONE UNA TARIFA DISTINTA

DE Miembro
DONDE Tipo de miembro = 'Senior'

Aunque los dos valores obtenidos por la consulta pueden resultar sorprendentes, no hay nada malo con la consulta o el
resultado. El valor de Brenda Nolan, que es inconsistente con los otros miembros principales, nos da el resultado de la tarifa
adicional. Ese valor puede ser un error tipográfico, o puede indicar algún tipo de descuento para Brenda, o puede ser una instancia
de la tarifa del año pasado que no se ha actualizado. En cualquier caso, hay un problema con el diseño. El diseño debe permitir que
las tarifas regulares para cada grado se registren de manera consistente y, si es necesario, permitir el almacenamiento de regímenes
de descuento adicionales. En este punto, además de rediseñar las tablas, no podemos hacer nada más que devolver la lista de
honorarios que se han registrado contra los miembros principales. Vale la pena entender los problemas subyacentes.

Otro problema que puede encontrar es una sola tabla que almacena datos de varios valores. Las versiones de las mesas de
club que hemos estado usando permiten que un miembro pertenezca a un solo equipo. El club puede evolucionar para tener varios
tipos diferentes de equipos (equipos interclubes, equipos sociales, parejas, cuartetos, etc.) a los que los miembros pueden pertenecer
al mismo tiempo. Cuando surge el requisito de que se registre un segundo equipo contra un miembro, una solución común a corto plazo
es agregar otra columna Equipo a la tabla existente. La figura 12-2 muestra cómo la tabla de miembros podría haber evolucionado para
permitir que los miembros se asocien con hasta tres equipos.

Figura 12-2. Diseño de mesa deficiente para almacenar más de un equipo para un miembro

196
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Ahora, supongamos que se nos pide que encontremos esos miembros en TeamB. Brenda tiene TeamB en la columna Team1,
Helen tiene TeamB en la columna Team2 y Thomas tiene TeamB en la columna Team3. Necesitamos verificar cada columna de
equipo para la existencia de TeamA. Esto no es difícil, como muestra la consulta aquí:

SELECCIONE * DE Miembro
DONDE Equipo1 = 'EquipoB' O Equipo2 = 'EquipoB' O Equipo3 = 'EquipoB';

Si bien podemos extraer la información que necesitamos de la tabla de la Figura 12-2 , el diseño va a causar
problemas. Tendremos problemas si tenemos consultas como "Buscar miembros que estén en el Equipo A y en el Equipo B" o
"Buscar miembros que estén en más de dos equipos". Probablemente podría idear consultas que respondieran a estas preguntas, pero
serían desgarbadas. Pediría que la base de datos se rediseñara correctamente antes de intentar cumplir con tales solicitudes. Si
encuentra resistencia, puede preguntarles qué harán si un miembro pertenece a cuatro equipos o tal vez a veinte equipos.

Si los miembros pueden pertenecer a varios equipos, tenemos una relación Muchos - Muchos, que debe representarse en una base
de datos relacional con una tabla de Membresía intermedia 1 — algo como el de la Figura 12-3 .

Figura 12-3. Una tabla de Membresía que registra la relación entre miembros y equipos

La tabla de Membresía en la Figura 12-3 registra las relaciones entre los miembros y los equipos y es muy similar a la tabla
de Entrada, que registra las relaciones entre los miembros y los torneos. La Membresía
Será necesario unir la tabla con la tabla de miembros para encontrar los nombres asociados, pero si se hace eso, tendremos la
misma información que en la Figura 12-2 . Con la nueva tabla Membresía,
como seahora podemos
describe en los usar todasanteriores,
capítulos las operaciones relacionales,
para responder fácilmente
preguntas como "¿Quién está en el Equipo A y el Equipo B?" y "¿Quién está en tres o más equipos?"

Podemos crear una tabla de Membresía con el siguiente código SQL. La tabla incluye solo dos claves foráneas,
a las tablas Miembro y Equipo existentes, y esos campos también forman una clave principal concatenada.

CREAR TABLA Membresía(


ID de miembro INT REFERENCIAS CLAVE EXTRANJERAS Miembro,
Equipo CHAR(20) REFERENCIAS CLAVE EXTRANJERAS Equipo,
CLAVE PRINCIPAL (ID de miembro, equipo));

1 Consulte mi libro Beginning Database Design (Nueva York: Apress, 20xx) para obtener más información.

197
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Si no le importa un poco de manipulación manual, puede completar la nueva tabla de Membresía con
consultas de actualización repetidas como la que se muestra aquí:

INSERTAR EN Membresía (MemberID, Team)


SELECCIONE ID de miembro, 'Equipo A'
DE Miembro
DONDE Equipo1 = 'EquipoA' O Equipo2 = 'EquipoA' O Equipo3 = 'EquipoA'

La consulta encuentra a cada miembro que está en TeamA y crea una fila adecuada en la tabla Membership.
Si no hay demasiados equipos, puede modificar manualmente la segunda y la última línea de la consulta para cada equipo (Equipo
A, Equipo B, etc.) y completar la nueva tabla de Membresía con bastante rapidez. Luego debe eliminar las columnas Equipo de la
tabla Miembro en la Figura 12-2 , y la base de datos mejorará mucho.

Tablas sin clave primaria La sección anterior dio un

ejemplo de los problemas que puede encontrar si la base de datos subyacente tiene tablas inapropiadas. A veces encontrará
que la base de datos tiene las tablas correctas, pero no tienen restricciones adecuadas de clave principal o externa. En estos
casos, es probable que los valores de datos subyacentes sean inconsistentes. Si bien sus consultas pueden formularse
correctamente, los resultados no serán confiables. En esta sección, verá cómo puede usar consultas para encontrar algunas
inconsistencias que pueden estar presentes en sus datos.
Suponga que la tabla Membresía en la Figura 12-3 se creó sin una clave principal. Esto permitiría que la tabla tenga filas
duplicadas. Por ejemplo, podríamos tener dos filas idénticas para el miembro 153. Una consulta para contar el número de miembros
en el equipo B.2 en TeamB producirá un resultado incorrecto.

Si intenta agregar una clave principal cuando ya existen duplicados, obtendrá un error. ¡Esta es una manera de encontrar
dónde están los problemas! Antes de que pueda agregar una clave principal, deberá encontrar las filas duplicadas e investigar
cómo resolver el problema. Una forma conveniente de encontrar valores duplicados es hacer una consulta GROUP BY (vea el
Capítulo 7) en los campos que deberían ser únicos y usar una cláusula HAVING para encontrar aquellos con dos o más entradas.
La siguiente consulta devolverá valores duplicados para nuestros posibles campos de clave principal MemberID y Team:

SELECCIONE ID de miembro, equipo, recuento (*)


DESDE Membresía
GRUPO POR ID de miembro, equipo
HABIENDO Recuento(*) > 1;

Si la tabla tiene campos que no sean los campos de clave principal, debe inspeccionar manualmente los valores en esos
columnas para decidir qué fila debe eliminarse. La tabla Membresía, que solo tiene campos de clave principal, causa un problema
diferente. ¿Cómo eliminamos solo una copia de la fila para el miembro 153 en TeamB? Debido a que todas las filas son iguales,
no podemos diferenciarlas, por lo que cualquier consulta que elimine una fila eliminará ambas. Su software puede tener una
interfaz de tipo tabular que le permitirá eliminar solo una de las filas, pero si no, es posible que deba eliminar ambas filas y volver a
agregar una manualmente. Si hay muchos valores duplicados, otra forma de resolver la situación es crear una nueva tabla y luego
insertar solo los valores distintos de la tabla original. La siguiente consulta muestra cómo completar la nueva tabla NewMembership :

INSERTAR EN NewMembership
SELECCIONE DISTINCT ID de miembro, equipo
DE Membresía;

2 Esta es la diferencia entre una relación que se define con tuplas únicas y una tabla que puede tener filas duplicadas.
Consulte el Apéndice 2 para obtener más información.

198
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Luego deberá eliminar todas las restricciones de clave externa que hacen referencia a la tabla anterior, eliminar esa tabla, cambiar
el nombre de la nueva tabla y volver a crear las claves externas. ¡Es más fácil asegurarse de que cada tabla tenga una clave principal
desde el principio!

Tablas con claves foráneas faltantes Otro problema es tener una

tabla de pertenencia (como en la figura 12-3 ) sin restricciones de clave foránea. Entonces podemos encontrarnos con el problema de
tener una fila para el miembro 1118 en el EquipoA cuando ningún miembro 1118 aparece en la tabla de miembros. No podremos
agregar una restricción de clave externa si los datos tienen este tipo de problema.

Hay varias formas de encontrar dichos valores de MemberID en la tabla Membership que no tienen una entrada coincidente
en la tabla Member. Una forma es usar una consulta anidada (discutida en el Capítulo 4), como se muestra aquí:

SELECCIONE ms.MemberID DE Membresía ms


DONDE ms.MemberID NO EN (SELECCIONE
m.MemberID DE Miembro m);

Habiendo encontrado los valores no coincidentes para el error , luego tendremos que decidir si es un tipográfico
de ID de miembro o si nos falta un miembro de la tabla de miembros.
Cuando los datos estén en un estado consistente, será posible agregar una restricción de clave externa a la tabla de
membresía para asegurarse de que se mantenga así. La siguiente consulta agregará la restricción al MemberID
campo:

Membresía ALTER TABLE


AÑADIR CLAVE EXTRANJERA (ID de miembro)

REFERENCIAS Miembro;

Datos similares en dos tablas


A veces, una base de datos puede tener tablas adicionales que no son necesarias y que causarán problemas. Un ejemplo para nuestra
base de datos de clubes podría ser tener una tabla separada para entrenadores o directores técnicos, como se muestra en la Figura
La 12-4 .
justificación podría haber sido que la tabla adicional facilitaría la creación de listas de entrenadores y sus números de teléfono (que, de lo
contrario, requeriría una autocombinación o una consulta anidada).

199
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Figura 12-4. Una tabla adicional para entrenadores puede generar datos inconsistentes

La mesa adicional inevitablemente causará problemas. En la Figura 12-4 , ya vemos datos inconsistentes para el número
de teléfono de William Cooper. La única cura real es deshacerse de la mesa extra.
Si el propósito de una tabla adicional como la de la figura 12-4 no está claro, podemos usar operaciones de conjuntos para investigar
qué miembros aparecen en cada una de las tablas. El operador de intersección encontrará filas para las personas que están en ambas
tablas, y el operador de diferencia encontrará las personas que están en una y no en la otra.
Esto puede ayudar a comprender lo que representan las tablas.
Una vez que el diseño sea correcto, la creación de una vista que muestre la información del entrenador sería útil para los usuarios que
no desean crear uniones propias cada vez que desean información solo sobre los entrenadores. La siguiente consulta hace el truco:

CREAR VER CoachInfo COMO


SELECCIONE * DE Miembro
DONDE ID DE MIEMBRO EN

(SELECCIONE Entrenador DE Miembro);

Tipos inapropiados Tener los campos en

una tabla creados con tipos inapropiados es otro problema que puede hacer que las consultas parezcan que no se están comportando. He
visto bases de datos completas donde cada campo es un campo de texto predeterminado.
Tener el tipo de campo incorrecto significa que los datos pierden una gran cantidad de verificación de validez. Por ejemplo, si nuestra
tabla de Miembros tuviera todos los campos de texto, podríamos terminar con valores como "16a" o "1o" en la columna Hándicap, que solo
debería tener números enteros, o texto como "Brenda" en la columna Entrenador, que solo debe contener identificaciones de miembros.

200
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Dejando a un lado los valores ingresados incorrectamente, los tipos inapropiados dan lugar a otros problemas. Cada tipo tiene sus
propias reglas para ordenar los valores. Los tipos de texto se ordenan alfabéticamente, los números se ordenan numéricamente y las fechas se
ordenan cronológicamente. Los diferentes pedidos claramente serán un problema si agregamos una cláusula ORDER BY a una consulta. Un
campo de texto que contenga números se ordenará alfabéticamente, dando un orden como "1", "15", "109", "20", "245" y "33", como se describe
en el Capítulo 2.
Los tipos incorrectos también causan problemas al hacer comparaciones. Si solicitamos que se comparen los valores, la comparación
utilizada dependerá de cómo se ordene el tipo de campo particular involucrado. Para los números ingresados en un campo de texto, obtendremos
comparaciones como “109” < “15” o “33” > “245” según el orden descrito en el párrafo anterior. Esto provocará alguna salida rara si preguntamos
por personas con minusvalía inferior a 5, por ejemplo. Puede ser difícil determinar qué es lo que va mal, porque la sintaxis de la consulta está bien
y los datos parecen estar bien. Es posible que ir detrás de escena para verificar el tipo de datos no sea algo que sea inmediatamente obvio.

Es posible cambiar el tipo de una columna en una tabla existente, pero me da un poco de miedo. Por ejemplo, si cambia de texto a
valores numéricos, "10" probablemente estará bien, pero "1o" provocará un error. Prefiero un enfoque más conservador: hago una nueva tabla
con los tipos apropiados y luego inserto los valores antiguos con la ayuda de una función de conversión. La consulta que sigue muestra cómo
podríamos llenar una nueva tabla NewMember con ID y nombres y con los valores de texto antiguos para la columna Handicap convertidos en
valores numéricos:

INSERTAR EN NewMember (MemberID, LastName, FirstName, Handicap)


SELECCIONE ID de miembro, Apellido, Nombre, CONVERTIR (Hándicap INT)
DE Miembro;

De esta manera, aún tenemos los datos originales si las conversiones resultan en algo inesperado.

Problemas con los valores de datos


Incluso con una base de datos bien diseñada, todavía tenemos el problema de la precisión de los datos que se han ingresado.
Como diseñador de consultas, no puede ser responsable de algunos problemas de precisión. Si la dirección de una persona se ingresó
incorrectamente, no hay mucho que nadie pueda hacer para encontrar o solucionar el problema (aparte de esperar a que se devuelva el correo al
remitente). Sin embargo, puede ser consciente de una serie de cosas, e incluso si no puede solucionar los problemas, al menos puede activar
algunas alarmas. Además, a veces es posible corregir algunos datos problemáticos con un uso cuidadoso de las consultas de actualización.

Nulos inesperados Los nulos pueden

causar todo tipo de problemas en las bases de datos. El verdadero problema (como se discutió en el Capítulo 2) es que un valor nulo puede
significar que el valor es desconocido o que el valor no se aplica a un registro en particular. Si un miembro de nuestro club tiene un valor nulo para
su campo Equipo, podría significar que no está en un equipo o podría significar que está en un equipo, pero no hemos registrado cuál. Al igual que
con otros problemas de datos, no hay mucho que podamos hacer al respecto. Sin embargo, con algo como el campo Género, sabemos que para el
club de golf, todos los miembros deben identificarse como hombres o mujeres. Los nulos significan que para algunos miembros no se ha registrado
el género.
Lo mismo se aplica a campos como la fecha de nacimiento.
Si, por ejemplo, se le solicita una lista de los hombres en el club, a menudo es una buena idea ejecutar también otra consulta para aquellas
filas donde Sexo ES Nulo. Luego puede decirle a su cliente: "Aquí
enfoque
están
puede
los hombres,
ayudar a evitar
y aquícartas
están de
loscaballeros
miembros agraviados
de los que no
queestoy
no aparecen
seguro". en
Tal
la lista.

Tenga en cuenta las diferencias entre las consultas con los dos recuentos siguientes: COUNT(*) y COUNT(Gender) . El
primero contará todas las filas de la base de datos; el segundo contará todas las filas con un valor no nulo para el género. En el club de golf
ideal, estos serían los mismos. En la práctica, es posible que no lo sean.

201
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Ortografía incorrecta o inconsistente


Cualquier base de datos tendrá errores de ortografía en los datos en algún momento. El Sr. Philips puede aparecer como
Phillips, Philipps o Philps por varias razones, que van desde una letra ilegible en el formulario de solicitud hasta un simple
error de ingreso de datos. Si está tratando de encontrar información sobre el Sr. Philips y sospecha que puede haber un
problema, puede usar funciones o comodines para encontrar datos similares. Diferentes productos tienen diferentes formas de
hacer esto.
Podemos usar la palabra clave LIKE para encontrar grafías similares. El símbolo comodín % ( * en Access) representa
cualquier grupo de caracteres. Nuestras diversas versiones de la ortografía de Philips se recuperarían de la siguiente manera
consulta:

SELECCIONE * DE Miembro
DONDE Apellido LIKE 'Phil%';

Otro problema relacionado con la ortografía incorrecta o incoherente surge cuando espera un conjunto particular de
valores o categorías en un campo. Por ejemplo, en nuestra tabla de miembros, es posible que esperemos valores M o F
en la columna Sexo, pero puede haber un valor impar de hombre o m. En la columna MemberType, esperamos que Junior
esté diseñado con las, Sénior , o asociado
restricciones , pero
de verificación adecuadas o claves
en la práctica externas,
puede esto
encontrar no oserá
jnior un. Si
señor las tablas
problema. han
Sin sido
embargo, a
menudo estas restricciones no están presentes, por lo que es útil verificar las entradas problemáticas con una consulta como
la que se muestra aquí:

SELECCIONE * DE Miembro
DONDE MemberType NOT IN ('Senior', 'Junior', 'Associate');

Habiendo encontrado las filas que no se ajustan a las expectativas, es posible modificar los datos y luego aplicar una
restricción de verificación para que permanezca consistente. Por ejemplo, la siguiente consulta aplicará una restricción en el
campo MemberType para que solo se puedan ingresar los valores válidos:

Miembro de ALTER TABLE


AGREGAR RESTRICCIÓN Chk_type CHECK(MemberType IN
('Senior', 'Junior', 'Asociado'));

Caracteres extraños en campos de texto


Un problema común al tratar de recuperar datos que coinciden con un valor de texto son los espacios iniciales o finales y
otros caracteres no imprimibles que se han introducido en los datos.
Si tenemos un campo como FirstName en nuestra base de datos, por ejemplo, podemos encontrar que hay algunos espacios
antes o después del nombre. A veces, si se especifica que un campo de caracteres tiene una longitud particular, se pueden
' '
Dan condición
agregar espacios finales. Si una fila tiene un nombre que se ha almacenado como luego una cláusula =WHERE
FirstName con
'Dan', es el
posible
que no se recupere esa fila. La mayoría del software de base de datos tendrá varias funciones para manejar texto. Es probable
que haya formas de funciones de recorte , que eliminen los espacios al principio y al final de los valores de texto. Consulte su
documentación para ver qué tiene su implementación.
La función RTRIM() en la instrucción SQL que sigue eliminará los espacios del extremo derecho del
Valor FirstName antes de hacer la comparación:

SELECCIONE * DE Miembro
WHERE RTRIM(Nombre) = 'Dan';

202
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

La consulta anterior no elimina los espacios del campo de forma permanente. La función RTRIM() simplemente devuelve un valor sin los
espacios para hacer la comparación. Sin embargo, puede utilizar consultas de actualización para solucionar de forma permanente algunas de
estas incoherencias de datos. La consulta que sigue muestra cómo asegurarse de que ningún valor en la columna FirstName de la tabla Member
tenga espacios iniciales ( LTRIM() ) o finales ( RTRIM() ).
Esencialmente reemplaza todos los valores con valores recortados:

ACTUALIZAR miembro

SET Nombre = RTRIM(LTRIM(Nombre));

Un problema más preocupante son los caracteres que parecen espacios pero en realidad son otros espacios en blanco.
caracteres. Esto ocurre a veces cuando los datos se cortan y pegan o se mueven entre varios productos y diferentes implementaciones.
Esto puede tomar algo de seguimiento.
Otros dos errores de ingreso de datos son los números 0 (cero) y 1 (uno) que se ingresan en lugar de las letras
o (oh) y l (el). Puede pasar horas tratando de depurar una consulta que busca "John" o "Bill", pero si los datos subyacentes se ingresaron por
error como "J0hn" o "Bi11", buscará en vano.
La moraleja es que pueden suceder cosas extrañas con los valores de los datos, por lo que cuando falla la solución de problemas de la
sintaxis de su consulta, verifique los datos subyacentes.

Caso inconsistente en campos de texto


Si su implementación de SQL distingue entre mayúsculas y minúsculas, debe tener en cuenta que algunos valores de datos pueden no
tener el caso esperado. Es posible que Dan haya ingresado incorrectamente su primer nombre en la tabla de miembros como "dan". En
implementaciones que distinguen entre mayúsculas y minúsculas, una consulta con la cláusula WHERE FirstName = 'Dan' no recuperará su
información. Como se mencionó en el Capítulo 2, usar una función que convierta cadenas de caracteres a mayúsculas ayudará a encontrar
las filas correctas. En la consulta que sigue, convertimos FirstName (temporalmente) a mayúsculas y luego lo comparamos con la versión en
mayúsculas de lo que estamos buscando:

SELECCIONE * DE Miembro

WHERE UPPER(Nombre) = 'DAN';

Es bastante difícil encontrar problemas con las mayúsculas y minúsculas en los nombres porque no todos los nombres se
conforman con estar en minúsculas con una primera letra mayúscula; por ejemplo, de Vere y McLennan. Pero, para campos como Género (M
o F) o MemberType (Junior, Senior o Associate), sabemos cuáles esperamos que sean los valores. La mejor forma de asegurarse de que sean
coherentes es poner una restricción de verificación en el campo, como se explicó anteriormente en este capítulo.

Diagnóstico de problemas
En las secciones anteriores, vimos problemas que pueden surgir con un diseño de base de datos deficiente y datos inconsistentes o
incorrectos. Sin embargo, la mayor parte del tiempo, si el resultado de su consulta no se ve bien, probablemente se deba a que tiene una
instrucción SQL incorrecta. La declaración puede estar recuperando filas que son diferentes de lo que se esperaba. En el Capítulo 10 hay una
sección sobre algunas formas en que puede verificar si el resultado de una consulta es el esperado.

En el capítulo anterior, sugerí una forma de abordar las consultas que le permite construir la consulta lentamente para que pueda verificar
que cada paso devuelva las filas apropiadas. Sin embargo, si se le presenta una consulta completa y compleja que no se entrega como se
esperaba, debe reducirla hasta encontrar dónde radica el problema. Si ha notado un problema, entonces tiene un buen lugar para comenzar. Ha
notado que falta una fila esperada o que se ha recuperado una fila que no cumple con los requisitos. Concéntrese en encontrar en qué parte de la
consulta se encuentra ese problema. Las siguientes secciones ofrecen algunas sugerencias.

203
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Comprobar partes de consultas anidadas de forma independiente


Cuando tiene una consulta anidada dentro de otra, lo primero que debe comprobar es que la parte anidada se está comportando como
tal. Echa un vistazo a esta consulta:

SELECCIONE *

DE Miembro m
DONDE m.MemberType = 'Junior' AND Handicap < (SELECT
AVG(Handicap)
DE miembro);

Si tiene problemas con una consulta como esta, corte y pegue la consulta interna y ejecútela de forma independiente.
Compruebe si está devolviendo el resultado correcto. Si esto está bien, puede intentar hacer la consulta externa por su cuenta. Para hacer
esto, simplemente coloque algún valor en lugar de la consulta interna (como Handicap < 10) y vea si arroja los resultados correctos. Si
puede reducir el problema a una parte de la consulta, ha hecho un buen comienzo.
Este enfoque no funciona si las partes internas y externas de la consulta están relacionadas (consulte el Capítulo 4), pero
algunas de las siguientes técnicas podrían ayudar con esa situación.

Comprender cómo se combinan las tablas Muchas consultas implican


la combinación de tablas con operaciones relacionales (unión, unión, etc.). Asegúrese de entender
cómo se combinan las tablas y si eso es apropiado. Considere una consulta como la siguiente:

SELECCIONE m.Apellido, m.Nombre DE


Miembro m, Entrada e, Torneo t DONDE m.MemberID
= e.MemberID
AND e.TourID = t.TourID AND t.TourType = 'Open' AND e.Year = 2014;

Tres tablas están involucradas en esta consulta. Puede tomar un momento darse cuenta de que se están uniendo.
Asegúrese de que sea apropiado para la pregunta que se hace. El Capítulo 10 tiene ejemplos de palabras clave en preguntas y
las formas apropiadas de combinar tablas.

Eliminar cláusulas WHERE adicionales

Después de combinar tablas, generalmente solo se requieren algunas de las filas resultantes. En la consulta de la sección anterior,
solo se necesita una parte de la cláusula WHERE para las operaciones de combinación. Después de la unión, solo se conservan las filas
que cumplen t.TourType = 'Open' Y e.Year = 2014. Si le faltan filas en su resultado, a menudo es útil eliminar las partes de la cláusula
WHERE que seleccionan un subconjunto final de las filas después de la combinación. Si todavía faltan las filas, entonces sabe que (para
este ejemplo) el problema está ocurriendo en la unión.

Conservar todas las columnas

Soy un gran admirador de decir siempre SELECCIONAR * en las primeras etapas del desarrollo de consultas que involucran uniones. Si
sospechamos que hay un problema con las uniones, al dejar todas las columnas visibles, podemos ver si las condiciones de unión se
comportan como se esperaba. Una vez que estemos satisfechos con las filas recuperadas, podemos retener solo las columnas requeridas.
Sin embargo, si estamos combinando tablas con operaciones de conjuntos, este enfoque será contraproducente, ya que es
fundamental proyectar las columnas correctas (consulte la sección "¿Tiene las columnas correctas en las operaciones de conjuntos?"
más adelante en este capítulo).

204
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Comprobar consultas subyacentes en agregados Si tiene un


problema con una consulta que implica un agregado (por ejemplo, SELECT AVG(Handicap) FROM ... ... ),
DONDEverifique
la consulta
queahaya
SELECT
recuperado
* FROMlas
... filas
y confirme
correctas
queantes
esto de
devuelve
aplicarlas
la función
filas para
delas
agregado.
que desea
Cambie
encontrar
el promedio. De hecho, siempre recomiendo
DÓNDE ... ,hacer
verificar
estosicon
losun
números
agregado,
queporque
se devuelven
de otroson
modo
correctos.
es difícil

Síntomas comunes
Habiendo intentado algunos de los pasos del capítulo anterior, habrá simplificado su consulta para aislar dónde está el problema. En
esta sección, veremos algunos síntomas específicos y algunas causas probables.

No se devuelven filas
Por lo general, es fácil detectar un problema con su consulta cuando no se devuelven filas y sabe que algunas deberían serlo.
Las preguntas que involucran "y" o "ambos" a menudo pueden tener este problema. Por ejemplo, considere una pregunta como
"¿Qué miembros han ingresado a los torneos 24 y 36?" Un primer intento común (y todavía me sorprendo haciendo esto a veces)
es una declaración de consulta como:

SELECCIONAR * DESDE Entrada


DONDE TourID = 24 Y TourID = 36;

La consulta anterior solicita una fila de la tabla Entrada donde TourID tiene simultáneamente dos valores diferentes. Esto nunca
sucede, por lo que no se recuperan filas. La cura consiste en utilizar una autounión (tratada en el Capítulo 5) o una operación de
intersección (tratada en el Capítulo 7).
No obtener filas devueltas de una consulta también puede ser un ejemplo extremo de uno de los problemas en el
Siguiente sección.

Faltan filas
Puede ser difícil detectar si la consulta no incluye algunas filas, especialmente cuando el conjunto de filas recuperadas es grande.
Si obtiene 1000 filas devueltas, es posible que no note que falta una. Se requieren pruebas cuidadosas, y algunas ideas sobre
cómo hacer esto se discutieron en el Capítulo 10. A menudo vale la pena revisar la siguiente lista de errores comunes para ver si
alguno se puede aplicar.

¿Debería tener una unión externa?


El uso de una combinación interna cuando se requiere una combinación externa es un problema muy común. Supongamos que
estamos tratando de obtener una lista de información de miembros que incluye nombres y tarifas. Para ello, necesitamos la tabla
Member (para los nombres) y la tabla Type (para las tarifas). Un primer intento de consulta podría ser el siguiente:

SELECT m.LastName, m.FirstName, t.Fee FROM


Miembro m, Type t WHERE m.MemberType = t.Type;

205
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Sabemos que hay, digamos, 135 miembros, pero solo obtenemos 133 filas de la consulta. El problema aquí es que estamos
realizando una unión interna (consulte el Capítulo 3), por lo que los miembros con un valor nulo para el tipo de miembro no aparecerán
en el resultado. Por supuesto, este puede ser el resultado que desea (aquellos miembros que tienen un tipo y una tarifa), pero no es el
resultado correcto si desea una lista de todos los miembros y las tarifas para aquellos que los tienen.
Una combinación externa (también discutida en el Capítulo 3) que incluye todas las filas de la tabla de miembros resolverá este
problema. Siempre que tenga una combinación, vale la pena pensar en los campos de combinación y considerar lo que desea que
suceda cuando una fila tiene un valor nulo en ese campo.

¿Se han tratado las condiciones de selección con los valores nulos de forma adecuada?

Los valores nulos pueden causar bastantes dolores de cabeza si olvida considerar su efecto en sus consultas. La sección anterior
analizó los valores nulos en un campo de combinación. También debe recordar verificar las comparaciones que involucran campos
que pueden contener valores nulos. Vimos esto en el Capítulo 2 y también antes en este capítulo.
Considere dos consultas en la tabla Miembro con condiciones de selección Género = 'M' y Género <> 'M' .
Es razonable pensar que todas las filas de la tabla de miembros deben ser devueltas por una de estas consultas.
Sin embargo, las filas con un valor nulo en el campo Género devolverán false para ambas condiciones (cualquier comparación con
un valor nulo arroja false ), y la fila no aparecerá en ninguno de los resultados.
Digamos que queremos obtener una lista de miembros de nuestro club que no son particularmente buenos jugadores (quizás
para ofrecerles entrenamiento). Alguien puede sugerir una consulta como la siguiente para encontrar socios que no tengan un hándicap
bajo:

SELECCIONE *
DESDE Miembro m DONDE m.Hándicap > 10;

El problema es que la consulta anterior perderá todos los miembros sin discapacidad. Alterando el DONDE
condición a m.Handicap > 10 O m.Handicap IS Null ayudará en esta situación.

¿Está buscando una coincidencia con un valor de texto?


Es muy perturbador estar tratando de encontrar filas para Jim, poder ver a Jim en la tabla y que su consulta no devuelva nada. Esto
puede deberse a uno de los problemas que analizamos en la sección "Problemas con valores de datos" anteriormente en este capítulo.

Una forma rápida de eliminar la posibilidad de valores de texto dudosos es usar LIKE para las comparaciones. Para
ejemplo, donde tiene = 'Jim' , reemplácelo con LIKE '%Jim%' . Si la consulta luego encuentra la fila que esperaba
(posiblemente junto con algunas otras), sabrá que el problema está en los datos. Como se indicó anteriormente, al colocar el comodín %
(o * en Access) al principio y al final de la cadena, encontrará espacios iniciales o finales y otros caracteres no imprimibles.

¿Ha utilizado AND en lugar de OR?

Discutimos el problema de las consultas que involucran las palabras y o en el capítulo anterior ( en la sección "Detectar palabras clave
en las preguntas"). Recapitularé brevemente. La palabra y se puede usar en inglés natural para describir tanto una unión como una
intersección. Cuando decimos “mujeres y niños”, generalmente nos referimos a la unión del conjunto de hembras y el conjunto de
jóvenes. Cuando decimos “automóviles pequeños y rojos”, nos referimos a la intersección
del conjunto de coches pequeños y del conjunto de coches rojos.

206
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Si estamos buscando "mujeres y niños" y usamos la condición de selección Género = 'F' Y edad < 12, en realidad estamos ,
recuperando la intersección de mujeres y niños (o niñas). Necesitamos que la condición sea Género = 'F' O edad < 12 Es muy fácil
traducir involuntariamente el y en la .pregunta en inglés a un AND en la consulta de manera inapropiada, lo que puede resultar en
filas faltantes. En caso de duda, intente dibujar los diagramas de Venn descritos en el capítulo anterior.

¿Tiene las columnas correctas en las operaciones de conjuntos?

Si su consulta involucra operaciones de intersección o diferencia, el resultado puede tener menos filas de las esperadas porque
inicialmente proyectó las columnas incorrectas. Vimos esto en el Capítulo 7. Aquí hay un breve ejemplo de intersección; el mismo
problema se aplica a las operaciones de diferencia también.
Queremos saber quién ha entrado en los dos torneos 25 y 36. Nos damos cuenta de que necesitamos un
intersección e intente la siguiente consulta:

SELECCIONAR * DESDE Entrada


DONDE TourID = 25
INTERSECARSE

SELECCIONAR * DESDE Entrada


DONDE TourID = 36;

No se devolverán filas de esta consulta, independientemente de los datos subyacentes. La intersección encuentra filas que son
exactamente iguales en cada conjunto. Sin embargo, todas las filas del primer conjunto tendrán 25 como valor para TourID
25, y todas las filas del segundo conjunto tendrán el valor 36. Nunca puede haber una fila que esté en ambos conjuntos.
Lo que buscamos son los ID de miembros que se encuentran en ambos conjuntos, por lo que las cláusulas SELECT en cada parte
de la consulta deben ser SELECT MemberID FROM Entry .
La consulta anterior es un ejemplo extremo de retención de columnas incorrectas, lo que da como resultado que no se
devuelvan filas. La discusión sobre la Figura 7-14 en el Capítulo 7 muestra cómo retener diferentes columnas en las consultas de
intersección y diferencia puede generar resultados muy diferentes. Debe asegurarse de conservar las columnas que son
apropiadas para la pregunta que se hace.

Más filas de las que debería haber


A menudo, es más fácil detectar filas adicionales que notar que faltan filas en el resultado de la consulta. Solo necesita ver un registro
que no esperaba y puede concentrarse en las diferentes partes de su consulta para ver dónde no se pudo excluir. Aquí hay un par de
causas de filas adicionales.

¿Utilizó NOT en lugar de Diferencia?


Con preguntas que contienen las palabras no o cláusulas , una forma segura de obtener filas adicionales es usar una condición en un WHERE
nunca cuando realmente necesita un operador de diferencia. Analizamos este problema en el Capítulo 4. Para recapitular, considere
una pregunta como "¿Qué miembros nunca han ingresado al torneo 25?" Un primer intento común usando una condición de selección
es:

SELECCIONAR * DESDE Entrada


DONDE TourID <> 25;

207
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

La condición en la cláusula WHERE verifica las filas de una en una para ver si deben incluirse en el resultado. Si hay una fila para el miembro
415 que ingresó al torneo 36, esa fila se recuperará, independientemente de la posibilidad de que otra fila muestre que el miembro 415 ingresó al torneo
25. Por ejemplo, si el miembro 415 ingresó al torneo 25 y a otros cuatro torneos, recuperar cuatro filas cuando no esperábamos ninguna.

El procedimiento correcto para este tipo de pregunta es utilizar una consulta anidada (ver Capítulo 4) o el EXCEPTO
operador de diferencia (ver Capítulo 7). Necesitamos encontrar el conjunto de todos los miembros (de la tabla de Miembros) y eliminar el conjunto
de miembros que han ingresado al torneo 25 (de la tabla de Entrada).
Si empleamos el enfoque de proceso, podríamos llegar a la siguiente consulta, que busca el
diferencia entre los dos conjuntos:

SELECCIONE ID de miembro DE miembro


EXCEPTO

SELECCIONE ID de miembro DESDE la entrada


DONDE TourID = 25;

Si comenzamos con un enfoque de resultado, podríamos haber llegado a una consulta anidada, como aquí:

SELECCIONE ID de miembro DE miembro


DONDE NO ENTRA EL IDENTIFICADOR DE MIEMBRO

(SELECCIONE ID de miembro DE la entrada


DONDE TourID = 25);

¿Ha tratado los duplicados de forma adecuada?


A veces se necesita pensar un poco para decidir qué se debe hacer con los registros duplicados recuperados de una consulta. De forma predeterminada,
SQL conservará todos los duplicados. Las siguientes dos solicitudes suenan similares:

• Dame una lista de los nombres de mis clientes.

• Dame una lista de las ciudades en las que viven mis clientes.

En el primero, probablemente esperamos tantas filas como clientes tengamos; si tenemos varios Johns, esperamos que se conserven
todos. En el segundo, esperamos una fila por ciudad. Si tenemos 500 clientes que viven en Christchurch, no esperamos que se devuelvan las 500 filas.

En la consulta para encontrar las ciudades, solo queremos los valores distintos, por lo que debemos usar la palabra clave DISTINCT:

SELECCIONE DISTINTO (Ciudad) DEL Cliente;

Estadísticas o agregados incorrectos Si usamos agregados como contar,

agrupar o promediar y la consulta subyacente pierde filas o devuelve filas adicionales, entonces claramente las estadísticas se verán afectadas. Un
par de otras cosas a considerar son cómo se manejan los valores nulos y los duplicados.

SQL no incluirá ningún campo nulo en sus estadísticas. Por ejemplo, COUNT(Handicap) o AVG(Handicap)
ignorará cualquier fila con valores nulos en el campo Hándicap. También es importante considerar lo que desea hacer con los duplicados,
especialmente para las funciones de conteo. COUNT(Handicap) devolverá el número de miembros que tienen un valor en la columna Handicap.
COUNT(DISTINCT Handicap) devolverá el número de valores diferentes en la columna Handicap; si todos los integrantes tienen un hándicap de 20,
devolverá una cuenta de 1.

208
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

El orden es incorrecto
Si ha utilizado una cláusula ORDER BY en su consulta y tiene problemas con el orden en que se presentan las filas, a menudo hay un
problema con los datos subyacentes. Revise la sección "Problemas con los valores de los datos" anteriormente en este capítulo. Verifique
que los tipos de campo sean apropiados (por ejemplo, los valores numéricos no se almacenan en campos de texto) y que los valores de
texto tienen mayúsculas y minúsculas coherentes y no contienen caracteres extraños.

Errores tipográficos comunes y problemas de sintaxis


A veces, una consulta no se ejecuta debido a un problema simple con la sintaxis, es decir, la forma en que está redactada la
consulta. Los problemas de sintaxis involucran cosas como la falta de paréntesis o la ortografía incorrecta de campos o palabras clave.
Es de esperar que el software de la base de datos le avise si hay un problema con la sintaxis, pero, como algunos editores son bastante
básicos, eso puede ser útil o no para encontrar y corregir el problema. Aquí hay algunas cosas para verificar:

• Comillas : la mayoría de las versiones de SQL requieren comillas simples alrededor de los valores de texto,
como 'Smith' o 'Junior', aunque algunas usan comillas dobles
y pegando en algunas
consultas, circunstancias.
asegúrese de que seSihayan
está cortando
transferido las comillas correctas. Cuando corto y pego las consultas en este libro de Word a Access, las
comillas se ven bien, pero necesito volver a ingresarlas.

También verifique que todas las comillas estén emparejadas correctamente. No utilice comillas alrededor de
valores numéricos. Algo como Handicap < '12' causará problemas si Handicap es un campo numérico.

• Paréntesis: son necesarios en las consultas anidadas y también se pueden usar para mejorar la
legibilidad en muchas consultas (como aquellas con varias combinaciones). Compruebe que todos
los soportes estén emparejados correctamente.

• Nombres de tablas y campos : parece obvio que necesita obtener los nombres de tablas y campos correctos.
Sin embargo, a veces una simple falta de ortografía en el nombre de una tabla o campo puede generar un
mensaje de error ininteligible. Verifique con cuidado.

• Uso de alias : si usa un alias para los nombres de las tablas (por ejemplo, Miembro m), verifique que
ha asociado el alias correcto con cada nombre de campo.

• Ortografía de palabras clave : algunos programas para construir consultas SQL resaltarán las
palabras clave, por lo que es muy evidente si las ha escrito incorrectamente. Si su versión no muestra
esto, verifique también la ortografía de las palabras clave. A menudo escribo FORM en lugar de FROM
o PROMEDIO() en lugar de AVG() .

• IS Null versus = Null : algunas versiones de SQL los tratan de manera bastante diferente. Es nulo
siempre funciona si está tratando de encontrar campos con un valor nulo.

209
Machine Translated by Google

CAPÍTULO 12 ÿ PROBLEMAS COMUNES

Resumen
Antes de que pueda corregir una consulta, debe notar que es incorrecta en primer lugar. Es preferible que encontremos los problemas potenciales
antes de que nuestros usuarios los encuentren por nosotros. Compruebe siempre las filas devueltas de una consulta, como se describe en el
capítulo anterior. Cuando descubra errores, las siguientes son algunas ideas para rastrear la causa del problema:

• Compruebe que las tablas subyacentes estén combinadas correctamente (unión, intersección y
pronto).

• Simplificar la consulta eliminando condiciones de selección y agregados para garantizar la


las filas subyacentes son correctas.

• Conserve todas las columnas de una consulta con combinaciones hasta que esté seguro de que las tablas tienen
sido combinado apropiadamente.

• Verifique cada parte de consultas anidadas o consultas que involucren operaciones de conjuntos de forma independiente.

• Verifique las consultas para preguntas con las palabras y o no para asegurarse de que no ha utilizado condiciones de
selección cuando necesita una operación de conjunto o una consulta anidada.

• Comprobar que las columnas retenidas en las consultas con operaciones set sean las adecuadas.

• Compruebe que los nulos y los duplicados se han tratado correctamente.

• Compruebe que los tipos de datos subyacentes sean correctos y que los valores de los datos sean coherentes.

210
Machine Translated by Google

APÉNDICE 1

Base de datos de ejemplo

La mayoría de los ejemplos de este libro utilizan la base de datos de palos de golf. Visite la página del catálogo de este libro en el
sitio web de Apress, busque en la pestaña Código fuente/Descargas y encontrará una versión de Access de esta base de datos y
también los scripts SQL para crear y completar las tablas. La figura A1-1 muestra cómo se relacionan las tablas de la base de datos y la
figura A1-2 muestra los datos de las tablas.

Figura A1-1. El modelo de datos para la base de datos de palos de golf.

© Clare Churcher 2016 211


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_13
Machine Translated by Google

APÉNDICE 1 ÿ BASE DE DATOS DE EJEMPLO

Figura A1-2. Las tablas y datos para la base de datos de palos de golf.

212
Machine Translated by Google

APÉNDICE 2

Notación relacional

La teoría de bases de datos relacionales se basa en la teoría de conjuntos.1 Cuando consultamos una base de datos, esencialmente
formulamos una pregunta para recuperar un subconjunto que contiene la información que necesitamos. Hay dos enfoques para recuperar
un subconjunto de datos. El álgebra relacional es una descripción de las operaciones a realizar en los datos (en el cuerpo del libro llamamos
a esto el enfoque de proceso). El cálculo relacional describe las condiciones que deben satisfacer los datos recuperados (nos referimos a
esto como el enfoque de resultados). En este apéndice presentamos la notación formal para formular consultas usando álgebra relacional
y cálculo. Esto le permitirá pensar en las consultas desde una perspectiva diferente. Si está interesado en continuar con las matemáticas
formales, hay más publicaciones teóricas disponibles.2 Aquí no se presentan conceptos nuevos que no se hayan discutido anteriormente;
es solo la notación la que es diferente. La notación más formal permite que las consultas se expresen de manera muy concisa y las
matemáticas subyacentes pueden ser útiles cuando se trata de situaciones complejas. Usaremos la base de datos descrita en el Apéndice
1 para los ejemplos.

Introducción
Como ejemplo de cómo pensar en los datos como conjuntos puede ayudarnos, consideremos un conjunto que contiene información
sobre todas las personas en la Tierra. Podemos definir un subconjunto que contenga a todos los hombres, otro que contenga a todos los
golfistas, otro que contenga a las personas mayores de 40 años, y otro que contenga a los italianos. Todos estos conjuntos pueden
superponerse, como se muestra en el diagrama de la Figura A2-1. Este tipo de diagrama se llama diagrama de Venn.

1 La teoría relacional fue presentada por primera vez por el matemático EF Codd en junio de 1970 en su artículo “Un modelo
relacional de datos para grandes bancos de datos compartidos” en Communications of the ACM: 13, pp. 377–387.
2
Por ejemplo: Bases de datos en profundidad: Teoría relacional para profesionales por fecha CJ (Ciudad, estado: O'Reilly, 2005).

© Clare Churcher 2016 C. 213


Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3_14
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Figura A2-1. Diagrama de Venn que muestra subconjuntos de personas

La Figura A2-1 nos ayuda a visualizar los conjuntos que satisfacen criterios tales como hombres italianos mayores de 40 años que juegan al golf (el
donde todos los círculos se superponen) o personas que no juegan al golf (en todas partes del rectángulo grande excepto en el círculo de golfistas).
Estas dos áreas son fáciles de describir; sin embargo, no siempre es sencillo definir el subconjunto que requerimos. El área que contiene golfistas italianos
de 40 años o menos requiere un poco más de esfuerzo para encontrar, y es difícil de describir sin el diagrama para ayudar.

Una base de datos solo es útil si puede extraer con precisión el subconjunto de datos adecuado cuando lo necesite.
A medida que los criterios se vuelven más complejos y aumenta el número de tablas, puede resultar difícil mantener todo en la cabeza y describir
correctamente lo que está tratando de encontrar. Es en estas situaciones más complejas que tener una notación más formal y sucinta puede ser muy
útil.

Relaciones, tuplas y atributos


Es común pensar en una base de datos como un número de tablas. Una tabla (por ejemplo, Persona) tendrá varias columnas.
Cada fila de la tabla representa a una persona individual con los valores apropiados para esa persona que aparecen en cada columna. Más formalmente,
se hace referencia a una base de datos como un conjunto de relaciones, y cada relación es un conjunto de tuplas. Una tupla es un conjunto de valores de
atributo; por ejemplo, {Ali, Brown, 8/2/1967}.
Una relación consta de un encabezado y un cuerpo. El encabezado es una descripción de los datos contenidos en la relación. Parte de esa
descripción es un conjunto de nombres de atributos; por ejemplo, {Nombre, Apellido, Fecha_de_
Nacimiento}. Además, cada atributo tiene un dominio o conjunto de valores permitidos. Por ejemplo, Fecha_de_nacimiento debe ser una fecha válida. Un
dominio puede ser un tipo primitivo (p. ej., entero, cadena) o un tipo definido por el usuario (p. ej., WeekDays
= {lunes, martes, miércoles, jueves, viernes, sábado, domingo}). Un esquema de base de datos es el conjunto de encabezados para todas las relaciones
más las restricciones que se han definido.

214
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

El cuerpo de la relación contiene los valores de los datos. Consiste en tuplas que contienen valores para cada uno de los atributos.

La Tabla A2-1 muestra términos análogos para las dos formas de describir una base de datos.

Cuadro A2-1. Términos comparativos

Término relacional Término de la base de datos

Base de datos (conjunto de relaciones) Base de datos

Relación (conjunto de tuplas) Mesa

Tupla (conjunto de valores de atributo) Fila

Nombre del Atributo Nombre de columna

Dominio Tipo de datos de columna (primitivo o definido por el usuario)

Las principales diferencias entre una tabla (sin clave) y una relación o conjunto de tuplas son que no hay
orden a las tuplas y cada tupla debe ser única.
La figura A2-2 muestra cómo podemos visualizar una relación como un conjunto de tuplas.

Figura A2-2. Una relación es un conjunto de tuplas.

La forma tradicional de representar un conjunto en un diagrama de Venn, como en la Figura A2-1, refuerza el concepto de que no hay orden en los
elementos de un conjunto. No hay elemento primero, siguiente o anterior. El formato habitual de una tabla puede implicar que las filas tienen algún tipo de
orden intrínseco. Cuando consulta una base de datos, teóricamente, las tuplas devueltas no tienen un orden garantizado a menos que especifique un orden
como parte de la consulta.
En la práctica, es probable que una consulta simple devuelva filas en el mismo orden cada vez que se repite porque en el fondo se llevarán a cabo las mismas
operaciones. Sin embargo, con tablas grandes, a medida que cambia el número de tuplas, el número y el orden de las operaciones pueden cambiar para
mejorar la eficiencia, o se puede acceder primero a los datos que se han almacenado en caché previamente. Estos pueden afectar el orden en que se
devuelven las tuplas.

215
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Como se discutió en el Capítulo 1, tener tuplas únicas en una relación es esencial si vamos a poder identificar nuestros datos
correctamente. Si, en la relación de la Figura A2-2, descubrimos que tenemos otra persona llamada John Smith nacido el 6/2/1988,
estaríamos en problemas, porque no podríamos distinguir las tuplas de las dos personas. Necesitamos almacenar suficiente información
sobre las personas para poder diferenciarlas. El concepto de clave primaria (un conjunto de atributos que deben ser únicos para cada
tupla) asegura la unicidad.
Una vez que pensamos en nuestros datos como conjuntos de tuplas, todo el poder de las operaciones con conjuntos está a nuestra disposición.

SQL, Álgebra y Cálculo


SQL es un lenguaje que se basa principalmente en el cálculo relacional. El cálculo relacional describe las condiciones que deben
obedecer las tuplas recuperadas. En la siguiente consulta SQL, la cláusula WHERE describe las tuplas resultantes:

SELECCIONE Apellido, Nombre, Discapacidad, Noche de práctica


DE Miembro, Equipo
WHERE TeamName = Team AND Handicap < 15;

Aunque SQL es un lenguaje basado en cálculo, cada vez hay más palabras clave que sugieren operaciones de conjuntos desde
El álgebra relacional se ha incluido en la sintaxis a lo largo de los años. En muchos casos, esto hace que las consultas sean más
fáciles de entender. La consulta anterior también se puede escribir usando la sintaxis asociada con la operación de unión interna del
álgebra relacional, de la siguiente manera:

SELECCIONE Apellido, Nombre, Discapacidad, Noche de práctica


FROM Miembro INNER JOIN Equipo ON TeamName = Equipo
DONDE Hándicap < 15;

El SQL anterior parece sugerir que primero se realiza la unión y luego se recuperan aquellas tuplas con Handicap < 15.
Este no es el caso en la práctica. SQL es simplemente una descripción de las tuplas resultantes y no implica cómo se realizará la
consulta. El optimizador de consultas de la base de datos determinará cómo se recuperan las tuplas, y un buen optimizador llevaría a
cabo las dos consultas de la misma manera (la más eficiente).

En el resto de este apéndice, veremos una notación más formal para el álgebra y el cálculo relacionales. A menudo
proporcionaré una expresión SQL equivalente y elegiré una que sea similar al álgebra o al cálculo, según la sección. Lo importante que
debe recordar es que todas las expresiones SQL son descripciones del resultado de la consulta y la forma en que se expresan no
determina necesariamente las operaciones involucradas en la recuperación de los datos resultantes.

Álgebra Relacional: Especificando las Operaciones


Con álgebra relacional, describimos consultas considerando una secuencia de operaciones o manipulaciones en las relaciones en
la base de datos. Algunas operaciones actúan sobre una relación (operaciones unarias ), mientras que otras son formas diferentes
de combinar datos de dos relaciones (operaciones binarias ). Cada vez que realizamos una operación sobre una o más relaciones
el resultado es otra relación. Este es un concepto muy poderoso y significa que podemos crear consultas complicadas en pequeños
pasos tomando el resultado de una operación y aplicándole otra operación.

Las operaciones del álgebra relacional y los símbolos comúnmente usados para representarlas se muestran en la Tabla A2-2.

216
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Cuadro A2-2. Operadores relacionales y sus símbolos

Operación Símbolo

Seleccione ÿ

proyecto ÿ

producto cartesiano ×

Unión ÿ

diferencia -

ÿ
unir internamente

intersección ÿ

división ÷

Las operaciones no son completamente independientes. Por ejemplo, más adelante veremos que una unión interna se
define como un producto cartesiano seguido de una selección y un proyecto. Los primeros cinco operadores de la Tabla A2-2
se pueden usar para definir los tres últimos, por lo que SQL no necesita proporcionar palabras clave que representen la división y
la intersección. Sin embargo, es conveniente poder especificar el SQL equivalente para una operación como la combinación
interna porque ocurre con mucha frecuencia en las consultas de la base de datos. Ahora presentaremos una notación más formal
para cada una de las operaciones y mostraremos cómo se puede usar para especificar consultas.

Seleccione

La operación de selección devuelve solo aquellas tuplas de una relación que satisfacen una condición particular que involucra
los atributos. Un ejemplo del uso de una operación de selección sería recuperar todos los miembros senior de nuestra relación
Miembro. La letra griega sigma (ÿ) representa la operación de selección y la condición, MemberType =
'Senior', se especifica en un subíndice. La siguiente expresión muestra la notación para usar select para devolver miembros
senior:

ÿ ÿ ( Miembro
)
MemberType Senior
ÿ=

Se investiga cada tupla de la relación Miembro y, si la tupla cumple la condición, se incluye en la relación resultante. En
términos de tabla, el operador de selección recupera un subconjunto de las filas de la tabla. Se devuelven todos los atributos o
columnas.
En SQL, la cláusula WHERE contiene la condición para el operador de selección y controla las tuplas o filas ( )
que se devuelven. El equivalente SQL de la operación de selección es: ÿ
MemberType Senior ÿ= Miembro
ÿ

SELECCIONE *

DE Miembro m
DONDE m.MemberType = 'Senior';

Tenga en cuenta que la palabra clave SELECT en SQL no tiene nada que ver directamente con la selección de álgebra relacional
operación. Más sobre eso en la siguiente sección.

217
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Proyecto
La operación del proyecto devuelve una relación donde los atributos son un subconjunto de los atributos de una relación. El
operador del proyecto se denota por ÿ (pi), y los atributos se enumeran en un subíndice. En términos de tabla, proyecto
devuelve un subconjunto de las columnas de una tabla. La siguiente declaración devolvería los atributos FirstName y LastName
de cada tupla en la relación Miembro:

ÿ
Nombre Apellido
, (Miembro )

¿Cuántas tuplas o filas esperaría obtener de la relación Miembro como resultado de la


operación ÿNombre(Miembro)? Las tuplas constan del único atributo FirstName. La relación Miembro tiene 20 tuplas,
pero eso incluye dos ocurrencias de William, dos de Robert y tres de Thomas. Anteriormente mencioné que el resultado de
cada operación da como resultado otra relación. El resultado de ÿFirstName(Member) debe ser un conjunto de tuplas únicas.
Todos los duplicados serán eliminados, dejándonos con 16 nombres únicos.
Piense en la operación del proyecto como si devolviera todas las combinaciones únicas de valores para los atributos
especificados.
En SQL, los atributos que devolverá el operador del proyecto se especifican en la cláusula SELECT. Sé que esto parece
perverso, pero recuerde que la sintaxis SQL se basa en cálculo relacional, no en álgebra. El equivalente SQL de la operación de
proyecto ÿFirstName, LastName(Miembro) es:

SELECCIONE DISTINTO Nombre, Apellido


DE Miembro;

Combinando Seleccionar y Proyectar


Como el resultado de una operación algebraica sobre una relación siempre da como resultado otra relación, podemos aplicar
las operaciones sucesivamente. La siguiente expresión primero usa la operación de selección para encontrar todas las tuplas
de los miembros senior (los paréntesis internos) y luego aplica la operación de proyecto para devolver solo los nombres:

ÿ
(ÿ
, Tipo de miembro Senior
Nombre Apellido
= ÿ ÿ
(Miembro ) )

¿El orden de las operaciones hace una diferencia? Considere la siguiente expresión donde el orden
de las operaciones de seleccionar y proyectar se invierte:

ÿ
= ÿ ÿ
(ÿ
MemberType Senior FirstName Apellido , (Miembro ) )

Las tuplas resultantes de la operación inicial del proyecto (paréntesis internos) tienen solo los dos atributos
Nombre y apellido. El atributo MemberType ya no está en las tuplas, por lo que no podemos usarlo en la condición de
selección. La expresión de álgebra no es válida.
La instrucción SQL equivalente a nuestras operaciones combinadas de selección y proyecto es:

SELECCIONE Nombre, Apellido DE Miembro


WHERE MemberType = 'Senior';

Debido a que SQL se basa en cálculo relacional en lugar de álgebra, no existe el concepto de operaciones o
orden en el enunciado anterior. Es solo una descripción de las tuplas que se recuperarán.

218
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Para consultas más complejas, a veces es útil introducir relaciones intermedias para que podamos dividir la consulta en pasos
más pequeños. Por ejemplo, podríamos llamar a la relación resultante de la operación de selección SenMemb, como se muestra a
continuación:

SenMemb ÿ ÿ
ÿ=
MemberType Senior
ÿ (Miembro )

Ahora podemos usar la operación del proyecto en la relación recién nombrada SenMemb para devolver los nombres:

ÿ
Nombre Apellido
,
(SenMemb )

En SQL, podemos usar vistas para dividir las consultas en pasos más simples. Se puede pensar en una vista como
instrucciones para crear una nueva relación temporal:

CREAR VER SenMemb COMO


SELECCIONE * DE Miembro
WHERE MemberType = 'Senior';

La vista se puede utilizar en otras consultas:

SELECCIONE Apellido, Nombre


DESDE SenMemb;

Producto cartesiano
Las operaciones seleccionar y proyectar son operaciones unarias, lo que significa que actúan sobre una sola relación.
Ahora veremos las operaciones binarias, que actúan sobre dos relaciones. El resultado de las operaciones unarias y binarias es
una sola relación.
Un producto cartesiano es la operación binaria más versátil porque se puede aplicar a dos
relaciones. La notación para un producto cartesiano entre dos relaciones Miembro y Equipo es:

Miembro ×Equipo

Cada tupla en un producto cartesiano tendrá un valor para cada atributo de las dos relaciones contribuyentes. Las tuplas
en la relación resultante consisten en cada combinación de tuplas de las relaciones originales. Si una relación tiene N tuplas y la
otra M, entonces las relaciones resultantes tendrán N x M tuplas.
En términos de tablas, el producto cartesiano toma dos tablas de cualquier forma y produce una tabla con una columna para cada
columna de las tablas originales y una fila para cada combinación de las filas originales. La Figura A2-3 muestra tablas abreviadas de
Miembros y Equipos y su producto cartesiano.

219
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Figura A2-3. El producto cartesiano de miembro y equipo

El SQL para un producto cartesiano usa la palabra clave CROSS JOIN, como en la siguiente declaración:

SELECCIONE * DEL Equipo CROSS JOIN de miembros;

Unir internamente

En álgebra relacional, una unión interna se define como un producto cartesiano seguido de una operación de selección que
compara los valores de los atributos de las dos relaciones originales. Los atributos que se comparan deben tener los mismos
dominios.
Haciendo referencia a las tablas de la Figura A2-3, podemos especificar un producto cartesiano seguido de una selección que
devuelve solo aquellas tuplas donde el valor de Team es el mismo que el valor de TeamName:

ÿ
Equipo=NombreEquipo (
Miembro × Equipo )

Podemos usar la operación de unión para producir una expresión equivalente. Se utiliza el símbolo de unión ÿ, y el
seleccionar, o unir, la condición se expresa en un subíndice como se muestra en la siguiente expresión:

Miembro ÿ Equipo Nombre del equipo =


Equipo

Las expresiones anteriores son combinaciones de igualdad en las que la condición de selección utiliza la igualdad. Este
es el tipo de unión más común. El caso más general es un ÿ-join (theta-join) donde la expresión puede incluir comparaciones
como > y <. Una unión natural es aquella en la que las dos relaciones tienen cada una uno o más atributos con el mismo nombre.
Por defecto, la condición de unión será la igualdad de los valores del atributo con el mismo nombre, y uno de esos atributos duplicados
se eliminará del resultado final con una operación de proyecto.
Cuando tenemos expresiones que implican varias operaciones, a menudo tenemos la opción de elegir el orden en que
se aplican las operaciones. Por ejemplo, si queremos recuperar la noche de práctica para el Sr. Pollard, podemos seleccionar
Pollard de la relación Miembro antes de la unión o después del resultado de la unión. Estas dos opciones se muestran aquí:

ÿ ÿ
Noche de práctica Apellido Pollard
=ÿ ÿ
(Me ber ÿÿ m =
Equipo Nombre del equipo ) Equipo )

ÿ
PracticaNoche
ÿ
( ( ( Apellido= Pollard ÿ ÿ Miembro ) ÿÿ =
Equipo Nombre del equipo Equipo)

220
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Las tuplas resultantes de las dos expresiones anteriores son las mismas; sin embargo, el método para obtenerlos es
bastante diferente. El primero implicará primero crear una gran relación que sea el producto cartesiano de Miembro y Equipo. En
la segunda expresión, reducimos el número de tuplas en la relación Miembro a solo las de Pollard y luego construimos un producto
cartesiano mucho más pequeño. Claramente, la segunda expresión será más eficiente.

El hecho de que SQL se base en cálculo en lugar de álgebra no implica ningún orden de operaciones. Si bien la declaración SQL
que sigue podría sugerir que la unión se lleva a cabo primero, es solo una declaración que describe las tuplas que se recuperarán:

SELECCIONE *

DESDE Miembro INTERNO ÚNETE AL EQUIPO ON Equipo = Nombre del equipo

WHERE Apellido = 'Pollard';

El optimizador de consultas en un sistema de base de datos determinará un método efectivo para realizar la consulta.

unión, diferencia e intersección


Debido a que una relación se define como un conjunto de tuplas, las tres operaciones de conjuntos binarios unión (ÿ), diferencia
(ÿ) e intersección (ÿ) se pueden usar para recuperar información. Para el álgebra relacional existe la restricción adicional de que
las dos relaciones involucradas en estas operaciones deben ser compatibles entre sí. Esto significa que las dos relaciones deben tener
el mismo número de atributos y los atributos correspondientes en cada relación deben estar definidos en los mismos dominios.

Por ejemplo, considere dos relaciones con los siguientes atributos:

Personal:{FamilyName, FirstName, Salary}


Estudiantes:{Apellido, Nombre, Dirección, Curso}

Las operaciones de conjuntos nos ayudarán a recuperar los nombres de todas las personas (sindicato), los nombres de
aquellas personas que son tanto estudiantes como miembros del personal (intersección), y aquellos que son estudiantes pero no
miembros del personal y viceversa (diferencia). (¡Esto, por supuesto, hace suposiciones ingenuas sobre la singularidad de los nombres!)
No podemos comparar tuplas en las relaciones tal como están porque tienen atributos diferentes. Personal
y Student no son compatibles con la unión. Uno tiene un Salario mientras que el otro tiene una Dirección y un Curso.
Sin embargo, los nombres se pueden comparar, ya que tienen los mismos dominios (texto) en cada relación. Podemos recuperar
solo los nombres aplicando una operación de proyecto a cada una de las relaciones originales de la siguiente manera:

ÿ
, Nombre
Nombre de la familia
(Personal )

ÿ
,
Apellido, nombre (Alumno )

Estrictamente hablando, para la compatibilidad de la unión, los atributos deben ser idénticos (mismo nombre y dominio).
Sin embargo, en la práctica, solo los dominios deben ser iguales y el orden de los atributos determina lo que se compara. Ahora
podemos aplicar cualquiera de las tres operaciones con conjuntos a las nuevas relaciones compatibles con unión. Por ejemplo:

ÿ
, Nombre
Nombre de la familia
(Personal ) ÿ ÿ
,
Apellido, nombre (Alumno )

221
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

La expresión SQL es:

SELECCIONE FamilyName, FirstName FROM Personal


UNIÓN
SELECCIONE Apellido, Nombre DEL Estudiante;

Podemos seguir aplicando operaciones a los resultados de nuestras expresiones. Si se toma con calma, es bastante
sencillo. Por ejemplo, si queremos encontrar los nombres y salarios de aquellos miembros del personal que también son estudiantes,
podemos construir una serie de operaciones de álgebra relacional comenzando con las relaciones iniciales. Ver lo siguiente:

1. Proyecte los nombres para obtener relaciones compatibles con la unión.

2. Utilice la operación de intersección para encontrar a los miembros del personal que también son estudiantes.

3. Unir el resultado con la relación Staff para que tengamos acceso al atributo Salary.

4. Proyecte los atributos Nombres y Salario.

Puede ver cada una de estas operaciones en la siguiente expresión: simplemente lea los corchetes de adentro hacia afuera:

ÿ
, Salario Apellido
Apellido Nombre , Nombre
((ÿ , ( Personal
)ÿ

ÿ
Apellido, nombre
, (Alumno )) ÿ Nombre de familia =Apellido Y Nombre Nombre
= () Personal )

Unión, diferencia e intersección no son independientes. Una intersección se puede expresar en términos de dos operaciones de
diferencia. Suponiendo que StaffNames y StudentNames sean dos relaciones compatibles con la unión, tenemos que:

Nombres del personal ÿ Nombres de los estudiantes = ÿ Nombres del personal ( Nombres del personal ÿ Nombres de los estudiantes )

Dibuja una secuencia de imágenes para convencerte de esto.


Algunas versiones del lenguaje SQL no implementan la palabra clave INTERSECT porque la consulta puede
ser reformulado, como se acaba de ver, usando EXCEPTO (la sintaxis SQL para la diferencia). La siguiente consulta SQL utiliza la
palabra clave INTERSECT:

SELECCIONE * DE Nombres del personal


INTERSECARSE

SELECCIONE * DE Nombres de Estudiantes;

Se puede construir una consulta equivalente usando la palabra clave EXCEPT:

SELECCIONE * DE Nombres del personal


EXCEPTO

(
SELECCIONE * DE Nombres del personal
EXCEPTO
SELECCIONE * DE Nombres de estudiantes

);

222
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

División
La división es la última de las operaciones de álgebra relacional que consideraremos. La forma más fácil de entender la
operación de división es con un ejemplo.
Si queremos saber qué miembros de nuestro club han entrado en cada torneo, necesitamos dos datos. Necesitamos
información sobre los miembros y los torneos a los que han ingresado, que podemos obtener de la tabla Entrada, y también
necesitamos una lista de todos los torneos, que proviene de la tabla Torneo.

En la Figura A2-4, puede ver cómo funciona la división. Muestra los atributos MemberID y TourID de la relación Entrada
y el atributo TourID de la relación Torneo. El resultado de la división es el conjunto de valores MemberID que tienen una tupla
en la relación Entry para cada valor TourID.

Figura A2-4. Usar el operador de división para encontrar miembros que hayan ingresado a todos los torneos

223
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

La expresión del álgebra relacional para la operación de división en la figura A2-4 es la siguiente:

ÿ
ID de miembro, ID de viaje
() Entrada ÷ ÿ Tour ID (T ) torneo

No hay una palabra clave de SQL para el operador de división. Sin embargo, es posible expresar la división en términos de
otras operaciones algebraicas. Puede ser un poco desalentador si se presenta en un solo paso, por lo que lo tomaremos con calma.
En primer lugar, busque todos los miembros que han entrado en un torneo y, mediante un producto cartesiano, cree tuplas
para cada uno de esos miembros emparejados con cada torneo. Llamaremos a la relación resultante AllPairs:

Todos los pares = ÿ () Entrada × ÿ (T ) torneo


Identificación de miembro Tour ID

Ahora eliminaremos de AllPairs los emparejamientos que están en la tabla de entrada usando una operación de
diferencia. Si proyectamos el ID de miembro del resultado, tendremos los ID de los miembros que no están asociados con todos
los torneos.

Sin par =ÿ ( ÿ

ÿ
Identificación de miembro
Todos los pares
ID de miembro Tour
, ID ) )( Entrada

Al eliminar estos ID de miembros no coincidentes de los ID de miembros en la relación Entrada, llegaremos al resultado que
requerimos:

ResultadoDivisión = ÿ () Entrada ÿ Sin par


Identificación de miembro

Podemos usar vistas SQL para expresar estos mismos pasos de una manera manejable. Primero, cree una vista con todos los
parejas de miembros y torneos:

CREAR VER AllPairs COMO


SELECCIONE M.MemberID, T.TourID DE
(SELECCIONE ID de miembro DE la entrada)M
UNIÓN CRUZADA

(SELECCIONE TourID DEL Torneo)T;

Ahora cree una vista para encontrar los pares no coincidentes:

CREAR VISTA COMO inigualable


SELECCIONE * DE Todos los pares
EXCEPTO

SELECCIONE ID de miembro, ID de viaje

DESDE Entrada;

Ahora usa estas dos vistas para encontrar el resultado de la división; es decir, el MemberID de los miembros que tienen
ingresó a todos los torneos:

SELECCIONE ID de miembro DESDE la entrada


EXCEPTO

SELECCIONE ID de miembro DESDE Sin coincidencia;

Si es valiente, podría intentar combinar todos estos pasos en una consulta SQL; sin embargo, veremos una forma más
manejable de expresar la consulta equivalente usando cálculo relacional en la sección sobre el cuantificador universal más
adelante en este Apéndice.
224
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Cálculo relacional: especificación del resultado


El álgebra relacional nos permite especificar una secuencia de operaciones que eventualmente da como resultado un conjunto
de tuplas con la información requerida. En lugar de especificar cómo hacer la consulta, el cálculo relacional describe qué
condiciones que deben satisfacer los datos resultantes. Esta sección proporciona una breve introducción a la notación para describir
consultas de cálculo sin profundizar en las matemáticas.

Expresiones de cálculo simple


En lenguaje informal, una descripción de cálculo relacional de una consulta tiene la siguiente forma:

Quiero las tuplas que obedezcan las siguientes condiciones....

Más formalmente podemos expresar lo anterior como:

{ metro | condición(m) }

La parte de la izquierda de la barra | especifica los atributos de las tuplas que queremos devolver, mientras que la parte de la
derecha (a menudo denominada predicado) describe los criterios que deben satisfacer. m se llama una variable de tupla
y condition(m) es una función que debe ser verdadera para cada una de las tuplas m devueltas. Estrictamente hablando, la
notación anterior se llama cálculo de tuplas. Otra notación equivalente, que no analizaremos aquí, es el cálculo de dominio.

La siguiente expresión significa que cada tupla devuelta m debe ser de la relación Miembro y debe
tener 'Senior' como el valor del atributo MemberType:

{metro | Miembro(m) AND m.MemberType = 'Senior'}

Podemos refinar aún más la expresión para especificar qué atributos de la tupla m deben incluirse en el resultado:

{m.Apellido, m.Nombre | Miembro(m) AND m.MemberType = 'Senior'}

Debido a que SQL se basa en el cálculo relacional, la instrucción SQL equivalente es una traducción casi directa de
la expresión de cálculo, como vemos aquí:

SELECCIONE m.Apellido, m.Nombre


DE Miembro m
DONDE m.MemberType = 'Senior';

La parte de la izquierda de la barra | en la expresión de cálculo se convierte en la cláusula SELECT, Miembro(m)


se convierte en la cláusula FROM y el resto de la expresión constituye la cláusula WHERE. En SQL, se puede hacer referencia a
la m como un alias de tabla, pero también es útil pensar en ella como una variable de tupla.

Variables libres y vinculadas


La siguiente expresión de cálculo recupera los nombres de los miembros y la tarifa asociada con su tipo de membresía. Es
esencialmente una unión interna entre las relaciones Miembro y Tipo.

{m.Apellido, m.Nombre, t.Cuota | Miembro(m) Tipo(t) AND m.MemberType = t.Type}

225
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Las variables de tupla m y t se denominan variables libres. Las condiciones a la derecha de la expresión no se
pueden evaluar hasta que demos valores m y t. Es habitual referirse a las variables libres como que se extienden sobre
cada tupla en sus respectivas relaciones y luego evaluar las condiciones de cada combinación para ver si debe incluirse
en el resultado. En el cuerpo del libro sugerí pensar en las variables como si estuvieran unidas a dedos que se mueven a
través de sus respectivas relaciones para que podamos determinar si la condición (en este caso m.MemberType = t.Type)
se evalúa como verdadera. Las variables libres indican las tuplas que se devuelven y siempre deben aparecer a la
izquierda de la barra.
Supongamos que queremos encontrar los nombres de aquellos miembros que han entrado en algún torneo.
Para incluir un miembro en el resultado, tiene que haber una tupla para ese miembro en la relación Entrada. El símbolo
ÿ, que significa “existe”, se usa en la siguiente expresión de cálculo para devolver los nombres de aquellos miembros
donde existe una tupla en la relación Entrada con su MemberID:

{m.Apellido, m.Nombre | Miembro(m) Y


ÿ(e)(
Entrada(e) AND m.MemberID = e.MemberID
)
}

He repartido la expresión en diferentes líneas para que las condiciones de la variable e sean claras. La
variable e se conoce como una variable ligada. No aparece a la izquierda de la ecuación y solo se usa para determinar
si la condición del lado derecho de la expresión es verdadera. Las variables libres (que siempre aparecen en el lado
izquierdo de la expresión) son aquellas para las que consideramos todas las posibilidades. En la expresión anterior, a
nuestra variable libre m se le da el valor de cada tupla en la relación Miembro a su vez. Para cada valor de m
usamos la variable ligada e para ayudar a determinar si hay una tupla apropiada en la relación Entrada.
Las variables vinculadas deben tener lo que se llama un cuantificador, que explica cómo se usará la variable para
calcular la declaración de condición. En este caso usamos el cuantificador existencial (ÿ), que nos obliga a encontrar
una única tupla en la relación asociada que satisfaga la condición. También hay un cuantificador universal.
(ÿ) que requiere cada tupla en la relación asociada para satisfacer la condición. Ahora veremos ejemplos del uso de
estos cuantificadores y las declaraciones SQL equivalentes.

Cuantificador existencial y SQL


Una expresión como ÿ(e)(Entrada(e) Y (condición(e)) es verdadera, si podemos encontrar una tupla e en la relación
Entrada que satisfaga la condición especificada. Veamos cómo se puede representar esta consulta en SQL:

{m.Apellido, m.Nombre | Miembro(m) Y


ÿ(e)(
Entrada(e) AND e.MemberID = m.MemberID
)
}

Primero, consideraremos una expresión SQL que sigue el cálculo lo más cerca posible usando un
Cláusula EXISTS:

SELECCIONE m.Apellido, m.Nombre


DE Miembro m
DONDE EXISTE (
Seleccione * FROM Entrada e DONDE e.MemberID = m.MemberID
);

226
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Como puede ver, el SQL es casi una traducción directa de la declaración de cálculo. De manera equivalente, podemos
representar el requisito de existencia con una consulta anidada y una cláusula IN:

SELECCIONE m.Apellido, m.Nombre


DE Miembro m
DONDE ID de miembro m EN (
SELECCIONE ID de miembro DE la entrada e
);

Cada una de las declaraciones SQL anteriores devuelve el resultado correcto, pero estoy seguro de que está pensando que
son una forma complicada de llegar allí. La consulta para encontrar miembros que hayan ingresado a un torneo se puede expresar
de manera más simple como una unión entre las dos relaciones:

SELECCIONE m.Apellido, m.Nombre


DE Miembro m, Entrada e
WHERE e.MemberID = m.MemberID;

La instrucción SQL anterior no es estrictamente equivalente a las dos primeras. Este último devolverá nombres
duplicados, uno para cada uno de los torneos en los que haya entrado el miembro. Si observamos las dos primeras consultas
SQL, vemos que están verificando cada tupla en la tabla de miembros (solo una vez) y buscando una tupla correspondiente en
la tabla de entrada. La consulta final considera todas las combinaciones de las tuplas en Member
y Entry y devuelve cualquiera que satisfaga la condición (devolviendo así los duplicados).
Aunque podemos eliminar los duplicados de la consulta SQL final agregando una palabra clave DISTINCT,
está considerando un conjunto diferente de tuplas para su inclusión en el resultado, por lo que está respondiendo a una
pregunta sutilmente diferente a las dos declaraciones SQL anteriores. La consulta de cálculo relacional es muy precisa y es esa
precisión la que puede ser útil en algunas situaciones.
Para encontrar miembros que no han ingresado a un torneo, simplemente reemplazamos ÿ con NOT ÿ (o ÿ) en la consulta
para encontrar miembros que han entrado en un torneo:

{m.Apellido, m.Nombre | Miembro(m) Y


NO ÿ(e)(
Entrada(e) AND m.MemberID = e.MemberID
)
}

La declaración SQL equivalente simplemente requiere la adición de la palabra clave NOT, como en el ejemplo aquí:

SELECCIONE m.Apellido, m.Nombre


DE Miembro m
DONDE NO EXISTE (
SELECCIONE * DE la entrada e
WHERE e.MemberID = m.MemberID
);

227
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Cuantificador Universal y SQL


El cuantificador universal ÿ nos permite comprobar que una condición se cumple para todas las tuplas de algún conjunto.
Esto es lo que requerimos para que una consulta encuentre los nombres de los miembros que han ingresado a todos los
torneos. ¡Hemos visto esta consulta muchas veces! La declaración de cálculo relacional que sigue es una forma sencilla
de expresar la consulta:

{m.Apellido, m.Nombre | Miembro(m) Y


ÿ(t)(
torneo
ÿ(e)(
Entrada (e) Y
e.ID de miembro = m.ID de miembro
Y e.TourID = t.TourID
)
)
}

La declaración de cálculo debe interpretarse como "Recuperar el apellido y el nombre de una tupla m
particular en el miembro si para cada tupla t en el torneo existe una tupla e en la entrada para el miembro m y el
torneo t".
Reconocerá el resultado de esta consulta como el equivalente del operador de división del álgebra
relacional. También recordará que SQL no tiene una palabra clave para la división. Lamentablemente, tampoco
tiene una palabra clave para el cuantificador universal. El cálculo relacional puede ayudarnos aquí con el uso de
la siguiente identidad:

ÿ(t)(condición (t)) ÿ NO ÿ(t)(NO condición(t))

Esta declaración significa que si decimos "para cada tupla se cumple la condición ta", entonces eso es lo mismo que decir
“no hay tupla t para la cual la condición no se cumpla”. Podemos usar esta identidad para reformular nuestra expresión
de cálculo original de la siguiente manera:

{m.Apellido, m.Nombre | Miembro(m) Y


NO ÿ(t)(
Torneo(t)(
NO ÿ(e)(
Entrada(e) AND e.MemberID = m.MemberID
Y e.TourID = t.TourID
)
)
}

Esencialmente, esto dice que no hay tupla t en Torneo para la cual no haya una tupla e correspondiente
en Entrada. Esto se traduce con bastante facilidad a la instrucción SQL que se ve aquí:

SELECCIONE m.Apellido, m.Nombre


DE Miembro m
DONDE NO EXISTE (
SELECCIONE * DESDE Torneo t
DONDE NO EXISTE (
SELECCIONE * DE la entrada e

228
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

WHERE e.MemberID = m.MemberID


Y e.TourID = t.TourID
)
);

Un ejemplo
Veamos el álgebra y el cálculo para una consulta que podría ser un poco complicada. Queremos encontrar los nombres de las mujeres
que nunca han jugado en el torneo de Leeston.

Álgebra
Primero, necesitamos recuperar todas las entradas para el torneo de Leeston uniéndonos a las mesas de Torneo y Entrada y luego
usando una operación de selección:

LeestonEntradas ÿ ÿ ÿ( Entrada = Torneo ÿ )


TourName = Leeston
ÿ
TourID TourID

Las palabras "nunca" sugieren que necesitamos un operador de diferencia. Entonces, necesitamos encontrar el conjunto de todas las mujeres.
usando una operación de selección en la tabla de miembros, y luego necesitamos eliminar el conjunto de personas que tienen
jugó en Leeston. Para usar nuestro operador de diferencia, necesitamos tener relaciones compatibles con la unión, por lo que
proyectaremos solo el MemberID de los dos conjuntos que se acaban de describir. La siguiente expresión devolverá las identificaciones
de las mujeres que no han ingresado al torneo de Leeston:

NoLeestonLadies ÿ ÿ (ÿ
ID de miembro Sexo = F
ÿ ÿ
(Miembro ))ÿ
ÿ
Identificación de miembro
(L ) estonEntradas

Ahora necesitamos unir NonLeestonLadies a la tabla de miembros para tener acceso a sus nombres. Podemos
recuperar el conjunto final de nombres con:

Resultadoÿ ÿ
Nombre Apellido
,
( Miembro ÿ
ID de miembro=ID de miembro
NonLeestonLadies )

Ahora podemos construir una instrucción SQL que refleje la expresión algebraica. En el siguiente SQL, las filas más sangradas
representan LeestonEntries, la siguiente sangría representa NonLeestonLadies
(y se le ha dado ese alias), y las filas exteriores representan la unión final y el proyecto:

SELECCIONE m2.Apellido, m2.Nombre DESDE


(SELECCIONE m.MemberID DE Miembro m
DONDE m. Género = 'F'
EXCEPTO
SELECCIONE ID de miembro electrónico

DESDE entrada e INNER JOIN torneo t ON e.tourID = t.tourID


DONDE t.TourName = 'Leeston'
)NonLeestonDamas
INNER JOIN Miembro m2 ON m2.MemberID = NonLeestonLadies.MemberID;

229
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

Cálculo
Abordemos la misma consulta (los nombres de las mujeres que nunca han jugado en el torneo de Leeston) usando cálculo.
Siempre necesito visualizar las variables de tupla como dedos para comenzar. La figura A2-5 muestra las relaciones que
necesitaremos para la consulta.

Figura A2-5. Variables de tupla requeridas para la consulta

Queremos recuperar los nombres de las mujeres de la tabla de miembros, por lo que debemos considerar cada
tupla a la vez. Eso significa que m será nuestra variable libre. Para cada tupla m necesitamos verificar que el valor de
Género sea F y que no haya ninguna tupla e en la tabla Entrada que tenga el mismo MemberID que m y también TourID
= 24 (el torneo de Leeston). La Figura A2-5 nos muestra que aunque Barbara Olson es una mujer, no la incluiremos porque
tiene una entrada para el torneo de Leeston.
La siguiente expresión de cálculo recuperará los nombres de los miembros que cumplan las condiciones que acabamos
de describir:

{m.Apellido, m.Nombre | Miembro(m) AND m.Gender = 'F'


NO ÿ(e)
( Entrada(e)
( e.MemberID = m.MemberID
AND ÿ(t)( t.TourID = e.TourID
AND t.Tourname =
'Leeston'
)
}

230
Machine Translated by Google

APÉNDICE 2 ÿ Notación relacional

La expresión de cálculo se traduce directamente en la siguiente instrucción SQL:

SELECCIONE m.Apellido, m.Nombre


DESDE Miembro m DONDE m.Género
= 'F'
Y NO EXISTE
( SELECCIONE
* DESDE
Entrada e DONDE e.MemberID =
m.MemberID Y EXISTE ( Seleccione *
DESDE Torneo t DONDE t.TourID
= e.TourID AND t.Tourname =
'Leeston'

)
);

Conclusión
Después de aplicar un enfoque de cálculo y álgebra a nuestra consulta para encontrar mujeres que no hayan participado en
un torneo de Leeston, hemos llegado a dos consultas SQL equivalentes pero de aspecto bastante diferente. Hay, sin duda,
varias otras sentencias SQL equivalentes. Probarlos en SQL Server 2012 muestra que el optimizador produce planes de
ejecución ligeramente diferentes, y la consulta de cálculo sale un poco más rápido, aunque agregar algunos índices podría
cambiar eso por completo.
El mensaje de este libro es que existen dos métodos equivalentes pero bastante diferentes de abordar
cualquier duda. Este apéndice agrega notaciones concisas para ayudarlo a representar esos enfoques.

231
Machine Translated by Google

Índice

ABC G, H
operaciones agregadas base de datos de palos de golf, 211–212
,
Función AVG() 132 Función de agrupación
duplicados, 133 Palabra clave DISTINCT , 143
nulos ,133 TENIENDO palabra clave, 140, 142
La función COUNT() , 129
duplica, 132
nulos ,130 yo

función MAX() , 135 Intersección, 16, 99, 101–102, 107, 111–117, 118,
Consultas anidadas , 135 120, 124, 126–127, 161, 175, 185, 187, 200,
de la función MIN(), 144 205–207, 210, 217, 221–222
Función REDONDA() , 134
función SUMA() , 135
J, K, L, M
D Unir
interfaces esquemáticas , 43
Diseño de base de 44 interna, 35–
equi-join, unión
datos combinando , 204
36, 38, 43, 45–49, 96, 109, 216– 217, 220
tablas restricciones de clave , 199
natural
, 200
externa datos inconsistentes
, 44
ortografía inconsistente, 202
, 41
valores numéricos , 201
enfoque de resultado de
clave principal , 198
110, 205–206
orden , 36,
enfoque
42 , 45–49,
de proceso
73–76,
externo
109–
Implementación SQL , 203
Diferencia, 40–41, 44, 63, 99, 101–102, 112, 118–122,
Producto cartesiano , 33
124, 126–127, 163, 170, 173, 188, 198, 200,
combinación interna
técnicas
, 35
207–208, 217–218, 221–222, 224, 229
combinar combinación , 170 bucles
División , 63, 122–126, 128, 134, 142–143, 146, 188,
anidados , 169–170 171
217, 223–224, 228

servidor SQL,
mi
EXISTE palabra clave, 57–60, 62, 64 norte

Consultas anidadas, 51, 53, 58, 60, 62, 65, 115, 144–
F 145, 171–172, 191, 199, 204, 208–209
marcos , 156–159 Normalización , 5, 9

© Clare Churcher 2016 233


C. Churcher, Inicio de consultas SQL, DOI 10.1007/978-1-4842-1955-3
Machine Translated by Google

ÿ ÍNDICE

O calculo relacional , 12
Consulta SQL, 10
Enfoque de resultados, 11–13, 33, 35–38, 42, Tabla SQL, álgebra y cálculo , 216
76–78, 80–81, 93–94, 175, 188
Función SOBRE() , 147 diseño, clave
principal de
, 5
pag q dominios 2,
inserción y actualización de 3 filas, 3
Proyecto , 1, 15–16, 32, 38, 41, 43, 53, 72, 107, 120, 122, 217– Diagrama de Venn, 213–214
222, 224, 229 Enfoque basado en procesos . Véase
también Álgebra relacional Producto cartesiano , 33 unión
interna , 35 CALLE
Seleccionar , 15–16, 19, 32, 38, 41, 43, 51, 53, 59, 61–64,
71, 73, 94, 104–105, 136–137, 146–148, 164,
R 183–184, 186, 192, 204, 205, 207, 217–220,
225, 229
Integridad referencial , 8 Agregados simples, 129–130, 147–149, 159
Operaciones de Subconsultas
álgebra de álgebra relacional, 222, 229 EXISTE palabra clave, 57
Producto cartesiano, 219 Palabra clave IN, 51
operación combinada de selección y proyecto, 218 consultas internas
diferencia (ÿ), 221interna,
operación
220de
intersección
división, unión
(Ç), 221 EXISTE palabra clave, 62
operación de proyecto, 223
218 operación de selección, 60
valores de valor ,único,
unión (È), 221 cuantificador universal conjunto de , 62
NOT IN palabra clave,
inserción de 54 filas y actualización , 63
217 , 63
de datos de eliminación
Clave sustituta, 3
, 228
Cálculo relacional
Cuantificador existencial, 226–227
U, V
Variables libres, 226 Modelado unificado
Cuantificador universal, 228 Idioma (UML), 6, 8
Modelos de datos de bases Unión, 16, 48, 99, 101–113, 118, 120, 126, 127, 145,
de datos relacionales 180, 185, 204, 206, 217, 221–222, 229
cardinalidad , 7
clase 6,
clave externa ,
WXYZ
8 opcionalidad de la , 8 Marco de conteo
interfaz gráfica , 7 acumulativo de , 151
integridad referencial , 8 , 156
funciones de ventana
relaciones, tuplas y atributos , 214 Cláusula GROUP BY, 149
recuperación de información enfoque Cláusula ORDER BY, 150
de resultado , 11, 13 enfoque Función RANGO() , 152–153
de proceso , 10, 13 álgebra Función SUM() , 154
relacional , 12 agregados simples, 147

234

También podría gustarte