¿Cuáles son los antipatrones SQL más comunes? [cerrado]

232

Todos los que trabajamos con bases de datos relacionales hemos aprendido (o estamos aprendiendo) que SQL es diferente. Obtener los resultados deseados, y hacerlo de manera eficiente, implica un proceso tedioso que se caracteriza en parte por aprender paradigmas desconocidos y descubrir que algunos de nuestros patrones de programación más familiares no funcionan aquí. ¿Cuáles son los antipatrones comunes que has visto (o que has cometido)?

le dorfier
fuente
Esta es una pregunta que no se ajusta a los estándares más nuevos sobre qué tipo de pregunta es apropiada para Stack Overflow. Cuando se le preguntó, esto puede no haber sido cierto.
David Manheim
@casperOne, ¿no hay alguna cláusula de "importancia histórica" ​​que haga que esta pregunta sea aceptable?
Amy B
26
Me parece triste que una de las preguntas más útiles en el sitio de Wohole esté cerrada por no ser constructiva.
HLGEM
11
@HLGEM Estoy totalmente de acuerdo. Esta pregunta es un ejemplo perfecto de todo lo que está mal con StackExchange
Kevin Morse
1
El tema es absolutamente importante y relevante. Pero la pregunta es demasiado abierta, por lo que cada una de las respuestas describe el error personal contra el patrón de un ingeniero individual.
Shane

Respuestas:

156

Me decepciona constantemente la tendencia de la mayoría de los programadores a mezclar su lógica de interfaz de usuario en la capa de acceso a datos:

SELECT
    FirstName + ' ' + LastName as "Full Name",
    case UserRole
        when 2 then "Admin"
        when 1 then "Moderator"
        else "User"
    end as "User's Role",
    case SignedIn
        when 0 then "Logged in"
        else "Logged out"
    end as "User signed in?",
    Convert(varchar(100), LastSignOn, 101) as "Last Sign On",
    DateDiff('d', LastSignOn, getDate()) as "Days since last sign on",
    AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' +
        City + ', ' + State + ' ' + Zip as "Address",
    'XXX-XX-' + Substring(
        Convert(varchar(9), SSN), 6, 4) as "Social Security #"
FROM Users

Normalmente, los programadores hacen esto porque tienen la intención de vincular su conjunto de datos directamente a una cuadrícula, y es conveniente tener el formato del servidor SQL en el lado del servidor que el formato en el cliente.

Las consultas como la que se muestra arriba son extremadamente frágiles porque acoplan estrechamente la capa de datos a la capa de la interfaz de usuario. Además de eso, este estilo de programación evita a fondo que los procedimientos almacenados sean reutilizables.

Julieta
fuente
10
Un buen patrón poster-child para un acoplamiento máximo en el mayor número posible de niveles / capas de abstracción.
dkretz
3
Puede que no sea bueno para el desacoplamiento, aunque por razones de rendimiento he hecho cosas así a menudo, los cambios iterativos realizados por SQL Server son más rápidos que los realizados por el código de nivel medio. No entiendo su punto de reutilización: nada le impide ejecutar el SP y cambiar el nombre de los cols si así lo desea.
Joe Pineda
54
Mi favorito es cuando la gente incrusta HTML y JavaScript, por ejemplo, SELECCIONE '<a href=... onclick="">' + name '</a>'
Matt Rogish el
15
Con consultas como esta, puede editar la cuadrícula en un sitio web con una simple declaración alter. O cambie el contenido de una exportación, o vuelva a formatear una fecha en un informe. Esto hace felices a los clientes y me ahorra tiempo. Así que gracias, pero no gracias, me quedaré con consultas como esta.
Andomar
44
@ Matt Rogish - Jesús, ¿alguien realmente hace eso?
Axarydax
118

Aquí están mis 3 mejores.

Número 1. Error al especificar una lista de campos. (Editar: para evitar confusiones: esta es una regla de código de producción. No se aplica a los scripts de análisis únicos, a menos que yo sea el autor).

SELECT *
Insert Into blah SELECT *

debiera ser

SELECT fieldlist
Insert Into blah (fieldlist) SELECT fieldlist

Número 2. Usando un cursor y un bucle while, cuando lo hará un bucle while con una variable de bucle.

DECLARE @LoopVar int

SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable)
WHILE @LoopVar is not null
BEGIN
  -- Do Stuff with current value of @LoopVar
  ...
  --Ok, done, now get the next value
  SET @LoopVar = (SELECT MIN(TheKey) FROM TheTable
    WHERE @LoopVar < TheKey)
END

Número 3. DateLogic a través de tipos de cadena.

--Trim the time
Convert(Convert(theDate, varchar(10), 121), datetime)

Debiera ser

--Trim the time
DateAdd(dd, DateDiff(dd, 0, theDate), 0)

