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 ".
WHERE age = '0x0F'
es una forma válida de esperar que una base de datos busque quince años ..
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