Campo Data tra VB.NET ed SQL Server

di il
6 risposte

Campo Data tra VB.NET ed SQL Server

Buongiorno,

vi chiedo aiuto perchè sto impazzendo su un concetto che riguarda un argomento già trattato ma su cui
non riesco ad oggi a trovare una risposta esaustiva e comunque mirata. Il problema risiede nella coerenza del campo datetime tra VB e SqlServer.

Premesse: tutto va inteso sotto un sistema operativo con impostazioni internazionali/lingua del pannello di controllo impostate su Italiano e con data breve nel formato dd/MM/yyyy

--------------------------------------------------
Dunque, utilizzo Vb.net sempre connesso ad un Database SqlServer express (2014).

Il Database è stato installato utilizzando la lingua inglese (questo è ininfluente) e la login utilizzata da VB per il collegamento all'archivio (che è il dato rilevante) riporta la lingua Inglese, quindi tutte le query che verranno eseguite da Sql Management Studio tratteranno il campo smalldatetime in formato inglese yyyy/MM/dd.

Ovviamente come controprova, andando a chiedere un Select Top 1000 di una tabella (pulsante destro su nome tabella da SSMS) le date escono nel formato yyyy-MM-dd. Quindi tutto corretto.

Da VB.NET quando devo inserire un record in una tabella, ad esempio Mario, 03 Febbraio 2020, tramite ADO.NET Scrivo:
SqlCommand.Commantext = "Insert into dbo.Tabella1 (Nome,Data_Nascita) VALUES ('Mario','20200203');"
quindi vado ad effettuare la scrittura mantenendo sempre la formattazione yyyyMMdd.

Il tutto funziona, perchè ordinando per data nella funzione Select Top 1000 i recordo vengono effettivamente letti in yyyy-MM-dd.
Preciso che la data che scrivo attraverso la query la prelevo da una textBox che so essere compilata da un utente in formato italiano dd/MM/yyyy, quindi per non sbagliare prelevo le ultime 4 cifre, le concateno alle 2 centrali ed infine alle prime 2 cifre cosi da ottenere yyyy-MM-dd

Quando sorge il dubbio?

Nella ricerca.

Non capisco come mai ottengo risultati identici ( e corretti) se andassi a scrivere le seguenti query di ricerca per data compresa tra il 13 Feb 2020 ed il 02 Marzo 2020:

caso 1) CMD.CommandText = "Select * from dbo.persone WHERE (Data_Nascita >= '20200213' and Data_Nascita =< '20200302')
caso 2) CMD.CommandText = "Select * from dbo.persone WHERE (Data_Nascita >= '02132020' and Data_Nascita =< '03022020')

Ovvero funzionano entrambe perfettamente le date scritte in yyyy-MM-dd e MM-dd-yyyy.
Salta fuori un errore nel caso utilizzassi il formato italiano dd/MM/yyyy.

Qual è la motivazione tecnica del perchè funzionano bene entrambe? C'è qualche bug dietro l'angolo utilizzandole entrambe? Esiste un modo per definire uno ed un singolo modo sistema?

Uso spesso questa conversione su una textbox riempita da un utente in formato dd/MM/yyyy per aggiungere un giorno alla data inserita:
Dim Data_Finale as String
Data_Finale = DateAdd(DateInterval.Day, +1, CDate(CasellaTesto1.Text))
Data_Finale = Format(CDate(Data_Finale), "MM/dd/yyyy")

Campo già formattato per il ''caso 2''. Questa procedura verrebbe invalidata se il Sistema operativo cambiasse impostazione?
Insomma quali potrebbero essere le implicazioni negative.

Spero di essere stato il più esaustivo possibile e spero in una vostra illuminazione ))
Grazie.