He visto un pico reciente de "Una consulta es mejor que dos, ¿verdad?"

SELECT *
FROM blah
WHERE (blah.Name = @name OR @name is null)
  AND (blah.Purpose = @Purpose OR @Purpose is null)

Esta consulta requiere dos o tres planes de ejecución diferentes según los valores de los parámetros. Solo se genera un plan de ejecución y se atasca en la memoria caché para este texto sql. Ese plan se utilizará independientemente del valor de los parámetros. Esto da como resultado un bajo rendimiento intermitente. Es mucho mejor escribir dos consultas (una consulta por plan de ejecución previsto).

David B
fuente
77
hmmm, te daré un +1 solo para los puntos 2 y 3, pero los desarrolladores sobrepasan la regla 1. A veces tiene su lugar.
annakata
1
¿Cuál es el razonamiento detrás del # 1?
jalf
29
Cuando usa select *, obtiene lo que está en la tabla. Esas columnas pueden cambiar los nombres y el orden. El código del cliente frecuentemente se basa en nombres y orden. Cada 6 meses me preguntan cómo preservar el orden de las columnas al modificar una tabla. Si se siguiera la regla, no importaría.
Amy B
He usado el # 2 a veces, otras he seguido la ruta del cursor (aunque primero guardo los resultados de la consulta en una tabla var, abro el cursor sobre eso). Siempre me he preguntado si alguien ha hecho una prueba de rendimiento de ambos.
Joe Pineda
44
... pero, por supuesto, los cursores casi siempre deberían ser el último recurso, después de no poder averiguar cómo hacer el trabajo con SQL basado en conjuntos. Una vez pasé unos 45 minutos diseccionando cuidadosamente un horrendo y gigantesco cursor PL / SQL en un procedimiento almacenado (dibujé diagramas de la cosa podrida), que pobló una gran tabla temporal y luego seleccioné el contenido de la tabla temporal de nuevo al llamador para renderizar un reporte. Tardó 8,5 minutos en ejecutarse, en hardware sustancial. Después de diagramar todo, pude reemplazarlo con una sola consulta que arrojó los mismos resultados en menos de 2 segundos. Cursores, hombre ...
Craig
71
  • Campos de contraseña legibles por humanos , egad. Autoexplicativo.

  • Utilizando LIKE contra columnas indexadas , estoy casi tentado a decir LIKE en general.

  • Reciclaje de valores PK generados por SQL.

  • Sorpresa, nadie mencionó la mesa de Dios todavía. Nada dice "orgánico" como 100 columnas de banderas de bits, cadenas grandes y enteros.

  • Luego está el patrón "Echo de menos los archivos .ini" : almacenar CSV, cadenas delimitadas por tuberías u otros datos requeridos para analizar en campos de texto grandes.

  • Y para el servidor MS SQL, el uso de cursores en absoluto . Hay una mejor manera de hacer cualquier tarea de cursor.

Editado porque hay tantos!

revs annakata
fuente
19
mal acerca de los cursores, dudaría en decir que hacer algo en particular es 100% correcto o 100% incorrecto
Shawn
44
Hasta ahora, todos los ejemplos de defensa del cursor que he visto están utilizando la herramienta incorrecta para el trabajo. Pero si todo lo que sabe es SQL, lo usa de manera inapropiada o aprende a escribir otros tipos de software.
dkretz
3
@tuinstoel: ¿Cómo LIKE '% blah%' puede usar un índice? La indexación depende del orden y este ejemplo busca una posición media aleatoria de una cadena. (Los índices se ordenan por el primer carácter, primero, y al mirar los 4 caracteres intermedios se obtiene un orden prácticamente aleatorio ...)
MatBailie
12
En la mayoría de los servidores de bases de datos (al menos los que he usado), LIKE puede usar índices ... siempre que sea una búsqueda de prefijo (LIKE 'xxx%'), es decir, siempre que los caracteres comodín no Primero en la cadena de búsqueda. Creo que podría estar hablando un poco de propósitos cruzados aquí.
Cowan
10
Es como si no te gustara LIKE '%LIKE'.
Johan
62

No tiene que profundizar en ello: no usar declaraciones preparadas.

stesch
fuente
3
Sip. Seguido de cerca en el mismo contexto, en mi experiencia, con "no atrapar errores".
dkretz
1
@stesch: Esto no es nada en comparación con el uso de vistas y tener una fecha de informe variable. Las vistas son un antipatrón si tiene una fecha de informe variable (supongo que la mayoría de las aplicaciones tienen). Agregaría esto en una respuesta separada, pero desafortunadamente está cerrado.
Stefan Steiger
56

Usando alias de tabla sin sentido:

from employee t1,
department t2,
job t3,
...

