SQLite, FMDatabase, objet NULL, nil, NSNumber, NSUInteger

Désolé pour le titre.
J'ai pour habitude de mettre à Null dans ma DB SQLite des champs de type INT si ceux-ci peuvent ne pas être renseignés. L'autre choix pourrait être de les passer à 0 en default value (Not Null).
J'ai du mal à comprendre comment Cocoa ou SQLiteLib gère les champs NULL sur les entiers. On dirait que dans tous les cas il voit un entier à 0, même si le champs dans la DB est Null.
Du coup, je peux ouvrir un élément avec un status entier à NULL (qui provient de la DB), mais lorsque je l'enregistre, il repart quand-même avec la valeur 0 et me l'enregistre dans la DB... au lieu de laisser le champ Null.
Pourtant juste avant d'enregistrer la requête SQL voilà la valeur du champ en question:
Il est donc bien NIL à ce moment là puisqu'il n'a pas été "touché".
Du coup, je me demande si la transformation en NSNumber nécessaire pour ma requête SQL avec FMDatabase n'en serait pas la cause:
Qu'en pensez-vous ?
EDIT: en analysant mes autres tables, je me suis aperçu qu'il se passait la même chose (cf screenshot avec 3 champs qui devraient être vides et pas à 0). Notez que pour la colonne en rouge (qui est une char) les champs restent bien vides (null) contrairement aux entiers ou aux doubles. Du coup j'ai un doute, est-ce qu'un champ entier ne peut logiquement pas être NULL en SQL ? Mais dans ce cas, pourquoi à la construction de la BDD autoriser la mise en NULL d'un entier ? Ou alors c'est FMDatabase qui force l'enregistrement en entier de valeur 0 s'il est nil...
J'ai pour habitude de mettre à Null dans ma DB SQLite des champs de type INT si ceux-ci peuvent ne pas être renseignés. L'autre choix pourrait être de les passer à 0 en default value (Not Null).
J'ai du mal à comprendre comment Cocoa ou SQLiteLib gère les champs NULL sur les entiers. On dirait que dans tous les cas il voit un entier à 0, même si le champs dans la DB est Null.
Du coup, je peux ouvrir un élément avec un status entier à NULL (qui provient de la DB), mais lorsque je l'enregistre, il repart quand-même avec la valeur 0 et me l'enregistre dans la DB... au lieu de laisser le champ Null.
Pourtant juste avant d'enregistrer la requête SQL voilà la valeur du champ en question:
(gdb) po self._outputType
Can't print the description of a NIL object.
Il est donc bien NIL à ce moment là puisqu'il n'a pas été "touché".
Du coup, je me demande si la transformation en NSNumber nécessaire pour ma requête SQL avec FMDatabase n'en serait pas la cause:
BOOL success = [[[Manager sharedManager] _dataDB] executeUpdate:request, [NSNumber numberWithInt:self._outputType]];
Qu'en pensez-vous ?
EDIT: en analysant mes autres tables, je me suis aperçu qu'il se passait la même chose (cf screenshot avec 3 champs qui devraient être vides et pas à 0). Notez que pour la colonne en rouge (qui est une char) les champs restent bien vides (null) contrairement aux entiers ou aux doubles. Du coup j'ai un doute, est-ce qu'un champ entier ne peut logiquement pas être NULL en SQL ? Mais dans ce cas, pourquoi à la construction de la BDD autoriser la mise en NULL d'un entier ? Ou alors c'est FMDatabase qui force l'enregistrement en entier de valeur 0 s'il est nil...
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
nil c'est juste 0, interprété selon le type id, comme NULL vaut zéro, interprété sous forme de void*.
Du coup évidemment [tt][NSNumber numberWithInt:nil][/tt], [tt][NSNumber numberWithInt:NULL][/tt] et [tt][NSNumber numberWithInt:0][/tt] sont tous trois équivalents et retournent un NSNumber encapsulent un entier de valeur 0.
Je n'utilise pas ta lib sqlite (mais directement l'API C) mais pour passer null, soit l'API accepte que tu passes nil dans le paramètre, soit tu dois sans doute devoir utiliser [tt][NSNull null][/tt].
Je pense à un truc comme ça :
Pourquoi pas mettre une valeur négative pour signaler une valeur non défini.
Très très mauvaise habitude que de donner des significations particulières à des valeurs dans une base de données. Very bad practice !
En plus en pratique ça veut dire que tu introduits des cas particuliers pour certaines valeurs et pas pour d'autres d'après leur valeur au lieu d'utiliser le comportement standard et les possibilités prévues par NULL. Meilleur moyen de dériver et de finir avec du grand n'importe quoi.
Je sais que je peux passer nil dans les requêtes, qui seront transformés en NULL dans la DB, ça je sais que ça passe avec FMDatabase.
Par contre, ça c'est super lourd:
Car j'ai X champs fois X tables...
En gros ça se présente comme ça (imagine sur tous les NSNumber...):
Donc, du coup, je me demande si je devrais pas mettre tout simplement 0 en default value dans la BDD...
Qu'en penses-tu ? Qu'est-ce qui est plus logique ?
J'ai pris l'exemple des integer, mais c'est la même chose avec les double.
Et quand je n'ai pas de prix renseigné dans ma BD, je ne vois pas pourquoi j'y verrais 0 à la place de NULL.
Tu vas te priver de mettre du code correct juste parce que c'est chiant à taper ? Tu sais les macros c'est fait pour ça hein
Ceci dit je ne t'ai pas demandé mais y'a un truc qui m'intrigue : quel est le type de tes variables retournées par [tt]self._outputType[/tt] et consorts ?
Si c'est des [tt]int[/tt] y'a un pb de conception, puisque tu ne pourras pas alors faire la différence entre "nil" (valeur absente) et la valeur 0... du coup c'est pas logique d'utiliser des int si tu veux que ça puisse te retourner à la fois 0 dans certains cas et nil dans d'autres, un int ne pouvant pas être nil... et du coup faut peut-être directement que tes getters avec leur drôle de nom (jamais vu nulle part cette convention de nommage de mettre un underscore en début des... propriétés, berk) retournent des NSNumber (qui peuvent être nil ou encapsuler la valeur 0, ce sont 2 choses différentes là où pour un int 0 et nil c'est pareil vu qu'en fait nil n'a pas de sens pour un int) ?
D'ailleurs, je viens de tester:
Ce qui me donne 0 dans les 2 cas... cqfd
Donc, tu me conseilles de passer tous les int de mes modèles en NSNumber ? Du moins tous ceux qui sont à NULL dans la BDD ?
Travail fastidieux mais bon si je dois y passer...
Il y a une nouvelle méthode qui renvoie directement des objets de la base (NSNumber, NSString...), de la base... (objectForColumnName)
Si l'objet en base est NULL, il renvoie actuellement [NSNull null]... mais je peux le changer...
Etrangement la méthode qui renvoie une string renvoie nil et non [NSNull null] si l'objet est vide...
Donc je pense harmoniser tout ça à nil, plutôt que [NSNull null].
ça me permettrait de faire, pour un champ data:
plutôt que
Cela vous semble logique de représenter l'objet NULL SQL par nil plutôt que par [NSNull null] en cocoa ?
L'autre avantage du NSNumber et de la méthode objectForColumnName, c'est que je n'ai pas besoin de faire :
si l'objet n'est pas touché avant l'enregistrement, il restera à NULL en BDD et ne passera plus à zéro.
1) Comment font SQLite ou MySQL pour gérer des INTEGER à NULL dans ce cas ?
2) Tu me conseilles de renvoyer [NSNull null] ou nil quand la BDD renvoie NULL ?
2) [tt][NSNull null][/tt] est en pure théorie plus cohérent pour manipuler "la valeur représentant NULL en base", et habituellement quel que soit le langage, il y a souvent un objet spécifique pour ça, qui diffère de la valeur représentant une absence d'objet.
Mais c'est aussi un peu chiant à manipuler de manipuler des NSNull au lieu de manipuler juste des nil.
Donc franchement ça me choque pas du tout au final d'avoir la valeur "nil" en retour quand en base la colonne vaut NULL.
Ce qui me choquerait c'est de manipuler ça avec type primitif non-nullable, donc pour qui la valeur NULL n'existe pas (et que la valeur 0 représente déjà qqch de différent d'un NULL en base).
Je pense harmoniser tous les retours NULL à nil.
Le seul avantage de [NSNull null], c'est que ça peut être pratique pour remplir un dico juste après une requête, si l'objet récupéré est NULL.