2010-10-25 18 views
1

J'implémente une base de données où plusieurs tables ont des données de chaîne en tant que clés candidates (ex: nom d'utilisateur) et seront indexées en conséquence. Pour ces domaines, je veux:Est-il fou de contourner les problèmes de sensibilité de la base de données en stockant le cas de la chaîne d'origine et en minuscules?

  1. Insensibilité à la casse quand quelqu'un interroge la table sur les touches

  2. Le cas initialement écrit à préserver une certaine façon afin que l'application puisse présenter les données à l'utilisateur avec l'original cas utilisé

Je veux aussi le schéma de base de données soit comme base de données indépendante que possible, car le code d'application est (ou ne devrait pas être) pas asservies à un SGBDR particulier.

Il convient également de noter que la grande majorité des requêtes effectuées sur la base de données sera effectuée par le code de l'application, et non via l'accès direct à la table par le client. En mettant en œuvre ceci, je rencontre beaucoup de problèmes ennuyeux. La première est que tous les SGBDR n'implémentent pas COLLATE (où la sensibilité des cas semble être ajustable au niveau du schéma) de la même manière. Un autre problème est que les options de classement et de sensibilité à la casse peuvent être définies à plusieurs niveaux (serveur, base de données, table (?), Colonne) et je ne peux pas garantir à l'application quel paramètre elle obtiendra. Encore un autre problème est que COLLATE lui-même peut devenir poilu parce qu'il y a beaucoup plus de choses que la simple sensibilité à la casse (par exemple: les options Unicode).

Pour éviter tous ces maux de tête, ce que je considère est d'esquiver le problème en stockant deux colonnes pour une donnée. Une colonne avec le cas d'origine, une autre est passée en minuscules par la couche d'application.

par exemple: Deux des champs de la table

user_name = "fredflintstone" (a unique index on this one) 
orig_name = "FredFlintstone" (just data... no constraints) 

Les avantages et les inconvénients de ce que je vois sont:

Plus:

  1. Aucune ambiguïté - le code de l'application gérera les conversions de cas et je n'aurai jamais à m'inquiéter de l'échec «mystérieux» des tests unitaires lorsque les SGBDR/paramètres sous-jacents changent.

  2. recherches sur l'indice seront propres et ne jamais être ralenti par des caractéristiques de classement ou les appels à lower() ou quoi que ce soit (en supposant des choses ralentir l'indice, ce qui semble logique)

Moins :

  1. espace de stockage supplémentaire requis pour les données-up doublé

  2. Il semble un peu brutal

Je sais que cela fonctionnera, mais en même temps ça sent mauvais.

Est-ce que c'est fou/inutile de le faire?Y a-t-il quelque chose que je ne connais pas qui rend la question de la sensibilité à la casse moins compliquée qu'elle ne me semble en ce moment?

+0

Vous souhaitez dupliquer les données? Bad ... –

+4

Préférer 'UPPER' sur' LOWER', car certains caractères ne peuvent pas aller-retour dans certaines locales en faisant 'LOWER' http://msdn.microsoft.com/fr-fr/library/bb386042.aspx – bdukes

+0

@bdukes - wow ... thx. Je ne le savais pas. – Russ

Répondre

1

J'ai souvent vu des données dupliquées de cette manière pour des raisons de performances. Il vous permet de conserver le boîtier d'origine (dont vous aurez évidemment besoin car vous n'êtes pas toujours capable de deviner ce que le boîtier doit être, vous ne pouvez pas être sûr que chaque nom commence par une majuscule par exemple). Si la base de données ne supporte pas d'autres moyens de le faire (index fonctionnels), alors c'est pratique, pas fou. Vous pouvez conserver la cohérence des données en utilisant des déclencheurs.

+0

"pratique, pas fou" est exactement comme je le vois. Mais il semble que ce devrait être un problème commun et j'ai le sentiment que quelque chose me manque car je n'ai pas vu cela et je ne vois pas d'exemples autour de ça. – Russ

+0

Eh bien, certaines bases de données ne supportent tout simplement pas les index fonctionnels. Vous aurez très probablement besoin d'indexer des données comme ceci, donc ce sera la seule option. J'ai vu des applications sérieuses utilisant ce genre d'indexation donc je dirais que ce n'est pas fou ou hors de l'ordinaire. Lorsque les index fonctionnels sont supportés (par exemple Oracle et Postgre), l'utilisation de ceux-ci aurait probablement plus de sens. – steinar

1

Suggérer vos requêtes de recherche faire quelque chose comme ceci:

  • SELECT * FROM Users WHERE LOWER(UserName) = LOWER('fredFlinstone')
  • incluent explicitement l'indice COLLATION sur la requête lorsque la casse doit être ignorée/respectée

Je considérerais la la duplication des données pour la sensibilité à la casse est trop onéreuse.

+1

Est-ce que LOWER tue les avantages de l'indexation? – Russ

+1

La plupart des SGBD ne seront pas en mesure d'utiliser un index pour une telle requête, ce qui pourrait poser problème. La solution évidente sont les indices basés sur les fonctions, mais tous les SGBD ne les ont pas. De plus, les problèmes avec différentes implémentations COLLATE restent, comme mentionné dans la question. – sleske

+1

@Russ, dans Oracle, vous pouvez créer un index basé sur une fonction afin que la colonne elle-même indexée avec le Lower() - (http://www.akadia.com/services/ora_function_based_index_2.html) – Harrison

2

Bien sûr, les décisions comme celle-ci sont toujours un compromis, mais je ne pense pas que ce soit nécessairement des «données doublées». La mise en minuscules d'une chaîne peut être une opération non triviale, en particulier si vous allez au-delà de l'ASCII, donc la version en minuscule de la chaîne n'est pas simplement "dupliquée". C'est un peu lié à la chaîne d'origine, mais pas plus que cela.

Si vous considérez cela comme un analogue pour stocker des résultats calculés dans le DB, cela devient plus naturel.

L'option d'interrogation sur UPPER(UserName) est une autre bonne solution, qui évite la deuxième colonne. Cependant, pour l'utiliser, vous avez besoin au moins d'une fonction fiable UPPER (où vous pouvez en particulier contrôler les paramètres régionaux qu'il utilise pour les caractères non-ASCII), et probablement des indices fonctionnels pour des performances décentes.

1

recherches sur l'indice seront propres et ne jamais être ralenti par des caractéristiques de classement ou les appels à lower() ou quoi que ce soit (en supposant des choses ralentissent l'indice, ce qui semble logique)

Non, ce n'est pas logique. Vous pouvez avoir des index sur des fonctions constantes.

create index users_name on users(name); -- index on name 
create index users_name_lower on users(lower(name)); -- index on the function result 

Votre SGBDR devrait être assez intelligent pour savoir utiliser users_name_lower quand il fait cette requête:

select * from users where lower(name) = ? 

Sans users_name_lower, oui, cela aurait à marcher sur la table. Avec l'indice fonctionnel, il fait la bonne chose.