Hace que leer una declaración SQL grande sea mucho más difícil de lo necesario

Tony Andrews
fuente
49
alias? diablos, he visto nombres de columnas reales como ese
annakata
10
los alias concisos están bien. Si quieres un nombre significativo, no uses un alias.
Joel Coehoorn
43
No dijo "conciso", dijo "sin sentido". En mi libro no habría nada de malo en usar e, d y j como alias en la consulta de ejemplo.
Robert Rossney
11
Absolutamente, Robert - e, d y j estarían bien conmigo.
Tony Andrews
8
Usaría emp para empleado, dep para departamento y trabajo para trabajo (o tal vez jb) :)
Andrei Rînea
53
var query = "select COUNT(*) from Users where UserName = '" 
            + tbUser.Text 
            + "' and Password = '" 
            + tbPassword.Text +"'";
  1. Confiando ciegamente en la entrada del usuario
  2. No usar consultas parametrizadas
  3. Contraseñas de texto sin cifrar
Will
fuente
Todo lo cual puede tratarse de manera útil mediante el uso de una capa de resumen de base de datos de algún tipo.
dkretz
@doofledorfer: De acuerdo, un nivel medio sería definitivamente mejor en un caso como este, además de proporcionar resultados de almacenamiento en caché como un buen efecto secundario.
Joe Pineda
Impresionante ejemplo. Si un desarrollador considera cómo reemplazar eso con una buena solución, está a medio camino de convertirse en un desarrollador SQL decente.
Steve McLeod
46

Mis errores son las tablas de acceso de 450 columnas que ha preparado el hijo de 8 años de los mejores amigos del Director Gerente y la tabla de búsqueda dudosa que solo existe porque alguien no sabe cómo normalizar una estructura de datos correctamente.

Por lo general, esta tabla de búsqueda se ve así:

YO DINT,
Nombre NVARCHAR (132),
IntValue1 INT,
IntValue2 INT,
CharValue1 NVARCHAR (255),
CharValue2 NVARCHAR (255),
Date1 DATETIME,
Fecha2 FECHA

He perdido la cuenta de la cantidad de clientes que he visto que tienen sistemas que dependen de abominaciones como esta.

Pete OHanlon
fuente
1
Peor aún, leí que en la más reciente versión de Access que en realidad apoya de forma automática, lo que me temo va a animar a más de esto valor1, valor2, ... Valor3 columna fetichismo
Joe Pineda
Espera, ¿entonces el hijo de 8 años es hijo del peluquero de perros?
barrypicker
28

Los que más me disgustan son

  1. Uso de espacios al crear tablas, sprocs, etc. Estoy bien con CamelCase o under_scores y singular o plural y MAYÚSCULAS o minúsculas, pero tengo que referirme a una tabla o columna [con espacios], especialmente si [está espaciado de manera extraña] (sí, Me he encontrado con esto) realmente me irrita.

  2. Datos desnormalizados. Una tabla no tiene que estar perfectamente normalizada, pero cuando me encuentro con una tabla de empleados que tiene información sobre su puntaje de evaluación actual o su principal, me dice que probablemente necesite hacer una tabla separada en algún momento y luego trate de mantenerlos sincronizados. Normalizaré los datos primero y luego, si veo un lugar donde la desnormalización ayuda, lo consideraré.

  3. Uso excesivo de vistas o cursores. Las vistas tienen un propósito, pero cuando cada tabla está envuelta en una vista, es demasiado. He tenido que usar cursores varias veces, pero generalmente puedes usar otros mecanismos para esto.

  4. Acceso. ¿Puede un programa ser un antipatrón? Tenemos SQL Server en mi trabajo, pero varias personas usan el acceso debido a su disponibilidad, "facilidad de uso" y "amabilidad" para usuarios no técnicos. Aquí hay demasiado para entrar, pero si has estado en un entorno similar, ya sabes.

Jamal Hansen
fuente
2
# 4 - hay otro hilo solo para <a href=' stackoverflow.com/questions/327199/…> :).
dkretz
44
El acceso NO es un DBMS. Es un entorno RAD, con un administrador de base de datos muy simple incluido. SQL Server, Oracle, y col. será no reemplazarlo, a menos que agregue un VB-como el lenguaje y unos informes de Crystal como instalación.
Joe Pineda
26

use SP como prefijo del nombre del procedimiento de tienda porque primero buscará en la ubicación de procedimientos del sistema en lugar de los personalizados.

