Meilleure façon de stocker l'heure (hh: mm) dans une base de données

100

Je souhaite stocker les heures dans une table de base de données, mais je n'ai besoin que de stocker les heures et les minutes. Je sais que je pourrais simplement utiliser DATETIME et ignorer les autres composants de la date, mais quelle est la meilleure façon de le faire sans stocker plus d'informations que ce dont j'ai réellement besoin?

Matthew Commode
la source
Quel est l'équivalent de TIME pour SQL Server 2005?
Erran Morad
@BoratSagdiyev il n'y en a pas dans SQL Server 2005, c'est pourquoi j'ai posé la question.
Matthew Dresser le

Réponses:

135

Vous pouvez le stocker sous forme d'entier du nombre de minutes après minuit:

par exemple.

0 = 00:00 
60 = 01:00
252 = 04:12

Il vous faudrait cependant écrire du code pour reconstituer l'heure, mais cela ne devrait pas être compliqué.

BigJump
la source
8
Ensuite, utilisez DATEADD () pour récupérer les temps réels. Même le plus petit serait suffisant.
Joel Coehoorn le
36
été là, fait ça ... mins = dd% 60 et heures = dd / 60 sur ints fait l'affaire.
Osama Al-Maadeed le
2
C'est une excellente idée, simple et directe. +1
whiskeysierra
7
un petit inconvénient est le manque de lisibilité dans la base de données
Jowen
nous devons stocker une variante des jours, des heures et des minutes; le type de données Time dans SQL Server ne va que jusqu'à 23:59:59, nous ne pouvons donc pas l'utiliser. Inspiré par votre réponse, nous avons décidé d'avoir trois colonnes int pour jours: heures: minutes pour une flexibilité maximale. Merci!
matao
55

Si vous utilisez SQL Server 2008+, tenez compte du TIMEtype de données. Article SQLTeam avec plus d'exemples d'utilisation.

WebMatrix
la source
15

DATETIME début DATETIME fin

Je vous implore d'utiliser à la place deux valeurs DATETIME , étiquetées quelque chose comme event_start et event_end .

Le temps est une affaire complexe

La plupart des pays du monde ont maintenant adopté le système métrique basé sur le denery pour la plupart des mesures, à tort ou à raison. C'est bien dans l'ensemble, car au moins nous pouvons tous convenir que ag, est un ml, est un cm cube. Du moins à peu près. Le système métrique a de nombreux défauts, mais au moins, il est systématiquement imparfait au niveau international.

Avec le temps cependant, nous avons; 1000 millisecondes dans une seconde, 60 secondes à une minute, 60 minutes à une heure, 12 heures pour chaque demi-journée, environ 30 jours par mois qui varient selon le mois et même l'année en question, chaque pays a son décalage horaire par rapport aux autres , la façon dont l'heure est formatée dans chaque pays varie.

C'est beaucoup à digérer, mais au bout du compte, il est impossible pour un scénario aussi complexe d'avoir une solution simple.

Certains coins peuvent être coupés, mais il y a ceux où il est plus sage de ne pas

Bien que la réponse principale ici suggère que vous stockez un nombre entier de minutes après minuit peut sembler parfaitement raisonnable, j'ai appris à éviter de le faire à la dure.

Les raisons d'implémenter deux valeurs DATETIME sont pour une augmentation de la précision, de la résolution et de la rétroaction.

Ceux-ci sont tous très pratiques lorsque la conception produit des résultats indésirables.

Est-ce que je stocke plus de données que nécessaire?

Il peut sembler initialement que plus d'informations sont stockées que ce dont j'ai besoin, mais il y a une bonne raison de prendre ce coup.

Stocker ces informations supplémentaires finit presque toujours par me faire gagner du temps et des efforts sur le long terme, car je constate inévitablement que lorsque quelqu'un est informé de la durée de quelque chose, il voudra également savoir quand et où l'événement a eu lieu.

C'est une énorme planète

Dans le passé, j'ai été coupable d'ignorer qu'il y a d'autres pays sur cette planète en dehors du mien. Cela semblait être une bonne idée à l'époque, mais cela a TOUJOURS entraîné des problèmes, des maux de tête et une perte de temps plus tard sur toute la ligne. Tenez toujours compte de tous les fuseaux horaires.

C #

Un DateTime se rend bien en une chaîne en C #. La méthode ToString (string Format) est compacte et facile à lire.

Par exemple

new TimeSpan(EventStart.Ticks - EventEnd.Ticks).ToString("h'h 'm'm 's's'")

Serveur SQL

De plus, si vous lisez votre base de données séparément de l'interface de votre application, il est agréable de lire dateTimes en un coup d'œil et d'effectuer des calculs dessus est simple.

Par exemple

