Informazioni sulla data del mio database data crociata tipo: valido? Vale la pena? Qualcun altro lo sente?


13

Passo molto tempo a rispondere alle domande SQL su SO. Mi capita spesso di incontrare domande di questo genere:

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'

ovvero fare affidamento su una conversione implicita da stringa a data (non valida), dei parametri dati o fare affidamento sul database che converte x milioni di valori di riga del database in stringa e che esegue un confronto di stringhe (peggio)

Occasionalmente faccio un commento, in particolare se si tratta di un utente di alto livello che scrive una risposta intelligente, ma che ritengo debba essere meno sciatto / tipizzato con stringhe con i loro tipi di dati

Il commento di solito prende la forma che probabilmente sarebbe meglio se convertissero esplicitamente le loro stringhe in date, usando to_date (Oracle), str_to_date (MySQL), convert (SQLSERVER) o un meccanismo simile:

    --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)

Le mie giustificazioni tecniche per farlo è che è esplicito sul formato della data e garantisce che i pochi parametri di origine diventino sicuramente il tipo di dati della colonna di destinazione. Ciò impedisce qualsiasi possibilità che il database ottenga una conversione implicita errata (l'argomento 3 gennaio / 1 marzo del primo esempio) e impedisce al db di decidere di convertire un milione di valori di data nella tabella in stringhe (utilizzando una data specifica del server formattazione che potrebbe non corrispondere nemmeno al formato della data nei parametri stringa all'interno di sql) per fare il confronto - gli orrori abbondano

La mia giustificazione sociale / accademica per farlo è che SO è un sito di apprendimento; le persone su di esso acquisiscono conoscenza implicitamente o esplicitamente. Per colpire un principiante con questa query come risposta:

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

Potrebbe indurli a pensare che sia sensato, modificando la data per il formato che preferiscono:

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

Se almeno vedessero un tentativo esplicito di convertire la data, potrebbero iniziare a farlo per il loro strano formato di data e uccidere alcuni bug per sempre prima che insorgano. Dopotutto, noi (I) proviamo a dissuadere le persone dall'entrare nell'abitudine di iniezione SQL (e qualcuno vorrebbe sostenere la parametrizzazione di una query e quindi dichiarare al driver che @pBirthdateè una stringa, quando il frontend ha un tipo di data-ora?)

Tornando a ciò che succede dopo che ho formulato la mia raccomandazione: di solito ricevo un po 'di pushback alla raccomandazione "sii esplicito, usa x", come "lo fanno tutti gli altri", "funziona sempre per me", "mostrami un manuale o un documento di riferimento che dice che dovrei essere esplicito "o addirittura" cosa ?? "

Ho chiesto, in risposta ad alcuni di questi, se avrebbero cercato una colonna int facendo WHERE age = '99'passare l'età come stringa. "Non essere sciocco, non abbiamo bisogno di mettere 'durante la ricerca di int", arriva la risposta, quindi c'è qualche apprezzamento per i diversi tipi di dati nella loro mente da qualche parte, ma forse proprio nessuna connessione al salto logico che la ricerca di un int colonna passando una stringa (apparentemente sciocca) e cercare una colonna data passando una stringa (apparentemente sensibile) è ipocrisia

Quindi nei nostri SQL abbiamo un modo per scrivere cose come numeri (usare numeri, senza delimitatori), cose come stringhe di stringhe (usare qualcosa tra delimitatori di apostrofi) .. Perché nessun delimitatore per le date? È un tipo di dati così fondamentale nella maggior parte dei DB? Tutto questo potrebbe forse essere risolto semplicemente avendo un modo di scrivere una data nello stesso modo in cui JavaScript ci consente di specificare una regex inserendo /entrambi i lati di alcuni caratteri. /Hello\s+world/. Perché non avere qualcosa per le date?