Oscar Cabrero
fuente
1
También se puede extender al uso de cualquier otro prefijo común para todos los procedimientos almacenados, lo que hace que sea más difícil seleccionar una lista ordenada.
dkretz
77
¡+1 para el comentario de doofledorfer! ¡He visto esto mucho, me parece idiota y de hecho hace que la búsqueda de un SP en particular sea muy difícil! También extendido a "vw_" para vistas, "tbl_" para tablas y similares, ¡cómo los odio!
Joe Pineda
1
Los prefijos pueden ser útiles si está creando scripts de los objetos en archivos (por ejemplo, para control de origen, implementaciones o migración)
Rick
1
¿Por qué sería útil prefijar cada procedimiento almacenado con sp o usp? Simplemente hace que sea más difícil escanear la lista para encontrar la que desea.
Ryan Lundy
25

Uso excesivo de tablas y cursores temporales.

Rockcoder
fuente
2
Buena evidencia de que "todo lo que sé es lenguajes de procedimiento".
dkretz
2
El uso excesivo de cualquier cosa es, por definición, no deseado. Sería útil un ejemplo específico de dónde no sería necesario usar tablas / cursores temporales.
Jace Rhea
66
Principalmente veo tablas temporales subutilizadas. Con SQL Server, a menudo obtiene ganancias de rendimiento al hacer cosas con un montón de tablas temporales en lugar de una consulta monolítica.
Cervo
24

Para almacenar valores de tiempo, solo se debe usar la zona horaria UTC. La hora local no debe ser utilizada.

Frank Schwieterman
fuente
3
Todavía no he encontrado una buena solución simple para convertir de UTC a la hora local para fechas en el pasado, cuando se debe considerar el horario de verano, con diferentes fechas de cambio según los años y los países, así como todas las excepciones dentro de los países. Por lo tanto, UTC no lo salva de la complejidad de la conversión. Sin embargo, es importante tener una manera de conocer la zona horaria de cada fecha y hora almacenada.
ckarras
1
@CsongorHalmai Muchos lugares practican el horario de verano, por lo que los valores de tiempo dentro de una hora del horario pueden ser ambiguos.
Frank Schwieterman
Eso es ciertamente correcto para el presente y el pasado, pero para el futuro, especialmente el futuro bastante lejano, las zonas horarias explícitas son a menudo una necesidad. Si tiene una opción de 30 años que se acaba de escribir y vence en 2049-09-27T17: 00: 00 hora de Nueva York, entonces no puede asumir ciegamente que será 21: 00: 00Z. El Congreso de los Estados Unidos bien podría cambiar las reglas del horario de verano. Debe mantener separadas la hora local y la zona horaria real (América / Nueva_York).
John Cowan
23

usando @@ IDENTITY en lugar de SCOPE_IDENTITY ()

Citado de esta respuesta :

  • @@ IDENTITY devuelve el último valor de identidad generado para cualquier tabla en la sesión actual, en todos los ámbitos. Debe tener cuidado aquí, ya que está en todos los ámbitos. Podría obtener un valor de un activador, en lugar de su declaración actual.
  • SCOPE_IDENTITY devuelve el último valor de identidad generado para cualquier tabla en la sesión actual y el alcance actual. Generalmente lo que quieres usar.
  • IDENT_CURRENT devuelve el último valor de identidad generado para una tabla específica en cualquier sesión y cualquier ámbito. Esto le permite especificar de qué tabla desea el valor, en caso de que los dos anteriores no sean exactamente lo que necesita (muy raro). Puede usar esto si desea obtener el valor de IDENTIDAD actual para una tabla en la que no ha insertado un registro.
Brann
fuente
+1 muy cierto, podría causar errores que serían difíciles de eliminar
Axarydax
23

Reutilizar un campo 'muerto' para algo para lo que no estaba destinado (por ejemplo, almacenar datos de usuario en un campo 'Fax'), ¡aunque es muy tentador como una solución rápida!

FruitBreak
fuente
21
select some_column, ...
from some_table
group by some_column

y suponiendo que el resultado se ordenará por some_column. He visto esto un poco con Sybase donde se cumple el supuesto (por ahora).

Adrian Pronk
fuente
1
voto positivo para NUNCA asumiendo el orden de clasificación, solo porque esa fue la forma en que apareció en la herramienta de consulta esa vez
Joel Coehoorn el
3
Incluso he visto esto reportado como un error más de una vez.
dkretz
66
en MySQL, está documentado para ordenar. < dev.mysql.com/doc/refman/5.0/en/select.html >. Así que culpe a MySQL (nuevamente).
derobert
1
En Oracle, los resultados sin clasificar (casi) siempre coincidían con la agrupación, hasta la versión 10G. ¡Un montón de retrabajo para los desarrolladores que solían omitir ORDER BY!
Tony Andrews el
1
Incluso estaba en una clase de entrenamiento donde esto se afirmó como un hecho para SQL Server. Tuve que protestar muy fuerte. Para guardar solo para escribir 20 caracteres, confía en un comportamiento oscuro o indocumentado.
erikkallen
20
SELECT FirstName + ' ' + LastName as "Full Name", case UserRole when 2 then "Admin" when 1 then "Moderator" else "User" end as "User's Role", case SignedIn when 0 then "Logged in" else "Logged out" end as "User signed in?", Convert(varchar(100), LastSignOn, 101) as "Last Sign On", DateDiff('d', LastSignOn, getDate()) as "Days since last sign on", AddrLine1 + ' ' + AddrLine2 + ' ' + AddrLine3 + ' ' + City + ', ' + State + ' ' + Zip as "Address", 'XXX-XX-' + Substring(Convert(varchar(9), SSN), 6, 4) as "Social Security #" FROM Users