SELECT DATEDIFF(MINUTE, event_start, event_end)

Norme de date ISO8601

Si vous utilisez SQLite, vous ne l'avez pas, alors utilisez plutôt un champ Texte et stockez-le au format ISO8601, par exemple.

"2013-01-27T12: 30: 00 + 0000"

Remarques:

  • Cela utilise une horloge de 24 heures *

  • La partie du décalage horaire (ou +0000) de l'ISO8601 correspond directement à la valeur de longitude d'une coordonnée GPS (sans tenir compte de l'heure d'été ou du pays).

Par exemple

TimeOffset=(±Longitude.24)/360 

... où ± fait référence à la direction est ou ouest.

Il vaut donc la peine de se demander s'il vaudrait la peine de stocker la longitude, la latitude et l'altitude avec les données. Cela variera en application.

  • ISO8601 est un format international.

  • Le wiki est très bon pour plus de détails à http://en.wikipedia.org/wiki/ISO_8601 .

  • La date et l'heure sont enregistrées dans l'heure internationale et le décalage est enregistré en fonction de l'endroit dans le monde où l'heure a été stockée.

D'après mon expérience, il est toujours nécessaire de stocker la date et l'heure complètes, que je pense qu'il y en ait ou non au début du projet. ISO8601 est une très bonne façon de le faire.

Conseils supplémentaires gratuits

Cela vaut également la peine de regrouper les événements comme une chaîne. Par exemple, si vous enregistrez une course, l'événement entier pourrait être regroupé par coureur, race_circuit, circuit_checkpoints et circuit_laps.

D'après mon expérience, il est également judicieux d'identifier qui a conservé l'enregistrement. Soit en tant que table séparée remplie via un déclencheur, soit en tant que colonne supplémentaire dans la table d'origine.

Plus vous en mettez, plus vous en sortez

Je comprends parfaitement le désir d'être aussi économique que possible avec l'espace, mais je le ferais rarement au détriment de la perte d'informations.

Une règle de base avec les bases de données est, comme le titre l'indique, qu'une base de données ne peut vous en dire autant qu'elle contient des données, et il peut être très coûteux de parcourir les données historiques, en comblant les lacunes.

La solution est de le corriger du premier coup. C'est certainement plus facile à dire qu'à faire, mais vous devriez maintenant avoir une idée plus approfondie de la conception efficace de la base de données et avoir par la suite une chance beaucoup plus grande de bien faire les choses du premier coup.

Plus votre conception initiale est bonne, moins les réparations seront coûteuses par la suite.

Je dis seulement tout cela, parce que si je pouvais remonter le temps, c'est ce que je me dirais en arrivant là-bas.

WonderWorker
la source
2
Votre commentaire "La partie +0000 de l'ISO8601 correspond directement à la lattitude dans une coordonnée GPS" est faux. Il représente le décalage par rapport à UTC
Gary Walker
10

Stockez simplement une date / heure normale et ignorez tout le reste. Pourquoi passer plus de temps à écrire du code qui charge un int, le manipule et le convertit en datetime, alors que vous pouvez simplement charger un datetime?

Seth
la source
3
Une raison possible pourrait être d'économiser de l'espace sur le disque dur - Un DATETIMEtype de données prend 4 octets à stocker, tandis qu'un SMALLINTpar exemple, n'en prend qu'un quart. Pas de grande différence si vous n'avez que quelques milliers de lignes, mais si vous avez plusieurs millions de lignes comme le font de nombreuses entreprises, alors vos économies d'espace seront substantielles.
Sheridan
32
Peut-être, mais considérez que 12 personnes ont répondu à cette question, chacune prenant, disons, 15 minutes pour y réfléchir. Supposons qu'ils soient tous des programmeurs et qu'ils gagnent 50 $ l'heure. Avec le coût du temps passé à réfléchir à ce problème, vous auriez pu acheter un nouveau disque dur brillant de 2 To pour stocker les octets supplémentaires.
Seth
@Seth vous avez raison si nous ne parlons que d'octets supplémentaires. Mais votre suggestion ne peut être que s'il s'agit d'un petit projet avec 1-2 développeurs. Mais ce sera un gros projet avec beaucoup de développeurs (plus QA), ce serait un gros problème quand un nouveau programmeur essaiera de comprendre cela. Hélas, nous dépendons tous de la logique métier.
Mediator
Avec les services cloud, économiser de l'espace disque peut être important dans les cas où vous payez par octet lu / traité.
RedShift
2
Un autre problème amusant est que si vous comptez sur la composante heure, elle ne tiendra pas compte des ajustements d'heure d'été lorsqu'elle est utilisée à différentes périodes de l'année.
Chris Seufert
4

