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 @pBirthdate
es 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 ".
fuente
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 sistemasWHERE age = '0x0F'
es una forma válida de esperar que una base de datos busque quince años ..Respuestas:
Tu escribiste:
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.
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.
fuente
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:
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.
fuente
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.
fuente
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) .
fuente
WHERE datecolumn = @dateParameter
y luego en el código frontal, decirle al controlador DB que@dateParameter
es 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 pareceWHERE datecol = 'some string that looks like a date'
(y espero que un novato sepa) es solo una pista / parametrízalo para evitar problemas)