O bien, agrupando todo en una sola línea.

Radu
fuente
Usé la consulta de un comentario anterior, solo porque esa fue la primera instrucción SQL que tuve disponible.
Jasper Bekkers
17
  • La FROM TableA, TableB WHEREsintaxis de JOINS en lugar deFROM TableA INNER JOIN TableB ON

  • Suponiendo que una consulta se devolverá ordenada de cierta manera sin poner una cláusula ORDER BY, solo porque esa fue la forma en que se mostró durante la prueba en la herramienta de consulta.

Joel Coehoorn
fuente
55
Mis DBA de Oracle siempre se quejan de que uso "uniones ANSI", es decir, lo que usted presenta como la forma correcta. Pero sigo haciéndolo, y sospecho que en el fondo saben que es mejor.
Steve McLeod
1
Sospecho que Oracle desea que el SQL estándar desaparezca. :-) Además, no puedes mezclar JOINS implícitas y explícitas (también conocidas como ANSI JOINs) en MySQL 5; no funciona. Cuál es otro argumento para los JION explícitos.
staticsan
3
Yo diría que incluso A INNER JOIN B ON es un anti patrón. Prefiero UNA UNIÓN INTERNA B USANDO.
John Nilsson
Oracle admite ahora la sintaxis ANSI, pero solían tener esta sintaxis realmente extraña para las uniones externas en el pasado y todavía hay demasiadas personas que la usan.
Cervo
bueno ... Oracle todavía no le permitirá usar las uniones ANSI para vistas materializadas
rápidas
14

Aprender SQL en los primeros seis meses de su carrera y nunca aprender nada más durante los próximos 10 años. En particular, no aprender o usar eficazmente las funciones de SQL analítico / ventanas. En particular, el uso de over () y la partición por.

Las funciones de ventana, como las funciones de agregado, realizan una agregación en un conjunto definido (un grupo) de filas, pero en lugar de devolver un valor por grupo, las funciones de ventana pueden devolver múltiples valores para cada grupo.

Consulte el Apéndice A del O'Reilly SQL Cookbook para obtener una buena descripción de las funciones de ventanas.

Brian
fuente
12

Necesito poner mi propio favorito actual aquí, solo para completar la lista. Mi antipatrón favorito no prueba tus consultas .

Esto aplica cuando:

  1. Su consulta involucra más de una tabla.
  2. Cree que tiene un diseño óptimo para una consulta, pero no se moleste en probar sus suposiciones.
  3. Acepta la primera consulta que funciona, sin tener idea de si está incluso cerca de optimizada.

Y cualquier prueba realizada contra datos atípicos o insuficientes no cuenta. Si se trata de un procedimiento almacenado, coloque la declaración de prueba en un comentario y guárdela con los resultados. De lo contrario, póngalo en un comentario en el código con los resultados.

le dorfier
fuente
Una técnica muy útil para una prueba T-SQL mínima: en el archivo .SQL donde define su SP, UDF, etc., inmediatamente después de crear una prueba de bloque como IF 1 = 2 BEGIN (casos de muestra para su código, con resultados esperados) como comentarios) FIN
Joe Pineda
SQL Server analiza el código dentro del bloque de prueba, aunque nunca se ejecute. Entonces, cuando su objeto se modifique y reciba más parámetros, o de diferente tipo, etc., o se modifique un objeto del que depende, ¡recibirá un error con solo pedir un plan de ejecución!
Joe Pineda
No siempre es posible realizar pruebas con datos reales. A menudo, el servidor de desarrollo / servidor de "prueba" está mal pagado y obtiene una fracción del servidor en vivo. En general, las pruebas están mal vistas contra el servidor en vivo. Algunos lugares son mejores y tienen un servidor de prueba o provisional con datos en vivo.
Cervo
11

Abuso de mesa temporal.

Específicamente este tipo de cosas:

SELECT personid, firstname, lastname, age
INTO #tmpPeople
FROM People
WHERE lastname like 's%'

DELETE FROM #tmpPeople
WHERE firstname = 'John'

DELETE FROM #tmpPeople
WHERE firstname = 'Jon'

DELETE FROM #tmpPeople
WHERE age > 35

