8
votes

SQL Server Convertir datetime en date sans null

J'ai une procédure stockée comme celle-ci:

select 
    TransactionDate 
from 
    (select 
         isnull(cast(TransactionDate as Date), getdate()) as TransactionDate
     from 
         RetailTransaction) t

Cependant, cela rend la colonne TransactionDate de la sélection externe nullable, tandis que RetailTransaction.TransactionDate n'est pas nulle.

RetailTransaction.TransactionDate definition / design:

 entrez la description de l'image ici p>

Sélection interne:

 Sélection interne

Sélection externe:

 Sélection externe a>

Même après avoir ajouté isnull ou coalesce SQL Server / SSMS montre toujours que la colonne de sélection externe TransactionDate est toujours Nullable.

select 
    TransactionDate 
from 
    (select 
         cast(TransactionDate as Date) as TransactionDate
     from RetailTransaction) t

 Exemple 2

Comment rendre la colonne TransactionDate non nullable?

Notez que la base de données est sur Azure avec le niveau de compatibilité 100 (SQL Server 2008).

EDIT:

L'ajout d'un isnull sur la sélection externe rend toujours la colonne nullable à une requête imbriquée externe:

 external-nested-query


13 commentaires

Qu'entendez-vous par la colonne est toujours "nullable"?


@TimBiegeleisen Voir les info-bulles, le type de données de cette colonne est (date, null) , alors qu'il devrait être (date, not null) . Comparez l'exemple Inner Select avec l'exemple Outer Select


Vous contrôlez cela à partir de la définition de votre table. Pouvez-vous ajouter cela?


La colonne TransactionDate dans le tableau RetailTransaction est (datetime, not null) . L'ensemble de données résultant après conversion en date est (date, null)


@TimBiegeleisen a mis à jour la question


Donc, je n'ai pas de réponses concrètes ici, mais juste des suppositions sur le fonctionnement de l'intellisense et pourquoi cela n'a pas réellement d'importance. Intellisense pourrait être conçu pour supposer automatiquement que le résultat de toute fonction (par exemple CAST, CONVERT, ISNULL) pourrait être NULL (car il pourrait ne pas être conçu pour déterminer si une fonction peut être nulle en fonction de contributions). Ceci, cependant, peut ne pas refléter réellement ce qui se passe dans la réalité. Si le résultat ne peut jamais être nul, peu importe ce que dit intellisense à ce sujet.


@ZLK La raison pour laquelle il s'agit d'un problème est que je consomme cette procédure stockée en utilisant EntityFramework (C #) et qu'il détecte que cette colonne est nullable, rendant mes types nullables. Je pense avoir trouvé une solution de contournement dans laquelle je fais un Insertion dans @temptable avec TransactionDate défini comme (date, non null) , puis je le sélectionne.


C'est probablement la solution la plus simple. Les alternatives seraient quelque chose comme changer manuellement le type de données en C # ou j'imagine ajouter une colonne calculée persistante à votre table qui convertit le datetime en date et n'est déjà pas nul (ce qui peut vous être utile si vous vous retrouvez régulièrement à le lancer comme une date en tous cas).


Cela peut être une question idiote, mais si la colonne est non nullable en premier lieu, pourquoi ajoutez-vous isnull ou coalesce ? s'il s'agit d'une valeur DateTime qui n'est pas nullable, la conversion en Date ne retournera jamais null, donc isnull / coalesce est redondant.


Pourquoi lancez-vous la colonne datetime? Est-ce pour supprimer des horodatages?


@MatsMagnem Oui, mais j'ai besoin qu'il soit fortement typé


@ZoharPeled Le CAST transforme la colonne en (datetime, null) et j'ai besoin que la colonne soit (datetime, not null)


Je vois. J'ai appris quelque chose de nouveau aujourd'hui, merci!


3 Réponses :


3
votes

Oui, la raison pour laquelle il affiche Nullable pour une colonne non nullable est que vous faites votre sélection externe comme calculée en fonction d'une condition isnull et d'autres.

Dans ce tableau, l'e-mail n'est pas une colonne nullable et maintenant si je donne la condition Isnull dans la requête interne, ce sera comme ça.

 entrez la description de l'image ici

Maintenant, si je ne donne aucune condition, ce sera comme ça.

 entrez la description de l'image ici

Vous pouvez également vous référer à ce lien pour des explications détaillées.

https: / /dba.stackexchange.com/questions/114260/why-is-a-not-null-computed-column-consemed-nullable-in-a-view


1 commentaires

Je ne pense pas que cette réponse soit exacte ... Et le lien que vous avez fourni dit explicitement "Une expression qui peut être Nullable peut être transformée en une expression qui ne peut être nulle en spécifiant ISNULL (check_expression, constant), où la constante est une valeur non nulle remplacé par tout résultat nul. " donc cela ne résout pas cela ...



0
votes

L'astuce consiste à mettre ISNULL sur la requête externe et à ajouter un alias pour ne pas perdre le nom

select 
    isnull(TransactionDate, GETDATE()) as TransactionDate
from ( 
    select 
        Cast(TransactionDate as Date) as TransactionDate
    from RetailTransaction
) t

De cette façon SQL se rendra compte que le champ ne peut pas être nul. Cela devrait bien informer tout ORM que ce champ ne doit pas être mappé à un type Nullable.


3 commentaires

Veuillez consulter la question mise à jour. Cette solution ne fonctionne pas pour moi.


@Minijack est-ce une colonne calculée?


Non. TransactionDate est datetime non nul



1
votes

Après avoir lu votre commentaire, j'ai fait quelques recherches et tests, et même si je n'ai pas trouvé de documentation officielle à ce sujet, vous avez raison et le casting à ce jour rend la valeur nullable, même si le datetime ne l'est pas. J'ai même essayé une méthode différente en utilisant datetimefromparts pour créer un type de données de date, mais cela modifie également la possibilité de valeur nulle du résultat.
Donc, d'après moi, vous avez deux options:

La première option est d'ajouter une colonne calculée persistante à la table qui contiendra la valeur de date de la date de transaction. Elle doit être persistante pour ne pas être nullable:

SELECT TransactionDate 
FROM  @Target

et votre requête ressemble à ceci:

DECLARE @Target AS TABLE
(
    TransactionDate Date NOT NULL
)
INSERT INTO @Target(TransactionDate)
SELECT CAST(TransactionDate as Date) 
FROM RetailTransaction) t

L'autre L'option serait de déclarer une variable de table avec une colonne de date non Nullable, de sélectionner les résultats de la distribution dans celle-ci, puis de sélectionner dans la variable de table:

SELECT TransactionDateOnly as TransactionDate
FROM RetailTransaction

Et puis votre sélection ressemble à ceci:

ALTER TABLE RetailTransaction
    ADD TransactionDateOnly AS CAST(TransactionDate As Date) PERSISTED NOT NULL;

Les deux méthodes vous permettront d'obtenir une date non nullable en conséquence, mais je pense que l'option de colonne calculée persistante devrait probablement donner de meilleures performances sur la sélection, car il ne nécessite pas cette insertion supplémentaire ... sélectionnez.


3 commentaires

Merci pour votre réponse réfléchie avec des solutions concrètes.


J'ai décidé de suivre votre méthode pour placer mon jeu de résultats dans une table temporaire, puis de le renvoyer.


Heureux de vous aider :-)