In realtà, per quanto ne so, (solo) Microsoft Access in realtà ha simboli che indicano "è stata scritta una data tra questi delimitatori" in modo da poter ottenere una buona scorciatoia come WHERE datecolumn = #somedate#ma la presentazione della data è ancora suscettibile di dare problemi, ad esempio mm / di vs dd / mm, perché la SM ha sempre giocato in maniera veloce e libera con le cose che la folla di VB pensava fosse una buona idea


Torna al punto principale: sto sostenendo che è saggio essere espliciti con questo mezzo che ci costringe a passare una moltitudine di tipi di dati diversi come stringhe.

È un'affermazione valida?

Devo continuare questa crociata? È un punto valido che digitare in modo rigoroso sia un no-no moderno? O tutti i RDBMS (comprese le versioni antiche) là fuori, quando una query verrà WHERE datecolumn = 'string value'sicuramente sicuramente convertita correttamente la stringa in una data e farà la ricerca senza convertire i dati della tabella / perdere l'uso degli indici? Sospetto di no, almeno per esperienza personale di Oracle 9. Sospetto anche che potrebbero esserci alcuni scenari di fuga se le stringhe sono sempre scritte in un formato standard ISO e la colonna ha un sapore di data, quindi il il parametro string verrà sempre convertito implicitamente correttamente. Questo lo rende giusto?

È un compito utile?

Molte persone non sembrano ottenerlo, o non gliene importa, o mostrano ipocrisia in quanto i loro ints sono ints ma le loro date sono stringhe .. Comune per la maggior parte però è che poche persone si sono mai voltate e hanno detto "sai cosa, sono d'accordo con il tuo punto. Sarò esplicito sulle mie date d'ora in poi ".


Ho anche visto qualcuno avere problemi con WHERE datecolumn = 01/02/12 '' dove è possibile che stiano chiedendo per l'anno 1912, 2012, 2001, 1901, 12 o 1. È anche un problema al di fuori del mondo del database, il numero dei programmatori che non riescono a capire perché la conversione "09"in un int causi un arresto anomalo sono legioni, 9 non è una cifra ottale valida e uno 0 iniziale rende la stringa ottale in molti sistemi
Steve Barnes

2
Ho pensato di estendere il mio esempio per chiedere se WHERE age = '0x0F'è un modo valido per sperare che un database cerchi quindicenni.
Caius Jard

1
Ho rimosso una domanda fuori tema qui: non facciamo richieste di risorse. Per questo motivo è stato dato uno dei 2 voti ravvicinati. Altrimenti, penso che questa sia una domanda valida, anche se potrebbe non essere troppo ampia. Spero che la rimozione della domanda fuori tema aiuti a restringere un po 'le cose.
Thomas Owens

TL; DR ma nei sistemi di produzione, mi aspetto che date come questa siano quasi sempre nei parametri. Le date di hardcoding nelle query sono un problema maggiore rispetto al fatto che si utilizzino conversioni implicite. Se sto scrivendo qualche query da buttare, funziona o no. Non lo faccio mai comunque (perché non ricordo mai il formato di data predefinito) ma non sono sicuro che importi molto.
JimmyJames,

1
La vita è di scegliere le tue battaglie. A mio avviso, questo non vale la pena combattere ...
Robbie Dee,

Risposte:


7

Hai scritto:

sono quei parametri dal 1 ° gennaio al 3 gennaio o dal 1 ° marzo ..

Questa è davvero una potenziale fonte di errori. Sottolinearlo a un richiedente può essere utile ad altri lettori, quindi sì, questa è una preoccupazione valida. Tuttavia, per essere costruttivo, lo farei

  • fare riferimento a ANSI SQL e utilizzare i letterali DATE o DATETIME di quello standard

  • usa il solito, inequivocabile formato datetime di un DBMS specifico (e menziona quale dialetto SQL viene usato)

Sfortunatamente, non tutti i DBMS supportano letterali data ANSI SQL esattamente nello stesso modo (se lo supportano del tutto), quindi questo in genere porterà a una variante del secondo approccio. Il fatto che "lo standard" non sia implementato rigidamente da diversi fornitori di DB è probabilmente parte del problema qui.