UPDATE People
SET firstname = 'Fred'
WHERE personid IN (SELECT personid from #tmpPeople)

No cree una tabla temporal a partir de una consulta, solo para eliminar las filas que no necesita.

Y sí, he visto páginas de código de esta forma en bases de datos de producción.

geofftnz
fuente
1
+1, estoy de acuerdo. Aunque, he encontrado al menos uno o dos casos en los que esta técnica ha mejorado el rendimiento: las consultas involucradas fueron complejas, por decir lo menos.
2010
1
Es cierto: tienen un lugar, pero no en todas las consultas :)
geofftnz
1
A veces tienes que hacer eso si las condiciones son muy complicadas. Es cierto que puede ser abusado hasta los extremos. Pero muchas veces una simple eliminación es mucho más simple que la lógica para obtener el caso en la consulta inicial. También a veces, si la cláusula no es sargeable, la consulta inicial se ralentizará. Pero solo hacerlo en la tabla temporal más pequeña es más eficiente. Y otras veces sigues agregando casos que la gente de negocios sigue agregando después del hecho.
Cervo
9

Visión contraria: obsesión excesiva con la normalización.

La mayoría de los sistemas SQL / RBDB ofrecen muchas características (transacciones, replicación) que son bastante útiles, incluso con datos no normalizados. El espacio en disco es barato y, a veces, puede ser más simple (código más fácil, tiempo de desarrollo más rápido) manipular / filtrar / buscar datos recuperados, que escribir un esquema 1NF y lidiar con todas las molestias (uniones complejas, subselecciones desagradables) , etc.)

He descubierto que los sistemas sobre normalizados suelen ser una optimización prematura, especialmente durante las primeras etapas de desarrollo.

(más pensamientos sobre él ... http://writeonly.wordpress.com/2008/12/05/simple-object-db-using-json-and-python-sqlite/ )

Gregg Lind
fuente
22
Creo que la no normalización es a menudo una optimización prematura.
tuinstoel 01 de
A veces lo es, a veces no lo es. Afortunadamente, a menudo es fácil de probar, y diferentes opciones funcionan con diferentes necesidades de base de datos.
Gregg Lind el
17
La normalización no es solo para ahorrar espacio en disco. También es crear una fuente autorizada para los datos. Si los datos se almacenan en un solo lugar, la coherencia no es un subproducto de una codificación cuidadosa, sino que es un subproducto del diseño.
Grant Johnson
Almacenar datos compuestos en formato JSON es una cosa: cada vez hay más soporte para ellos, y es una compensación consciente. El uso de valores separados por comas (o lo que sea) en un intento de guardar una unión es un centavo y una tontería.
John Cowan
Las soluciones noSQL están mostrando un beneficio de rendimiento a costa de duplicar datos al eliminar las búsquedas en varias tablas. Pone toda la normalización en su cabeza. En algunos ejemplos, los datos se recopilan en varios lugares para garantizar que un proceso tenga el tiempo de respuesta más rápido posible. Por supuesto, las preguntas sobre fuentes autorizadas entran en juego.
barrypicker
9

Acabo de armar este, basado en algunas de las respuestas SQL aquí en SO.

Es un antipatrón serio pensar que los desencadenantes son a las bases de datos como lo son los controladores de eventos a OOP. Existe la percepción de que cualquier lógica antigua se puede poner en disparadores, para que se active cuando una transacción (evento) ocurre en una mesa.

No es verdad. Una de las grandes diferencias es que los disparadores son síncronos, con una venganza, porque son síncronos en una operación de configuración, no en una operación de fila. En el lado OOP, exactamente lo contrario: los eventos son una forma eficiente de implementar transacciones asincrónicas.

dkretz
fuente
8

Procedimientos almacenados o funciones sin ningún comentario ...

Bliek
fuente
Y vistas;) Funciones verdaderas, excepto funciones con valores de tabla (= vistas con parámetros).
Stefan Steiger
7

1) No sé si es un antipatrón "oficial", pero no me gusta e intento evitar los literales de cadena como valores mágicos en una columna de base de datos.

Un ejemplo de la tabla 'imagen' de MediaWiki:

img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", 
    "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL,
img_major_mime ENUM("unknown", "application", "audio", "image", "text", 
    "video", "message", "model", "multipart") NOT NULL default "unknown",

(Acabo de notar una carcasa diferente, otra cosa para evitar)

Diseño casos como búsquedas int en tablas ImageMediaType e ImageMajorMime con claves primarias int.

2) conversión de fecha / cadena que se basa en configuraciones específicas de NLS

CONVERT(NVARCHAR, GETDATE())

sin identificador de formato