6 Risposte

  • Re: Campo Data tra VB.NET ed SQL Server

    Salve,
    personalmente riscontro delle problematiche con i tuoi dati...

    relativamente alla parte SQL Server, eseguendo il codice di sotto su SQL Server 2014, con impostazioni di lingua per la login in en-US
    
    SELECT @@LANGUAGE;
    GO
    SELECT CONVERT(date, '20200213')
    	, CONVERT(date, '20200302')
    	, CONVERT(date, '02132020')
    	, CONVERT(date, '03022020');
    GO
    SELECT CONVERT(date, '20200213')
    	, CONVERT(date, '20200302')
    	, CONVERT(date, '02-13-2020')
    	, CONVERT(date, '03-02-2020')
    
    ottengo:
    
    
    ----------------------
    us_english
    
    (1 row(s) affected)
    
                                     
    ---------- ---------- ---------- ----------
    Msg 241, Level 16, State 1, Line 3
    Conversion failed when converting date and/or time from character string.
    
                                     
    ---------- ---------- ---------- ----------
    2020-02-13 2020-03-02 2020-02-13 2020-03-02
    
    quindi come vedi, la localizzazione per le date e' legata alla locale americana

    il primo comando con la formattazione senza "-" mi solleva eccezioni nella conversione, e per questo solitamente utilizzo il formato con separazione "-", quindi "xx-xx-xxxx";
    scrivo "genericamente" xx in quanto non voglio qui fornire indicazioni sul formato specifico di data...
    questo perche', per buona prassi, al di la' della localizzazione della login, e' sempre preferibile passare le date in formato ISO "yyyy-MM-dd", formato SEMPRE riconoscibile e riconosciuto, non suscettibile di "interpretazione" e sempre corretto e validato.

    tornando allo script di cui sopra, dopo la correzione dell'eccezione, guardiamo cosa succede con i valori
    , CONVERT(date, '02-13-2020')
    , CONVERT(date, '03-02-2020')

    il primo dei 2 viene ovviamente correttamente interpretato, in quanto non e' ovviamente possibile avere un mese = 13, e la data viene parsata e interpretata come: 2020-02-13

    il valore successivo, CONVERT(date, '03-02-2020'), con impostazione americana, viene letto anche questo come MM-dd-yyyy, quindi 2 marzo 2020...

    e potrebbe NON essere il risultato desiderato... motivo quindi addizionale di utilizzare sempre una formattazione NON interpretabile, quindi "yyyy-MM-dd", che ripeto, NON e' interpretabile diversamente da quanto specificato...

    relativamente alla parte applicativa, ti sconsiglio vivamente di passare codice dinamico con parametri gia' valorizzati tipo
      
    CMD.CommandText = "Select * from dbo.persone WHERE (Data_Nascita >= '20200213' and Data_Nascita =< '20200302')
    
    e' sempre buona norma, sia per impedire problematiche di SQLinjection che per semplificare la valorizzazione dei parametri utilizzare SEMPRE un comando con parametri non "inline" ma aggiunti alla collection dei parameters dell'oggetto command...
    questo di aiuta anche in quanto e' direttamente l'oggetto parameter caricato con il "valore di tipo date" preso ad esempio dalla tua conversione
    
    ....  DateAdd(DateInterval.Day, +1, CDate(CasellaTesto1.Text))
    
    ad occuparsi del caricamento del "valore" in maniera trasparente e dipendente dalla locale "applicativa", in quanto CDATE utilizza la locale corrente e cerca di convertire il testo in una "data", dopodiche' l'operazione di DateAdd semplicemente utilizza tale valore nella sua finestra funzionale...
    comunque, ripeto, MAI usare
      
    CMD.CommandText = "Select * from dbo.persone WHERE (Data_Nascita >= '" & dataIniziale & "' and Data_Nascita =< '" & dataFinale & " ');"
    
    o cose simili...

    dal lato applicativo (VB), le impostazioni che "valgono" al fine dell'interpretazione dei dati sono sempre legate alla locale dell'account in esecuzione...
    quindi la data "teorica"
    
    02/03/2020
    
    potrebbe benissimo essere interpretata, a seconda dei casi, in 2 marzo oppure 3 febbraio, ed il risultato NON e' uguale :D
    ... come anche sollevare un'eccezione di parsing...

    quindi, a ripilogo, per SQL Server, dove "dovuto", usare sempre il formato ISO yyyy-MM-dd...
    nel caso, quindi, caricare
      
    CMD.CommandText = "Select * from dbo.persone WHERE (Data_Nascita >= '2020-02-13' and Data_Nascita =< '2020-03-02')
    
    ma, in ogni caso, utilizzare SEMPRE i parameters per caricare i parametri da utilizzare nelle queries...
    salutoni
    --
    Andrea
  • Re: Campo Data tra VB.NET ed SQL Server

    asql ha scritto:


    ma, in ogni caso, utilizzare SEMPRE i parameters per caricare i parametri da utilizzare nelle queries...
    Ci vorrebbe un modo per mettere questo consiglio in primo piano, con testo grande di colore sgargiante e lampeggiante, che sia impossibile ignorarlo, ovunque si parli di accesso ai dati.

    Non riuscirò mai a capacitarmi del perché i parametri vengano usati così poco nonostante i benefici che possono dare.
    Si perdoni il mio sfogo.

    Un saluto!
  • Re: Campo Data tra VB.NET ed SQL Server

    Gentilissimo Andrea,

    innanzitutto la ringrazio per la risposta dettagliata, per gli esempi e per il tempo dedicato ai test.
    Ho letto molto attentamente quanto mi ha scritto, cosi ho ulteriormente approfondito il caso facendo degli ulteriori test sulla base di altre specifiche che allegherò più avanti.

    Desideravo precisare anche che la stringa SQL in chiaro, senza l'uso dei parametri dell'oggetto command, l'ho scritta per brevità di lettura.. il vero progetto è molto più esteso e complesso e prevede certamente parametri passati in modo corretto ma quello che mi preme capire è appunto il passaggio di un ''accademico'' (Nome, Età) che banalmente possiamo passare tra i due apici per poi implementarlo a dovere.

    Dunque, appurato che risulti molto conveniente utilizzare un formato INDIPENDENTE dalle impostazioni locali dell'OS o della lingua della Login, sicuro ed inequivocabile, ci riferiremo allo standard ISO di SqlServer. A tal proposito, un estratto di una spiegazione (che per correttezza potete trovare nella sua interezza cliccando sul link inserito all'inizio dell'estratto - leggendo il regolamento di questo forum non credo di violare una norma inserendolo- ) ho letto anche che:
    Fonte: https://www.ugiss.org/2008/07/12/gestire-date-ed-orari-con-sql-server-con-sql-server-2000-e-2005/

    Cerchiamo di rispondere all’annosa domanda: il formato YYYYMMDD è uguale a YYYY-MM-DD?

    SQL Server supporta il formato ISO 8601 per la specifica dei valori di data e ora.

    L’utilità di utilizzare tale formato è che l’interpretazione della data espressa è indipendente dalla localizzazione [...] e da una lettura dello stesso si può dedurre che i pattern yyyymmdd e yyyy-mm-dd dovrebbero essere trattati in modo identico.

    Per SQL Server, però, il formato ISO è definito solamente dal seguente pattern:

    yyyy-mm-ddThh:mm:ss[.nnn]

    Questo significa che pensare che i formati yyyy-mm-dd e yyyymmdd siano identici, quando si parla di SQL Server, è errato.

    I BOL sono chiari (ma non a sufficienza):

    "To use the ISO 8601 format, you must specify each element in the format. This also includes the punctuation marks that are shown in the format."

    Tradotto in pratica, l’esempio che dimostra il funzionamento dei vari formati è questo:

    SET DATEFORMAT DMY
    SELECT CAST(‘20060501’ as datetime) — Corretto: 1 Maggio 2006
    SELECT CAST(‘2006-05-01T00:00:00’ as datetime) — Corretto: 1 Maggio 2006
    SELECT CAST(‘2006-05-01’ as datetime) — Sbagliato: 5 Gennaio 2006
    SELECT CAST(‘2006-05-01 00:00:00’ as datetime) — Sbagliato: 5 Gennaio 2006

    L’utilizzo del SET DATEFORMAT è necessario per dimostrare l’errore, in quanto, se si usasse invece il formato MDY (quello inglese per intenderci), l’errore non si presenterebbe e la data verrebbe riconosciuta correttamente anche in presenza di una stringa che non rappresenta un formato ISO riconosciuto da SQL Server.

    Letto ciò,
    ho dedotto che:
    Se non interessa registrare l'orario ma solo la data xx-xx-xxxx composta da 8 cifre l'unico modo è passarla in formato ISO SENZA I TRATTINI, a meno che non si inserisca un T00:00:00 subito dopo come costante per dichiare un orario fisso ed ininfluente.

    A questo punto ho fatto dei test ed ecco il codice t-sql:

    
    SELECT @@LANGUAGE;
    set dateformat DMY
    
    	SELECT  
    	  CONVERT(date, '20200102') 
    	, CONVERT(date, '03-02-2020')
    	, CONVERT (date, '2006-05-01')
    	, CAST('2006-05-01' as smalldatetime)
    
    ho integrato quindi anche quello che era il formato da lei utilizzato (YYYY-MM-DD) quando mi ha scritto:
    nel caso, quindi, caricare
    CMD.CommandText = "Select * from dbo.persone WHERE (Data_Nascita >= '2020-02-13' and Data_Nascita =< '2020-03-02')
    Sorpresa:
    a quanto pare cambiando il dateformat tra DMY e MDY otteniamo che apparentemente solo la prima e la terza data restano esenti dalle "interpretazioni" mostrandosi sempre nella sequenza YYYY-MM-DD

    Questo però va in contraddizione con l'estratto riportato.. sbaglio qualcosa nella lettura?

    non riesco a capire se devo o non devo usare i trattini di separazione (non inserendo l'orario)

    Insomma, mi piacerebbe capire dove io mi stia ingannando e desidererei sapere se fossi SEMPRE inattaccabile da errori trattando i dati in questo modo:
    
    'inserisco Mario Rossi nato il 02 Maggio 2020
    "Insert into tabella1 (Nome, DataNascita) VALUES ('Mario Rossi','20200502');
    
    e se facessi di conseguenza sempre cosi le ricerche:
    
    ' Cerco i nati tra il 01 Maggio 2020 incluso ed il 10 Maggio 2020 incluso
    CMD.CommandText = "Select * from Tabella1 WHERE (Data_Nascita >= '20200501' and Data_Nascita =< '20200510')
    
    Ringrazio anticipatamente..
  • Re: Campo Data tra VB.NET ed SQL Server

    Salve,
    brutalmente, scrivo codice T-SQL dalla versione 7.0 (codename Sphinx) del lontano 1999, e le date, intese come rappresentazione testuale, sono sempre risultate validabili e correttamente interpretare nel formato 'yyyy-MM-dd' e 'yyyyMMdd'
    quindi
    DECLARE @d date /* o anche datetime */ = '2020-02-01';
    e' sempre stato correttamente validato ed interpretato come 1 febbraio dell'anno 2020
    E
    non utilizzo MAI l'istruzione SET DATEFORMAT, che tendenzialmente e' nata per ovviare problemi interpretativi che NON sorgerebbero se venisse utilizzato un formato che come standard viene riconosciuto, nel codice che scrivo e revisiono "accetto" quindi 'yyyy-MM-dd' E 'yyyyMMdd' e tendenzialmente rigetto gli altri, anche con sotterfugi di SET....

    personalmente NON amo neanche impostare DATEFIRST e preferisco interrogare @@DATEFIRST per saperne la valorizzazione dove per me interessante...
    sono sicuramente abitudini/vezzi/vizi personali di "una vita", ma tant'e'

    Io utilizzo sempre la localizzazione di sistema (en-US) per la mia login ma comunque verifico sempre che i parametri o le valorizzazioni hard-coded NON entrino nel flusso del codice in formato "nazionale" (a prescindere dalla nazione) e che dipendano appunto dalla localizzazione della login in esecuzione per la loro interpretazione, aderendo quindi allo "standard" americano ... poco "nazionale", ma funziona

    salutoni
    --
    Andrea
  • Re: Campo Data tra VB.NET ed SQL Server

    Afferrato!

    Mi fido ciecamente
    Utilizzerò per andare sul sicuro allora uno dei due metodi (yyyy-MM-dd oppure yyyyMMdd) sia per l'inserimento di nuovi record che per la ricerca con gli operatori < e >.
    La login la creo sempre lasciando in default la lingua English.

    Anche io sono per il poco "nazionale".. uno standard un solo modo di vedere le cose (niente errori)! Per soddisfare la consuetudine di vedere le date in formato italiano creo una function apposita..

    Che dire, grazie di tutto. E' stato gentilissimo.

    Buona giornata.
    Luca
  • Re: Campo Data tra VB.NET ed SQL Server

    Luca,
    se posso permettermi, 2 cose...
    1) so che sono "anziano" e le mie origini derivano da usenet, ma (E proprio PER la mia visione delle commnunities) in generale nei forum "non si da del Lei" a meno di non voler rasentare l'insulto il oppure il sarcasmo... (mia visione...)

    2) NON fidarti mai ciecamente. Fidati solo di quello che riesci a verificare

    un salutone romagnolo
    --
    Andrea
Devi accedere o registrarti per scrivere nel forum
6 risposte