Acerca de mi base de datos fecha tipo de cruzada cruzada: ¿Válido? ¿Vale la pena? ¿Alguien más lo siente?

13

Paso mucho tiempo respondiendo preguntas SQL sobre SO. Con frecuencia me encuentro con consultas de este tipo:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

SELECT * FROM person WHERE birthdate BETWEEN 'some string' AND 'other string'

es decir, ya sea confiando en una conversión implícita de cadena a fecha (incorrecta), de los parámetros dados o confiando en la base de datos que convierte x millones de valores de fila de la base de datos a cadena y haciendo una comparación de cadena (peor)

De vez en cuando hago un comentario, especialmente si es un usuario de alta reputación el que escribe una respuesta inteligente, pero a quien creo realmente debería ser menos descuidado / tipeado con sus tipos de datos

El comentario generalmente toma la forma de que probablemente sería mejor si convirtieran explícitamente sus cadenas a fechas, usando to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) o algún mecanismo similar:

    --oracle
    SELECT * FROM person WHERE birthdate BETWEEN TO_DATE('20170101', 'YYYYMMDD') AND TO_DATE('20170301', 'YYYYMMDD')

    --mysql
    SELECT * FROM person WHERE birthdate BETWEEN STR_TO_DATE('20170101', '%Y%m%d') AND STR_TO_DATE('20170301', '%Y%m%d')

    --SQLS, ugh; magic numbers
    SELECT * FROM person WHERE birthdate BETWEEN CONVERT(datetime, '20170101', 112) AND CONVERT(datetime, '20170301', 112)

Mis justificaciones técnicas para hacerlo es que es explícito en cuanto al formato de la fecha, y garantiza que los pocos parámetros de origen definitivamente se conviertan en el tipo de datos de la columna de destino. Esto evita cualquier posibilidad de que la base de datos obtenga una conversión implícita incorrecta (el argumento del 3 de enero / 1 de marzo del primer ejemplo) y evita que la base de datos decida convertir un millón de valores de fecha en la tabla a cadenas (usando alguna fecha específica del servidor formateo que quizás ni siquiera coincida con el formato de la fecha en los parámetros de cadena dentro del sql) para hacer la comparación - abundan los horrores

Mi justificación social / académica para hacerlo es que SO es un sitio de aprendizaje; las personas en él adquieren conocimiento ya sea implícita o explícitamente. Para golpear a un novato con esta consulta como respuesta:

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

Podría llevarlos a pensar que esto es sensato, ajustando la fecha para algún formato que prefieran:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

Si al menos vieron algún intento explícito de convertir la fecha, podrían comenzar a hacerlo por su formato de fecha extraño y matar algunos errores para siempre antes de que surjan. Después de todo, (I) intentamos disuadir a la gente de que se meta en el hábito de inyección SQL (¿alguien abogaría por parametrizar una consulta y luego declarar al controlador que @pBirthdatees una cadena, cuando la interfaz tiene un tipo de fecha y hora?)

Volviendo a lo que sucede después de hacer mi recomendación: por lo general recibo un poco de la recomendación "sé explícito, usa x", como "todo el mundo lo hace", "siempre funciona para mí", "muéstrame algún manual o documento de referencia que dice que debería ser explícito "o incluso" ¿qué?

En respuesta a algunos de estos, he preguntado si buscarían una columna int al WHERE age = '99'pasar la edad como una cadena. "No seas tonto, no necesitamos poner 'al buscar int" viene la respuesta, por lo que apreciamos los diferentes tipos de datos en alguna parte de su mente, pero tal vez simplemente no hay conexión con el salto lógico de buscar un int columna al pasar una cadena (aparentemente tonta) y buscar una columna de fecha al pasar una cadena (aparentemente sensible) es hipocresía