devio
fuente
Y tampoco sangría sintáctica. Argghh
dkretz
2
¿Por qué es esto malo? seguramente si está tratando de expresar un conjunto de valores, esto funciona tan bien como una tabla de búsqueda, y se ajusta mejor con el código que lo llama. Prefiero tener una enumeración en el código de mi aplicación que se asigna a una restricción de enumeración en mi base de datos que una enumeración en el código de mi aplicación que se asigna a filas específicas de una tabla de búsqueda. Simplemente se siente más limpio.
Jack Ryan
@JackRyan: Esto es malo porque cuando cambias la lista de enumeraciones más tarde, debes recordar cambiarla en dos lugares ahora. Viola SECO . La base de datos debe ser la única fuente de verdad.
Gerrat
7

Subconsultas idénticas en una consulta.

EvilTeach
fuente
10
Desafortunadamente, a veces no se puede evitar eso: en SQL 2000 no había una palabra clave "WITH", y el uso de UDF para encapsular subconsultas comunes a veces conduce a penalizaciones de rendimiento, culpe a MS de eso ...
Joe Pineda
Bueno, espero que puedan agregarlo uno de estos días.
EvilTeach
En SQL 2000, puede usar variables de tabla.
recursivo
@recursive: no puede tener índices en una variable de tabla, lo que a menudo lo hará más lento que una subconsulta. Sin embargo, podría usar una tabla temporal con índices personalizados.
Rick
Genial, he estado trabajando con SQL durante años, y ni siquiera sabía que existían las expresiones de tabla comunes (aunque las hubiera necesitado). ¡Ahora lo hago! ¡Gracias!
sleske
7
  • La vista alterada: una vista que se modifica con demasiada frecuencia y sin previo aviso ni razón. El cambio se notará en el momento más inapropiado o, peor aún, se equivocará y nunca se notará. Tal vez su aplicación se interrumpirá porque alguien pensó en un mejor nombre para esa columna. Como norma, las vistas deberían ampliar la utilidad de las tablas base mientras se mantiene un contrato con los consumidores. Solucione problemas pero no agregue características o peor cambie el comportamiento, para eso cree una nueva vista. Para mitigar, no comparta vistas con otros proyectos y use CTE cuando las plataformas lo permitan. Si su tienda tiene un DBA, probablemente no pueda cambiar las vistas, pero todas sus vistas estarán desactualizadas o serán inútiles en ese caso.

  • The! Paramed: ¿Puede una consulta tener más de un propósito? Probablemente, pero la próxima persona que lo lea no lo sabrá hasta la meditación profunda. Incluso si no los necesita en este momento, es probable que lo haga, incluso si es "solo" para depurar. Agregar parámetros reduce el tiempo de mantenimiento y mantiene las cosas SECAS. Si tiene una cláusula where, debe tener parámetros.

  • El caso para no CASE -

    SELECT  
    CASE @problem  
      WHEN 'Need to replace column A with this medium to large collection of strings hanging out in my code.'  
        THEN 'Create a table for lookup and add to your from clause.'  
      WHEN 'Scrubbing values in the result set based on some business rules.'  
        THEN 'Fix the data in the database'  
      WHEN 'Formating dates or numbers.'   
        THEN 'Apply formating in the presentation layer.'  
      WHEN 'Createing a cross tab'  
        THEN 'Good, but in reporting you should probably be using cross tab, matrix or pivot templates'   
    ELSE 'You probably found another case for no CASE but now I have to edit my code instead of enriching the data...' END  
jason saldo
fuente
Me encantó ese tercero. Yo ya lo estoy usando localmente ...
alphadogg
Gracias por los accesorios. :)
Jason Saldo
5

Los dos que más encuentro y que pueden tener un costo significativo en términos de rendimiento son:

  • Usar cursores en lugar de una expresión basada en conjuntos. Supongo que esto ocurre con frecuencia cuando el programador está pensando con procedimientos.

  • Usando subconsultas correlacionadas, cuando una unión a una tabla derivada puede hacer el trabajo.

Trigo Mitch
fuente
Estoy de acuerdo si quieres decir lo que creo que quieres decir; aunque una subconsulta correlacionada es un tipo de tabla derivada IIRC.
dkretz
1
Una tabla derivada es una operación de establecimiento, mientras que se ejecuta una subconsulta correlacionada para cada fila en la consulta externa, lo que la hace menos eficiente (9 de cada 10 veces)
Mitch Wheat
Hace un par de años, para mi sorpresa, descubrí que SQL S. de alguna manera está optimizado para manejar consultas correlacionadas: para las simples, obtienes el mismo plan de ejecución que con una consulta lógicamente equivalente usando JOIN! Además, las consultas correlacionadas que ponen de rodillas a Oracle se ejecutan solo lentamente en SQL S.
Joe Pineda
Es por eso que siempre lo pruebo en ambos sentidos. Y <i> lo hago </> usualmente lo intento en ambos sentidos. En la práctica, para SQL Server de todos modos, generalmente he encontrado que el sq correlacionado no es más lento.
dkretz
3
POR FAVOR, comprenda que una subconsulta correlacionada y una unión son IDÉNTICAS (en la mayoría de los casos). Ni siquiera son cosas diferentes que están optimizadas entre sí, sino simplemente diferentes representaciones textuales de la misma operación.
erikkallen
5