Nota inoltre, per molti sistemi del mondo reale, le persone possono effettivamente fare affidamento su una specifica locale fissa sul server di database, anche se le applicazioni client sono localizzate, poiché esiste un solo tipo di server, sempre configurato allo stesso modo. Pertanto, si può presumere che '01 / 03/2017 'abbia il formato fisso' gg / mm / aaaa 'o' mm / gg / aaaa 'per qualsiasi SQL utilizzato sul sistema specifico con cui stanno lavorando. Quindi, se qualcuno ti dice "funziona sempre per me", questa forse è davvero una risposta ragionevole per il suo ambiente . Se questo è il caso, rende meno utile discutere questo argomento.

Parlando di "motivi di prestazione": fintanto che non ci sono problemi di prestazione misurabili, è abbastanza superstizioso discutere con "potenziali problemi di prestazione". Se un database sta eseguendo un milione di conversioni string-to-date o meno probabilmente non importa quando la differenza di tempo è solo di 1/1000 di secondo, e il vero collo di bottiglia è la rete che fa sì che la query duri 10 secondi. Quindi meglio mettere da parte queste preoccupazioni fintanto che qualcuno chiede esplicitamente considerazioni sulle prestazioni.

Devo continuare questa crociata?

Ti dico un segreto: odio le guerre di religione. Non portano a nulla di utile. Quindi, se le specifiche di data / ora ambigue in SQL potrebbero portare a problemi, menzionali, ma non cercare di forzare le persone a essere più rigide se non portano loro alcun vantaggio nel loro contesto attuale.


Questa non è una domanda tanto sull'ambiguità dei formati di data American vs Sensible. Si tratta di capire se è ragionevole passare le date in un'istruzione SQL come stringa e fare affidamento sulla conversione implicita fino ad oggi. La questione del database che deve effettuare un milione di conversioni data-> str per tutti i milioni di righe è un aspetto prestazionale e potrebbe richiedere solo 1/1000 di secondo per una query, ma ora immaginalo nel contesto di così tante concorrenti utenti. Il problema più grande delle prestazioni è che la conversione dei dati significa che gli indici non possono più essere utilizzati e questo può essere davvero serio
Caius Jard

@CaiusJard: la mia risposta è valida: a volte è ragionevole, a volte no, dipende dal contesto. E onestamente, mi rifiuto di "... immaginare ..." qualsiasi cosa qui. Quando si tratta di prestazioni, discutere di qualsiasi caso ipotetico non è utile. Quando ci sono problemi di prestazioni misurabili, allora è il momento di ottimizzare, e talvolta di microottimizzare, non in anticipo.
Doc Brown,

È interessante vederlo come ipotetico; Vedo basarsi sul comportamento implicito come una chiara opportunità per l'insorgere di bug e complicazioni delle prestazioni (per motivi ben documentati: gli indici non funzionano se i dati dell'intera colonna vengono trasformati prima che vengano cercati) e con istruzioni esplicite questi non possono accadere
Caius Jard

@CaiusJard: non giocare con le parole - con "ipotetico" non intendo "improbabile", ho usato il termine per qualsiasi tipo di scenario immaginato, al contrario di "situazione reale esistente" in cui si può misurare ciò che accade.
Doc Brown,

1
@CaiusJard: se vuoi stupire altri professionisti del settore, dovresti sapere esattamente perché "l'ottimizzazione delle prestazioni" è molto diversa da "ottimizzazione della sicurezza", e questo è esattamente il mio punto qui: i problemi di prestazioni possono essere gestiti dopo che si verificano, che raramente troppo tardi. No, i problemi di sicurezza dovrebbero essere completamente evitati prima che si verifichino. Quindi, per favore, non confrontare le mele con le arance. Se ti piacciono le crociate, gli argomenti di sicurezza sono molto più adatti a questo ;-)
Doc Brown,