Entonces, en nuestros SQL tenemos una manera de escribir cosas como números (use números, sin delimitadores), cosas como cadenas de cadenas (use cualquier cosa entre delimitadores de apóstrofo). ¿Por qué no hay delimitadores para las fechas? ¿Es un tipo de datos tan fundamental en la mayoría de los DB? ¿Podría resolverse todo esto simplemente con una forma de escribir una fecha de la misma manera que javascript nos permite especificar una expresión regular colocando /cualquier lado de algunos caracteres? /Hello\s+world/. ¿Por qué no tener algo para las fechas?

En realidad, que yo sepa, (solo) Microsoft Access en realidad tiene símbolos que indican "se ha escrito una fecha entre estos delimitadores" para que podamos obtener un buen acceso directo como, WHERE datecolumn = #somedate#pero la presentación de la fecha todavía puede dar problemas, por ejemplo, mm / di vs dd / mm, porque MS siempre ha jugado rápido y suelto con las cosas que la multitud de VB pensó que era una buena idea


Volviendo al punto principal: estoy argumentando que es aconsejable ser explícito con este medio que nos obliga a pasar una multitud de tipos de datos diferentes como cadenas.

¿Es una afirmación válida?

¿Debo continuar esta cruzada? ¿Es un punto válido que la escritura en cadena es un moderno no-no? ¿O todos los RDBMS (incluidas las versiones antiguas), cuando empujan una consulta, WHERE datecolumn = 'string value'sin duda convertirán la cadena a una fecha y harán la búsqueda sin convertir los datos de la tabla / perder el uso de índices? Sospecho que no, al menos por experiencia personal de Oracle 9. Sospecho también que puede haber algunos escenarios de escape si las cadenas siempre se escriben en algún formato estándar ISO, y la columna tiene un sabor de fecha, entonces el El parámetro de cadena siempre se convertirá implícitamente correctamente. ¿Esto lo hace bien?

¿Es una tarea que valga la pena?

Muchas personas no parecen entenderlo, o no les importa, o exhiben cierta hipocresía en el sentido de que sus ints son ints pero sus fechas son cadenas. Sin embargo, es común para la mayoría que pocas personas se han vuelto y han dicho "sabes qué, estoy de acuerdo con tu punto. Seré explícito sobre mis fechas a partir de ahora ".

Caius Jard
fuente
Incluso he visto a alguien tener problemas con el WHERE datecolumn = 01/02/12 '' donde es posible que soliciten el año 1912, 2012, 2001, 1901, 12 o 1. También es un problema fuera del mundo de la base de datos, el número de los programadores que no pueden entender por qué la conversión "09"a un int está causando un bloqueo son legión, 9 no es un dígito octal válido y un 0 inicial hace que la cadena sea octal en muchos sistemas
Steve Barnes
2
Pensé en extender mi ejemplo para preguntar si WHERE age = '0x0F'es una forma válida de esperar que una base de datos busque quince años ..
Caius Jard
1
Eliminé una pregunta que está fuera de tema aquí: no hacemos solicitudes de recursos. Uno de los 2 votos cerrados fue dado por esta razón. De lo contrario, creo que esta es una pregunta válida, aunque podría ser demasiado amplia. Espero que la eliminación de la pregunta fuera de tema ayude a reducir un poco las cosas.
Thomas Owens
TL; DR pero en los sistemas de producción, esperaría que fechas como esta casi siempre estén en los parámetros. La codificación de fechas en consultas es un problema mayor que si usa conversiones implícitas. Si estoy escribiendo alguna consulta descartable, funciona o no. Nunca hago esto de todos modos (porque nunca puedo recordar el formato de fecha predeterminado) pero no estoy seguro de que importe mucho.
JimmyJames
1
La vida se trata de elegir tus batallas. En mi opinión, no vale la pena luchar contra este ...
Robbie Dee

Respuestas:

7

Tu escribiste:

son esos parámetros del 1 de enero al 3 de enero o del 1 de marzo.

De hecho, esa es una fuente potencial de errores. Señalar esto a un autor de la pregunta puede ser útil para otros lectores, así que sí, esta es una preocupación válida. Sin embargo, para ser constructivo, lo haría

  • consulte ANSI SQL y use los literales DATE o DATETIME de ese estándar

  • use el formato de fecha y hora habitual y sin ambigüedades de un DBMS específico (y mencione qué dialecto SQL se usa)