Poner cosas en tablas temporales, especialmente las personas que cambian de SQL Server a Oracle tienen la costumbre de usar en exceso las tablas temporales. Solo usa sentencias select anidadas.

tuinstoel
fuente
5

Desarrolladores que escriben consultas sin tener una buena idea sobre qué hace que las aplicaciones SQL (tanto consultas individuales como sistemas multiusuario) sean rápidas o lentas. Esto incluye ignorancia sobre:

  • estrategias físicas de minimización de E / S, dado que el cuello de botella de la mayoría de las consultas es E / S, no CPU
  • Impacto del rendimiento de diferentes tipos de acceso al almacenamiento físico (por ejemplo, muchas E / S secuenciales serán más rápidas que muchas E / S aleatorias pequeñas, ¡aunque menos si su almacenamiento físico es un SSD!)
  • cómo ajustar manualmente una consulta si el DBMS produce un plan de consulta deficiente
  • cómo diagnosticar el bajo rendimiento de la base de datos, cómo "depurar" una consulta lenta y cómo leer un plan de consulta (o EXPLICAR, según el DBMS que elija)
  • estrategias de bloqueo para optimizar el rendimiento y evitar puntos muertos en aplicaciones multiusuario
  • importancia del procesamiento por lotes y otros trucos para manejar el procesamiento de conjuntos de datos
  • diseño de tablas e índices para equilibrar mejor el espacio y el rendimiento (por ejemplo, cubriendo índices, manteniendo índices pequeños cuando sea posible, reduciendo los tipos de datos al tamaño mínimo necesario, etc.)
Justin Grant
fuente
3

Usar SQL como un paquete ISAM (Método de acceso secuencial indexado) glorificado. En particular, anidar cursores en lugar de combinar declaraciones SQL en una sola declaración, aunque más grande. Esto también cuenta como 'abuso del optimizador' ya que de hecho no hay mucho que pueda hacer el optimizador. Esto se puede combinar con declaraciones no preparadas para la máxima ineficiencia:

DECLARE c1 CURSOR FOR SELECT Col1, Col2, Col3 FROM Table1

FOREACH c1 INTO a.col1, a.col2, a.col3
    DECLARE c2 CURSOR FOR
        SELECT Item1, Item2, Item3
            FROM Table2
            WHERE Table2.Item1 = a.col2
    FOREACH c2 INTO b.item1, b.item2, b.item3
        ...process data from records a and b...
    END FOREACH
END FOREACH

La solución correcta (casi siempre) es combinar las dos instrucciones SELECT en una:

DECLARE c1 CURSOR FOR
    SELECT Col1, Col2, Col3, Item1, Item2, Item3
        FROM Table1, Table2
        WHERE Table2.Item1 = Table1.Col2
        -- ORDER BY Table1.Col1, Table2.Item1

FOREACH c1 INTO a.col1, a.col2, a.col3, b.item1, b.item2, b.item3
    ...process data from records a and b...
END FOREACH

La única ventaja de la versión de doble bucle es que puede detectar fácilmente los saltos entre los valores en la Tabla 1 porque el bucle interno termina. Esto puede ser un factor en los informes de interrupción de control.

Además, la clasificación en la aplicación suele ser un no-no.

Jonathan Leffler
fuente
El estilo, aunque no esta sintaxis, es particularmente rampante en PHP en mi experiencia.
dkretz
La sintaxis es en realidad IBM Informix-4GL, pero es lo suficientemente clara como para no necesitar mucha explicación (creo). Y el estilo es rampante en muchos programas SQL, independientemente del lenguaje de programación.
Jonathan Leffler
Excepto por el hecho de que está utilizando un antipatrón bien conocido (uniones implícitas) para ilustrar su antipatrón, de alguna manera derrota el punto.
Johan
Y, por supuesto, el uso de cursores es un antipatrón SQl. Prácticamente todos los cursores se pueden reescribir como operaciones basadas en conjuntos. Los pocos que no pueden son del tipo que solo los DBA con años de experiencia y que entienden cómo deberían estar escritos los componentes internos de la base de datos. Ningún desarrollador de aplicaciones debería necesitar escribir un cursor SQL.
HLGEM
3

Usando claves primarias como un sustituto para direcciones de registro y usando claves externas como un sustituto para punteros incrustados en registros.

Walter Mitty
fuente