5

La tua crociata non risolve il problema.

Esistono due problemi separati:

  • conversione di tipo implicita in SQL

  • formati di date ambigui come il 05/06/07

Vedo da dove vieni con la tua crociata, ma non credo che la conversione esplicita risolva effettivamente il problema:

  • La conversione implicita si verifica comunque in caso di mancata corrispondenza tra i tipi in un confronto. Se una stringa viene confrontata con una data, SQL tenterà prima di convertire la stringa in una data. Quindi il confronto di una colonna del tipo di data con un valore di data esplicitamente convertito è esattamente lo stesso del confronto con una data in formato stringa. L'unica differenza che vedo è se si confronta un valore di data con una colonna che in realtà non contiene date ma stringhe - ma questo sarebbe comunque un errore.

  • L'uso della conversione esplicita non risolve l'ambiguità in formati di data non ISO.

L'unica soluzione che vedo:

  • non confrontare le colonne di tipo stringa con valori non di stringa.
  • utilizzare sempre e solo formati di data di tipo ISO.

E, naturalmente, non archiviare mai le date in una colonna di tipo stringa. Ma ancora una volta, la conversione esplicita dei letterali della data non lo impedirà.

Probabilmente, le conversioni implicite sono state un errore in SQL, ma dato il modo in cui è progettato il linguaggio, non vedo il vantaggio della conversione esplicita. Non eviterà comunque la conversione implicita e renderà il codice più difficile da leggere e scrivere.


Vero. Forse dovrei sottolineare da questa prospettiva che la cosa più sensata da fare è assicurarsi che l'operando della colonna di dati e l'operando di valore abbiano lo stesso tipo di dati (sia esso stringa, data, qualunque cosa). Faccio specificamente questa raccomandazione solo nelle domande in cui so che la colonna della tabella è DATETIME e la loro risposta di esempio sta usando un operando di stringa con conversione implicita.
Caius Jard

Qualcosa non è adatto a me in questa risposta. Fai alcuni punti interessanti ma mi sembra che la conclusione sia idealistica. Dal punto di vista di design, sì, formati di data non-ISO sono ambigui per l'occhio umano, ma se si utilizza la conversione esplicita, sintatticamente è non ambiguo al parser. Allo stesso modo, molti processi ETL che prevedono date richiederanno un confronto (sotto forma di importazione di file) di una stringa con il formato data del database. Cercare di eliminare i confronti tra stringhe e data non mi sembra realistico.
DanK,

@DanK: ETL è un problema diverso: se stai leggendo i dati da un file CSV o qualcosa del genere, ovviamente devi elaborare i dati come stringhe e analizzare esplicitamente i valori digitati. Ma questo non è lo scenario descritto dall'OP.
Jacques B

Potrebbe facilmente essere il punto che sto descrivendo però; non c'è niente di speciale in una stringa di numeri memorizzati in un CSV che richiede di dichiarare esplicitamente il formato durante l'analisi e diventa rilevante per l'argomento che sto formulando se un principiante legge una risposta in SO dove il professionista non fa alcuno sforzo per esplicitamente dichiarare il formato della data, portando il neofita ad assumere che non debbano preoccuparsene (o che il db lo analizzerà sempre correttamente)
Caius Jard

@CaiusJard: credo che questi siano scenari molto diversi. Quando si parla di SQL in scenari normali, presumo che le colonne abbiano i tipi appropriati, ad esempio le colonne intere sono di tipo intero, le colonne di date sono di tipo di dati e così via. Se non hai i tipi corretti nelle tabelle (ad es. Date di archivio come stringhe), sei nei guai e la conversione esplicita dei letterali delle date nelle query non ti salverà , che è il mio punto.
Jacques B,

3

Innanzitutto, hai ragione. Le date non devono essere inserite in stringhe. I motori di database sono bestie complesse in cui non si è mai sicuri al 100% di cosa accadrà esattamente sotto una copertura arbitraria. La conversione in date rende le cose inequivocabili e può aumentare le prestazioni.