Desafortunadamente, no todos los DBMS admiten los literales de fecha ANSI SQL exactamente de la misma manera (si es que lo admiten), por lo que esto generalmente conducirá a una variante del segundo enfoque. El hecho de que "el estándar" no sea implementado rígidamente por diferentes proveedores de DB es probablemente parte del problema aquí.

Tenga en cuenta además que, para muchos sistemas del mundo real, las personas pueden confiar en un entorno local específico y fijo en el servidor de la base de datos, incluso si las aplicaciones del cliente están localizadas, porque solo hay un tipo de servidor, siempre configurado de la misma manera. Por lo tanto, se puede suponer que '01 / 03/2017 'tiene el formato fijo' dd / mm / aaaa 'o' mm / dd / aaaa 'para cualquier SQL utilizado en el sistema específico con el que están trabajando. Entonces, si alguien te dice "siempre funciona para mí", esta puede ser una respuesta sensata para su entorno . Si este es el caso, hace que valga menos la pena discutir este tema.

Hablando de "razones de rendimiento": siempre que no haya problemas de rendimiento mensurables, es bastante supersticioso discutir con "posibles problemas de rendimiento". Si una base de datos está haciendo un millón de conversiones de cadena a fecha o no, probablemente no importa cuando la diferencia de tiempo es solo 1/1000 segundos, y el verdadero cuello de botella es la red que hace que la consulta dure 10 segundos. Por lo tanto, mejor deje a un lado estas preocupaciones siempre que alguien solicite explícitamente consideraciones de rendimiento.

¿Debo continuar esta cruzada?

Te digo un secreto: odio las guerras religiosas. No conducen a nada útil. Por lo tanto, si las especificaciones ambiguas de fecha / hora en SQL pueden causar problemas, menciónelas, pero no intente forzar a las personas a ser más rígidas si realmente no les brinda ningún beneficio en su contexto actual.

Doc Brown
fuente
Sin embargo, esto no es tanto una pregunta sobre la ambigüedad de los formatos de fecha estadounidenses frente a los sensibles. Se trata de si es sensato pasar las fechas en una instrucción SQL como una cadena, y confiar en la conversión implícita a la fecha. La cuestión de que la base de datos tenga que hacer un millón de conversiones de fecha-> str para todos los millones de filas es un aspecto de rendimiento, y solo puede tomar 1/1000 de segundo para una consulta, pero ahora imagínelo en el contexto de miles de concurrentes usuarios. El mayor problema de rendimiento es que la conversión de datos significa que los índices ya no se pueden usar y eso puede ser realmente grave
Caius Jard,
@CaiusJard: mi respuesta está en pie: a veces es razonable, y a veces no, depende del contexto. Y honestamente, me niego a "... imaginar ..." cualquier cosa aquí. Cuando se trata de rendimiento, discutir cualquier caso hipotético no es útil. Cuando hay problemas de rendimiento mensurables, es hora de optimizar, y a veces de micro-optimizar, no de antemano.
Doc Brown
Es interesante que lo veas como hipotético; Veo que confiar en el comportamiento implícito es una oportunidad clara para que surjan errores y complicaciones de rendimiento (por razones bien documentadas: los índices no funcionan si se transforman todos los datos de la columna antes de que se busque), y con instrucciones explícitas esto no puede suceder
Caius Jard
@CaiusJard: no juegues con palabras: con "hipotético" no quiero decir "improbable", utilicé el término para cualquier tipo de escenario imaginado, opuesto a "situación real" en la que uno puede medir lo que sucede.
Doc Brown
1
@CaiusJard: si desea impresionar a otros profesionales de la industria, debe saber exactamente por qué la "optimización del rendimiento" es muy diferente de la "optimización de la seguridad", y ese es exactamente mi punto aquí: los problemas de rendimiento pueden manejarse después de que ocurran, eso rara vez demasiado tarde. Problemas de seguridad no, deben evitarse completamente antes de que ocurran. Así que por favor no compares manzanas con naranjas. Si te gustan las cruzadas, los argumentos de seguridad son mucho más adecuados para esto ;-)
Doc Brown
5

