web-dev-qa-db-fra.com

Méthode préférée pour stocker DateTime

Nous pouvons stocker les informations de date et d'heure de deux manières. Quelle est la meilleure approche pour stocker des informations DateTime?

Stocker la date et l'heure dans 2 colonnes distinctes ou une colonne en utilisant DateTime ?

Pouvez-vous expliquer pourquoi cette approche est meilleure?

(Lien vers les documents MySQL pour référence, la question est générale, non spécifique à MySQL)
Types de date et d'heure: Date et heure

19
Julian

Le stockage des données dans une seule colonne est la méthode préférée, car elles sont inextricablement liées. Un point dans le temps est une seule information, pas deux.

Un moyen courant de stocker des données de date/heure, utilisées "en arrière-plan" par de nombreux produits, est de les convertir en une valeur décimale où la "date" est la partie entière de la valeur décimale et le "temps" est la fraction valeur. Ainsi, 1900-01-01 00:00:00 est stocké en tant que 0.0 et le 20 septembre 2016 9:34:00 est stocké en tant que 42631.39861. 42631 est le nombre de jours depuis le 1900-01-01. .39861 est la partie du temps écoulé depuis minuit. N'utilisez pas de type décimal directement pour cela, utilisez un type de date/heure explicite; mon point ici est juste une illustration.

Le stockage des données dans deux colonnes distinctes signifie que vous devrez combiner les deux valeurs de colonne chaque fois que vous voulez voir si un point donné dans le temps est antérieur ou postérieur à la valeur stockée.

Si vous stockez les valeurs séparément, vous rencontrerez invariablement des "bogues" difficiles à détecter. Prenez par exemple ce qui suit:

IF OBJECT_ID('tempdb..#DT') IS NOT NULL
DROP TABLE #DT;
CREATE TABLE #DT
(
    dt_value DATETIME NOT NULL
    , d_value DATE NOT NULL
    , t_value TIME(0) NOT NULL
);


DECLARE @d DATETIME = '2016-09-20 09:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

SET @d = '2016-09-20 11:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.dt_value >= '2016-07-01 11:00:00';

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.d_value >= CONVERT(DATE, '2016-07-01')
    AND dt.t_value >= CONVERT(TIME(0), '11:00:00');

Dans le code ci-dessus, nous créons une table de test, la remplissons avec deux valeurs, puis effectuons une requête simple sur ces données. Le premier SELECT renvoie les deux lignes, mais le second SELECT ne renvoie qu'une seule ligne, ce qui peut ne pas être le résultat souhaité:

enter image description here

La bonne façon de filtrer une plage de date/heure où les valeurs sont dans des colonnes discrètes, comme indiqué par @ypercube dans les commentaires, est:

WHERE dt.d_value > CONVERT(DATE, '2016-07-01') /* note there is no time component here */
    OR (
        dt.d_value = CONVERT(DATE, '2016-07-01') 
        AND dt.t_value >= CONVERT(TIME(0), '11:00:00')
    )

Si vous avez besoin que la composante temporelle soit séparée à des fins d'analyse , vous pouvez envisager d'ajouter une colonne calculée et persistante pour la partie temporelle de la valeur:

ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;

SELECT *
FROM #dt;

enter image description here

La colonne persistante pourrait alors être indexée, ce qui permet des tri rapides, etc., par heure de la journée.

Si vous envisagez de diviser la date et l'heure en deux champs à des fins d'affichage, vous devez vous rendre compte que le formatage doit être effectué sur le client, pas sur le serveur.

23
Max Vernon

Je vais donner une opinion dissidente aux autres réponses.

Si les composants de date et d'heure sont requis ensemble, c'est-à-dire qu'une entrée n'est pas valide si elle contient l'un mais pas l'autre (ou est NULL dans l'un mais pas l'autre), alors le stockage dans une seule colonne est logique pour les raisons données dans d'autres réponses.