puisque vous ne l'avez pas mentionné un peu si vous êtes sur SQL Server 2008, vous pouvez utiliser le type de données time sinon utiliser les minutes depuis minuit

SQLMenace
la source
3

SQL Server stocke en fait le temps sous forme de fractions de jour. Par exemple, 1 jour entier = valeur de 1. 12 heures est une valeur de 0,5.

Si vous souhaitez stocker la valeur d'heure sans utiliser de type DATETIME, stocker l'heure sous forme décimale répondra à ce besoin, tout en simplifiant la conversion en DATETIME.

Par exemple:

SELECT CAST(0.5 AS DATETIME)
--1900-01-01 12:00:00.000

Le stockage de la valeur en tant que DECIMAL (9,9) consomme 5 octets. Cependant, si la précision n'est pas de la plus haute importance, un REAL ne consommera que 4 octets. Dans les deux cas, le calcul agrégé (c'est-à-dire le temps moyen) peut être facilement calculé sur des valeurs numériques, mais pas sur des types de données / temps.

Taylor Gerring
la source
"SQL Server stocke en fait le temps sous forme de fractions de jour." Je pense qu'il stocke les jours depuis (ou avant) le 1er janvier 1900 dans les 4 premiers octets et l'heure, en millisecondes, dans les 4 octets suivants. (SmallDateTime utilise 2 octets pour chacun avec une plage de dates plus étroite et des minutes, plutôt que des millisecondes pour l'heure)
Kristen
Je sais (sûr à 99,99%) que pour le moment de la journée, ce sont des fractions.
graham.reeds
2

Je les convertirais en un entier (HH * 3600 + MM * 60) et les stockerais de cette façon. Petite taille de stockage, et toujours assez facile à utiliser.

Glen Solsberry
la source
2

Si vous utilisez MySQL, utilisez un champ de type TIME et la fonctionnalité associée fournie avec TIME.

00:00:00 est le format d'heure UNIX standard.

Si jamais vous devez regarder en arrière et revoir les tableaux à la main, les entiers peuvent être plus déroutants qu'un horodatage réel.

Syntaxe
la source
1

Essayez smalldatetime. Cela peut ne pas vous donner ce que vous voulez mais cela vous aidera dans vos futurs besoins en manipulations de date / heure.

MarlonRibunal
la source
si @mdresser utilisait <MSSQL 2005, le serveur passerait par "valeur smalldatetime hors de portée"
Fergus
1

Êtes-vous sûr que vous n'aurez jamais besoin que des heures et des minutes? Si vous voulez faire quelque chose de significatif avec lui (comme par exemple calculer des intervalles de temps entre deux de ces points de données), ne pas avoir d'informations sur les fuseaux horaires et l'heure d'été peut donner des résultats incorrects. Les fuseaux horaires ne s'appliquent peut-être pas dans votre cas, mais l'heure d'été le fera certainement.

mghie
la source
1

Au lieu de minutes après minuit, nous le stockons au format 24 heures, en tant que SMALLINT.

09:12 = 912 14:15 = 1415

lors de la conversion en "forme lisible par l'homme", nous insérons simplement deux points ":" deux caractères à partir de la droite. Pavé gauche avec des zéros si vous en avez besoin. Enregistre les mathématiques dans chaque sens et utilise quelques octets de moins (par rapport à varchar), et garantit que la valeur est numérique (plutôt que alphanumérique)

Assez maladroit cependant ... il aurait dû y avoir un type de données TIME dans MS SQL depuis plusieurs années déjà à mon humble avis ...

Kristen
la source
Kristen a tout à fait raison, mais seulement si son hypothèse sur les heures et les minutes ne représentant qu'une seule période de 24 heures est correcte. 10 bits sont nécessaires pour stocker 0..1439 minutes. Cela signifie qu'il y a 6 bits supplémentaires qui peuvent être utilisés comme vous le souhaitez, il y a donc de la place pour faire preuve de créativité. Je suggérerais d'utiliser 5 bits pour stocker le décalage horaire pour le fuseau horaire dans lequel vous vous trouvez, mais uniquement si le demandeur voulait uniquement stocker une durée maximale de 23 h 59 (une minute à minuit)
WonderWorker
1

Ce que je pense que vous demandez, c'est une variable qui stockera les minutes sous forme de nombre. Cela peut être fait avec les différents types de variable entière:

SELECT 9823754987598 AS MinutesInput

Ensuite, dans votre programme, vous pouvez simplement afficher ceci sous la forme que vous souhaitez en calculant:

long MinutesInAnHour = 60;

long MinutesInADay = MinutesInAnHour * 24;

long MinutesInAWeek = MinutesInADay * 7;


long MinutesCalc = long.Parse(rdr["MinutesInput"].toString()); //BigInt converts to long. rdr is an SqlDataReader.   


long Weeks = MinutesCalc / MinutesInAWeek;

MinutesCalc -= Weeks * MinutesInAWeek;


long Days = MinutesCalc / MinutesInADay;

MinutesCalc -= Days * MinutesInADay;


long Hours = MinutesCalc / MinutesInAnHour;

MinutesCalc -= Hours * MinutesInAnHour;


long Minutes = MinutesCalc;

Un problème survient lorsque vous demandez que l'efficacité soit utilisée. Mais si vous manquez de temps alors utilisez simplement un BigInt nullable pour stocker la valeur de vos minutes.

Une valeur nulle signifie que l'heure n'a pas encore été enregistrée.

Maintenant, je vais l'expliquer sous la forme d'un aller-retour dans l'espace extra-atmosphérique.

Malheureusement, une colonne de table ne stockera qu'un seul type. Par conséquent, vous devrez créer une nouvelle table pour chaque type selon les besoins.

Par exemple:

  • Si MinutesInput = 0 .. 255 alors utilisez TinyInt (Convertissez comme décrit ci-dessus).

  • Si MinutesInput = 256 .. 131071, utilisez SmallInt (Remarque: la valeur minimale de SmallInt est -32 768. Par conséquent, annulez et ajoutez 32768 lors du stockage et de la récupération de la valeur pour utiliser la plage complète avant la conversion comme ci-dessus).

  • Si MinutesInput = 131072 .. 8589934591, utilisez alors Int (Remarque: annulez et ajoutez 2147483648 si nécessaire).

  • Si MinutesInput = 8589934592 .. 36893488147419103231, utilisez BigInt (Remarque: ajoutez et annulez 9223372036854775808 si nécessaire).

  • Si MinutesInput> 36893488147419103231, j'utiliserais personnellement VARCHAR (X) en augmentant X si nécessaire, car un caractère est un octet. Je devrai revoir cette réponse à une date ultérieure pour la décrire dans son intégralité (ou peut-être qu'un collègue stackoverflowee pourra terminer cette réponse).