Tu cruzada no resuelve el problema.

Hay dos problemas separados:

  • conversión de tipo implícito en SQL

  • formatos de fecha ambiguos como 05/06/07

Veo de dónde vienes con tu cruzada, pero no creo que la conversión explícita realmente resuelva el problema en cuestión:

  • La conversión implícita todavía ocurre en caso de una falta de coincidencia entre los tipos en una comparación. Si una cadena se compara con una fecha, SQL intentará convertir la cadena a una fecha primero. Por lo tanto, comparar una columna de tipo de fecha con un valor de fecha convertido explícitamente es exactamente lo mismo que comparar con una fecha en formato de cadena. La única diferencia que veo es si compara un valor de fecha con una columna que en realidad no contiene fechas sino cadenas, pero esto sería un error en cualquier caso.

  • El uso de la conversión explícita no resuelve la ambigüedad en formatos de fecha no ISO.

La única solución que veo:

  • no compare columnas de tipo cadena con valores que no sean cadenas.
  • solo use formatos de fecha tipo ISO.

Y, por supuesto, nunca almacene fechas en una columna de tipo cadena. Pero nuevamente, la conversión explícita de literales de fecha no evitará esto.

Podría decirse que las conversiones implícitas fueron un error en SQL, pero dada la forma en que está diseñado el lenguaje, no veo el beneficio de la conversión explícita. De todos modos, no evitará la conversión implícita, y solo hace que el código sea más difícil de leer y escribir.

JacquesB
fuente
Cierto. Quizás debería señalarlo desde esta perspectiva, que lo más sensato es asegurarse de que el operando datecolumn y el operando value tengan el mismo tipo de datos (ya sea string, date, lo que sea). Hago esta recomendación específicamente solo en las preguntas donde sé que la columna de la tabla es DATETIME y su respuesta de ejemplo está usando un operando de cadena con conversión implícita ..
Caius Jard
Algo no me sienta bien en esta respuesta. Haces algunos puntos interesantes, pero creo que la conclusión es idealista. Desde una perspectiva de diseño, sí, formatos de fecha ISO no son ambiguas para el ojo humano, pero si se utiliza la conversión explícita, sintácticamente es no ambigua al analizador. Del mismo modo, muchos procesos ETL que involucran fechas requerirán alguna comparación (en forma de importación de archivo) de una cadena con el formato de fecha de la base de datos. Intentar eliminar las comparaciones de cadenas hasta la fecha me parece poco realista.
DanK
@DanK: ETL es un problema diferente: si está leyendo datos de un archivo CSV o algo así, obviamente debe procesar los datos como cadenas y analizarlos explícitamente en valores escritos. Pero ese no es el escenario que describe el OP.
JacquesB
Sin embargo, podría ser fácilmente el punto que estoy describiendo; no hay nada especial en una cadena de números almacenados en un csv que exige declarar explícitamente el formato al analizar y se vuelve relevante para el argumento que estoy haciendo si un novato lee alguna respuesta en SO donde el profesional no hace ningún esfuerzo para explícitamente declara el formato de fecha, lo que lleva a los novatos a suponer que no necesitan preocuparse por eso (o que la base de datos lo analizará correctamente todo el tiempo)
Caius Jard
@CaiusJard: Creo que estos son escenarios muy diferentes. Cuando hablo de SQL en escenarios normales, supongo que las columnas tienen los tipos apropiados, es decir, las columnas de enteros son de tipo entero, las columnas de fecha son de tipo de datos, etc. Si no tiene los tipos correctos en las tablas (es decir, almacenar fechas como cadenas) está en serios problemas y la conversión explícita de literales de fecha en consultas no lo salvará , lo cual es mi punto.
JacquesB
3