Cependant, il peut arriver qu'un ou les deux composants soient individuellement facultatifs. Dans ce cas, il serait incorrect de le stocker dans une seule colonne. Cela vous obligerait à représenter des valeurs NULL de manière arbitraire, par exemple enregistrer l'heure comme 00:00:00.

Voici quelques exemples:

  • Vous enregistrez les trajets en véhicule pour les déductions fiscales de kilométrage. Il serait utile de connaître l'heure exacte du voyage, mais si un employé ne l'a pas noté et l'a oublié, la date doit toujours être enregistrée par elle-même (date requise, heure facultative).

  • Vous menez une enquête pour savoir à quelle heure les gens mangent leur déjeuner et vous demandez aux participants de remplir un formulaire avec un échantillon de leurs heures de déjeuner, y compris les dates. Certains ne prennent pas la peine de remplir la date, et vous ne voulez pas supprimer les données car ce sont les heures qui vous intéressent vraiment (date facultative, heure requise).

Voir cette question connexe pour des approches alternatives.

11
JBentley

Je préfère toujours le stocker dans une seule colonne, sauf s'il existe une demande spécifique pour l'entreprise/l'application. Voici mes points -

  • Extraire l'heure de l'horodatage n'est pas un problème
  • Pourquoi ajouter une colonne supplémentaire juste pour le temps si nous pouvons stocker les deux ensemble
  • Pour éviter d'ajouter la date et l'heure à chaque fois que vous interrogez.
5
Ashwini Mohan

Dans SQL Server, il est préférable de stocker DataTime dans un seul champ. Si vous créez un index sur la colonne DataTime, il peut être utilisé comme recherche de date et comme recherche de date. Par conséquent, si vous devez limiter tous les enregistrements qui existent pour la date spécifique, vous pouvez toujours utiliser l'index sans rien faire de spécial. Si vous devez interroger la partie de l'heure, vous ne pourrez pas utiliser le même index et, par conséquent, si vous avez une analyse de rentabilisation où vous vous souciez plus de l'heure de la journée que de DateTime, vous devez la stocker séparément car vous devrez créer un index dessus et améliorer les performances.

3
Vladimir Oselsky

En effet, c'est dommage qu'il n'y ait pas de type de SGBD standard pour cela (comme INT et VARCHAR sont pour les entiers et les valeurs de chaîne). Les 2 approches de bases de données croisées que j'ai rencontrées jusqu'à présent utilisent des colonnes VARCHAR/CHAR pour stocker les valeurs DataTime sous forme de chaînes formatées selon la norme ISO 8601 (plus pratique, lisible par l'homme) et utilisent BIGINT pour les stocker en tant qu'horodatages POSIX (stockés plus efficace, plus rapide, plus facile à manipuler mathématiquement).

1
Ivan

Après avoir lu un tas de trucs, l'heure UTC Unix dans BIGINT semble être la solution optimale. TZDB ID de temps dans VARCHAR pour le stockage du fuseau horaire si nécessaire. Quelques arguments:

  1. TIMESTAMP et DATETIME font un tas de conversions gimmicky en arrière-plan qui semblent être complexes et pas claires. Le serveur passe de l'heure locale à l'heure UTC ou à l'heure du serveur et vice-versa, parfois ou non. Un tas de frais généraux cachés pour chaque fonction.

  2. BIGINT (8 Ko) est au moins aussi léger ou plus léger que DECIMAL requis pour le stockage au format xxxxxx.xxxxxx, qui est pratiquement stocké sous la forme de deux INT + quelque chose par MySQL . Et c'est suffisant pour stocker les siècles à venir.

  3. Presque tous les principaux langages de programmation ont des bibliothèques de fonctions standard pour fonctionner avec le temps Unix.

  4. Les opérations mathématiques avec BIGINT devraient être aussi rapides ou plus rapides que toute autre chose sur n'importe quel matériel.

Bien sûr, tout ce qui précède est pertinent pour les grands projets internationaux. Pour quelque chose de petit, aller avec le format par défaut du cadre choisi semble être assez bon.

0
Arthur Tarasov