MA

Non è un problema che merita lo sforzo di pensiero extra da risolvere per la maggior parte delle persone. Se fosse facile usare letterali di date in una query, sarebbe facile difendere la tua posizione. Ma non lo è. Uso principalmente SQL Server, quindi cercare di ricordare quel casino per convertire una data non sta accadendo.

Per la maggior parte delle persone, il guadagno in termini di prestazioni è trascurabile. "Perché sì, signor Boss, ho trascorso altri 10 minuti a risolvere questo semplice bug (ho dovuto cercare su Google come convertire le date perché quella sintassi è ... speciale ...). Ma ho risparmiato 0,00001 secondi in più su una query eseguita raramente ". Non volerà nella maggior parte dei posti in cui ho lavorato.

Ma rimuove l'ambiguità nei formati di data che dici. Ancora una volta, per molte applicazioni (applicazioni interne dell'azienda, cose del governo locale, ecc. Ecc.) Non è davvero un problema. E per quelle applicazioni in cui è un problema (applicazioni di grandi dimensioni, internazionali o aziendali), che diventa una preoccupazione per l'interfaccia utente / livello aziendale o quelle aziende hanno già un team di DBA esperti che già lo sanno. TL / DR: se l'internazionalizzazione è una preoccupazione, qualcuno ci sta già pensando e ha già fatto come tu suggerisci (o altrimenti ha mitigato il problema).

Così quello che ora?

Se ti senti così incline, continua a combattere la buona battaglia. Ma non essere sorpreso se la maggior parte delle persone non ritiene che questo sia abbastanza importante di cui preoccuparsi. Solo perché ci sono situazioni in cui è importante, non significa che questa sia la situazione di tutti (e probabilmente non lo è). Quindi non essere sorpreso quando ottieni qualche respingimento per qualcosa che è tecnicamente corretto e migliore, ma non proprio rilevante.


1

Sto sostenendo che è saggio essere espliciti con questo mezzo che ci costringe a passare una moltitudine di tipi di dati diversi come stringhe.

Supponendo che "date" vengano passate in giro "in" Stringhe quindi sì; Sono assolutamente d'accordo che hai ragione a farlo.

Quando è "01/04/07"?
* 4 gennaio?
* 1 Aprile?
* 7 aprile [2001]?

Qualcuno o tutti questi potrebbero essere corretti, a seconda di come "il computer" sceglie di interpretarli.

Se si dispone di costruire SQL dinamico con letterali in loro, quindi la data di formattazione deve essere ben definito e, preferibilmente, la macchina-indipendente (avevo una strana uno su un Windows Server in cui l'elaborazione su base cronologica all'interno di un servizio di Windows è andato storto perché un operatore ha effettuato l'accesso alla console con preferenze di formato data diverse!). Personalmente, utilizzo esclusivamente [d] il formato "aaaa-mm-gg".

Tuttavia ...

La soluzione migliore è utilizzare le query con parametri che impongono la conversione del tipo di dati prima che venga coinvolto SQL - ottenere un valore "data" in un parametro Date impone la conversione del tipo all'inizio (rendendolo puramente un problema di codifica, non SQL) .


Sono d'accordo, anche se lo stesso problema può essere forzato nuovamente con le query parametrizzate, eseguendo WHERE datecolumn = @dateParametere quindi nel codice front-end, dicendo al driver DB che @dateParameterè di tipo varchar e restando incollato "01/04/07". L'ispirazione originale per la mia domanda è che sospetto che chiunque mi dica che sono pazzo di farlo a una query parametrizzata darebbe, nello stesso respiro, una risposta SO di una riga che sembra WHERE datecol = 'some string that looks like a date'(e aspettarsi che un principiante dovrebbe sapere è solo un suggerimento / parametrizza per evitare problemi)
Caius Jard
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.