En primer lugar, tienes un punto. Las fechas no se deben poner en cadenas. Los motores de base de datos son bestias complejas en las que nunca está 100% seguro de lo que sucederá exactamente bajo el capó dada una consulta arbitraria. La conversión a fechas hace que las cosas no sean ambiguas y puede aumentar el rendimiento.

PERO

No es un problema que valga el esfuerzo extra de pensamiento para resolver para la mayoría de las personas. Si fuera fácil usar literales de fecha en una consulta, sería fácil defender su posición. Pero no lo es. Principalmente uso SQL Server, así que intentar recordar ese desastre para convertir una fecha simplemente no está sucediendo.

Para la mayoría de las personas, el aumento de rendimiento es insignificante. "¿Por qué sí, señor jefe? Pasé 10 minutos adicionales arreglando este error simple (tuve que buscar en Google cómo convertir fechas porque esa sintaxis es ... especial ...). Pero ahorré 0.00001 segundos adicionales en una consulta rara vez ejecutada ". Eso no va a volar a la mayoría de los lugares en los que he trabajado.

Pero elimina la ambigüedad en los formatos de fecha que usted dice. Nuevamente, para muchas aplicaciones (aplicaciones internas de la compañía, asuntos del gobierno local, etc., etc.) no es realmente una preocupación. Y para aquellas aplicaciones donde es una preocupación (aplicaciones grandes, internacionales o empresariales), eso se convierte en una preocupación de UI / capa de negocios o esas compañías ya tienen un equipo de DBA bien versados ​​que ya lo saben. TL / DR: si la internacionalización es una preocupación, alguien ya está pensando en ello y ya ha hecho lo que usted sugiere (o ha mitigado el problema).

¿Y ahora qué?

Si te sientes tan inclinado, sigue luchando la buena batalla. Pero no se sorprenda si la mayoría de las personas no sienten que esto es lo suficientemente importante como para preocuparse. El hecho de que haya situaciones en las que es importante no significa que esa sea la situación de todos (y probablemente no lo sea). Por lo tanto, no se sorprenda cuando reciba un impulso para algo que es técnicamente correcto y mejor, pero no realmente relevante.

Becuzz
fuente
1

Estoy argumentando que es aconsejable ser explícito con este medio que nos obliga a pasar una multitud de diferentes tipos de datos como cadenas.

Suponiendo que las "fechas" se están pasando "en" Cadenas, entonces sí; Estoy totalmente de acuerdo en que tienes razón para hacer esto.

¿Cuándo es el "01/04/07"?
* 4 de enero?
* ¿1 ° de abril?
* 7 de abril [2001]?

Cualquiera o todos estos pueden ser correctos, dependiendo de cómo "la computadora" elija interpretarlos.

Si tiene que construir SQL dinámico con literales en ellos, entonces su formato de fecha debe estar bien definido y, preferiblemente, independiente de la máquina (tuve un extraño en un servidor de Windows donde el procesamiento basado en fechas dentro de un servicio de Windows salió mal ¡porque un operador inició sesión en la consola con diferentes preferencias de formato de fecha!). Personalmente, uso exclusivamente [d] el formato "aaaa-mm-dd".

Sin embargo ...

La mejor solución es utilizar consultas parametrizadas que obligan a que el tipo de datos se convierta antes de que SQL se involucre: obtener un valor de "fecha" en un parámetro de fecha obliga a la conversión de tipo desde el principio (lo que es puramente un problema de codificación, no uno de SQL) .

Phill W.
fuente
Estoy de acuerdo, aunque el mismo problema se puede volver a forzar con consultas parametrizadas, al hacer WHERE datecolumn = @dateParametery luego en el código frontal, decirle al controlador DB que @dateParameteres de tipo varchar y pegarlo "01/04/07". La inspiración original para mi pregunta es que sospecho que cualquiera que me diga que estoy loco por hacerle eso a una consulta parametrizada, entonces, en el mismo aliento, daría una línea de respuesta SO que parece WHERE datecol = 'some string that looks like a date'(y espero que un novato sepa) es solo una pista / parametrízalo para evitar problemas)
Caius Jard