Étant donné que chaque valeur nécessitera sans aucun doute une clé unique, l'efficacité de la base de données ne sera apparente que si la plage des valeurs stockées est un bon mélange entre très petite (proche de 0 minute) et très élevée (supérieure à 8589934591).

Jusqu'à ce que les valeurs stockées atteignent réellement un nombre supérieur à 36893488147419103231, vous pouvez également avoir une seule colonne BigInt pour représenter vos minutes, car vous n'aurez pas besoin de gaspiller un Int sur un identifiant unique et un autre int pour stocker la valeur des minutes.

WonderWorker
la source
0

Le gain de temps au format UTC peut mieux aider, comme l'a suggéré Kristen.

Assurez-vous que vous utilisez une horloge de 24 heures car il n'y a pas de méridien AM ou PM être utilisé en UTC.

Exemple:

  • 04h12 - 0412
  • 10 h 12 à 10 h 12
  • 14h28 - 1428
  • 23h56 - 2356

Il est toujours préférable d'utiliser le format standard à quatre chiffres.

balamurugavel
la source
0

Stockez le tickssous forme de long/ bigint, qui sont actuellement mesurés en millisecondes. La valeur mise à jour peut être trouvée en regardant leTimeSpan.TicksPerSecond valeur.

La plupart des bases de données ont un type DateTime qui stocke automatiquement l'heure sous forme de graduations dans les coulisses, mais dans le cas de certaines bases de données, par exemple SqlLite, stocker des graduations peut être un moyen de stocker la date.

La plupart des langues permettent la conversion facile de TicksTimeSpanTicks.

Exemple

En C #, le code serait:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Attention cependant, car dans le cas de SqlLite, qui ne propose qu'un petit nombre de types différents, qui sont; INT, REALet VARCHARIl sera nécessaire de stocker le nombre de graduations sous forme de chaîne ou de deux INTcellules combinées. En effet, an INTest un nombre signé 32 bits alors que BIGINTc'est un nombre signé 64 bits.

Remarque

Ma préférence personnelle cependant serait de stocker la date et l'heure sous forme de ISO8601chaîne.

WonderWorker
la source
-1

À mon humble avis, la meilleure solution dépend dans une certaine mesure de la façon dont vous stockez le temps dans le reste de la base de données (et le reste de votre application)

Personnellement, j'ai travaillé avec SQLite et j'essaie de toujours utiliser des horodatages unix pour stocker le temps absolu, donc lorsque je traite de l'heure de la journée (comme vous le demandez) je fais ce que Glen Solsberry écrit dans sa réponse et stocke le nombre de secondes depuis minuit

En adoptant cette approche générale, les gens (y compris moi!) Lisant le code sont moins confus si j'utilise le même standard partout

Sunyata
la source