Je travaille avec les programmeurs sur une solution de base de données. Ils veulent ajouter une colonne calculée pour imiter les anciennes clés des requêtes, procédures et systèmes plus anciens et l'indexer. Les nouvelles clés seront GUIDS.
Pour ce faire, ils souhaitent créer une fonction pour la colonne calculée qui crée une valeur et la conserver. Il ne les laissera pas persister dans la colonne. Je n'ai pas de flou chaleureux sur l'idée et je ne trouve pas non plus d'informations sur la technique sur le web (est-ce une technique?).
Je pense qu'ils doivent ajouter un déclencheur à la place. Quelqu'un a-t-il une idée?
La fonction sera exécutée comme suit:
(SELECT [INT Identity field] FROM TABLE WHERE [GUID COLUMN] = @GUIDKEY
Il renvoie un champ d'identité INT basé sur le GUID.
Celui-ci sera exécuté à chaque insertion dans une table associée. SO si Table One contient la clé primaire, la table associée Two sera mise à jour (en utilisant le GUID passé) pour obtenir la clé de la Table one et insérer dans le tableau deux.
Je ne comprends toujours pas pourquoi cela doit être un colonne dans un tablea, tant pis pour celui qui persiste.
Pourquoi ne pas simplement créer une fonction table que vous croisez appliquer lorsque (et seulement lorsque) la requête en a réellement besoin? Étant donné que l'ancienne clé ne changera jamais, elle n'a pas besoin d'être calculée ou persistée de toute façon.
Si vous voulez vraiment que l'ancienne clé vive à plusieurs endroits (et cela ressemble à des gens qui ne devraient pas prendre ce genre de décision ont déjà pris ce genre de décision), faites simplement la recherche dans un déclencheur et remplissez-la au moment de l'écriture . Ensuite, c'est juste une colonne statique dans un tableau.
Je recommande toujours fortement une fonction table pour faciliter cela, afin que vous puissiez écrire le déclencheur de manière à ce qu'il gère les opérations sur plusieurs lignes ... sans avoir à écrire une boucle, ou appeler une fonction à valeur scalaire et encore une fois pour chaque ligne.
Juste pour montrer à quel point ces choses sont similaires (et interrogez votre développeur principal qui "ne l'aime pas"):
-- bad, slow, painful row-by-row
CREATE FUNCTION dbo.GetIDByGUID
(
@GuidKey uniqueidentifier
)
RETURNS int
AS
BEGIN
RETURN (SELECT $IDENTITY FROM dbo.tablename WHERE guid_column = @GuidKey);
END
GO
-- in the trigger:
UPDATE r SET oldkey = dbo.GetIDByGUID(i.guid_column)
FROM dbo.related AS r
INNER JOIN inserted AS i
ON r.guid_column = i.guid_column;
Maintenant, si vous avez une fonction table, le code est assez similaire, mais vous constaterez que les performances sont bien meilleures dans les opérations sur plusieurs lignes et presque identiques pour les opérations sur une seule ligne.
-- ah, much better
ALTER FUNCTION dbo.GetIDByGUID_TVF
(
@GuidKey uniqueidentifier
)
RETURNS TABLE
AS
RETURN (SELECT id = $IDENTITY FROM dbo.tablename WHERE guid_column = @GuidKey);
GO
-- in the trigger:
UPDATE r SET oldkey = f.id
FROM dbo.related AS r
INNER JOIN inserted AS i
ON r.guid_column = i.guid_column
CROSS APPLY dbo.GetIDByGUID_TVF(i.guid_column) AS f;
Je ne sais pas pourquoi vous pensez avoir besoin d'une fonction ou d'une colonne calculée pour ce faire. Vous pouvez simplement ajouter une nouvelle colonne à la table avec une valeur par défaut et l'indexer comme vous le souhaitez.
CREATE TABLE dbo.whatever ( Id INT );
ALTER TABLE dbo.whatever
ADD YourMom UNIQUEIDENTIFIER
DEFAULT NEWSEQUENTIALID();
CREATE INDEX ix_whatever ON dbo.whatever (YourMom);
Depuis que vous avez mis à jour votre question, examinons ce qu'est vraiment une idée horrible. Je vais simplifier un peu l'exemple.
CREATE TABLE dbo.whatever ( Id INT PRIMARY KEY);
CREATE TABLE dbo.ennui ( Id INT PRIMARY KEY, meh INT );
GO
CREATE FUNCTION dbo.BadIdea ( @notguido INT )
RETURNS INT
WITH SCHEMABINDING, RETURNS NULL ON NULL INPUT
AS
BEGIN
DECLARE @out INT;
SELECT @out = ( SELECT e.Id FROM dbo.ennui AS e WHERE e.meh = @notguido );
RETURN @out;
END;
GO
ALTER TABLE dbo.whatever ADD ishygddt AS dbo.BadIdea(Id)
/*Will fail*/
ALTER TABLE dbo.whatever ALTER COLUMN ishygddt ADD PERSISTED;
/*Will fail*/
CREATE INDEX ix_whatever ON dbo.whatever (ishygddt);
Essayer de persister une colonne calculée basée sur une fonction scalaire (même déterministe avec LIAISON DE SCHÉMA ) échouera si elle effectue un accès aux données.
Msg 4934, niveau 16, état 3, ligne 23 La colonne calculée "ishygddt" dans la table "quel que soit" ne peut pas être conservée car la colonne a un accès aux données utilisateur ou système.
Vous ne pouvez pas non plus l'indexer:
Msg 2709, niveau 16, état 1, ligne 25 La colonne 'ishygddt' de la table 'dbo.wwhat' ne peut pas être utilisée dans un index ou des statistiques ou comme clé de partition car elle permet l'accès aux données utilisateur ou système.
Vous rencontrerez également de nombreux problèmes car la fonction s'exécutera ligne par ligne pour récupérer les données et forcera toutes les requêtes sur la table à s'exécuter en série .
Si vous modifiez des données dans la table référencée par la fonction et sélectionnez des données dans la table qui appelle la fonction dans la colonne calculée, vous pouvez vous retrouver avec des scénarios de blocage vraiment déroutants dans lesquels la fonction ne peut pas renvoyer de données à une requête sur une table apparemment sans rapport.
C'est une mauvaise idée tout autour. Aaron a donné ce que je pense être le meilleur conseil dans son commentaire:
Pourquoi ne pas simplement créer une fonction table que vous croisez appliquer lorsque (et seulement lorsque) la requête en a réellement besoin? Étant donné que l'ancienne clé ne changera jamais, elle n'a pas besoin d'être calculée ou persistée de toute façon. Si vous voulez vraiment que l'ancienne clé vive à plusieurs endroits, faites la recherche dans un déclencheur.
Vous pouvez lire sur Colonnes calculées persistantes dans BOL , et les Index sur les colonnes calculées .
Il existe des restrictions sur l'expression que vous pouvez utiliser dans une colonne calculée persistante. L'expression "doit être déterministe lorsque PERSISTED est spécifié."
Si je comprends bien, vous avez:
t
.t
a une colonne guid, appelons-la gc
.t.gc
à une valeur de clé différente d'un type différent, qui est utilisée dans le code hérité. Appelons la table lt
et la colonne de clé héritée lk
.lt.lk
pour apparaître dans t
en tant que t.lk
afin que le code hérité puisse continuer à l'utiliser.J'enquêterais en utilisant une vue.
t
, quelque chose comme t_base
.t
qui rejoint t_base
à lt
et renvoie la colonne de t_base
et lt.lk
.Le problème que les développeurs tentent de résoudre est un problème de migration assez standard, d'un type de données à un nouveau. Ils ont un nouveau problème qui n'était pas prévu au moment où la base de données a été initialement conçue; à savoir, ils doivent maintenant synchroniser les données entre les bases de données. Cela a tendance à être une entreprise coûteuse, en particulier si les types de données sont utilisés dans de nombreux codes. La recherche de mesures d'économie n'est pas totalement déraisonnable.
Tout d'abord, il est important de réaliser que le problème est probablement mathématiquement insoluble. Un GUID ou UUID est simplement un nombre de 128 bits ou 16 octets. La taille d'une colonne IDENTITY
dépend du type de données utilisé, mais elles sont généralement INT
(32 bits, 4 octets); parfois BIGINT
(64 bits, 8 octets) est utilisé. Il n'existe aucun moyen mathématique de mapper complètement la plage des GUID à la plage des INT ou même des BIGINT. C'est mathématiquement possible si la colonne est DECIMAL(38,0)
, ce qui est assez grand, mais c'est très rare.
Même s'il est possible de mapper des GUID au type DECIMAL
, cela ne signifie pas que ce soit pratique. Pratiquement personne ne fait cela, vous devrez donc passer du temps (= argent) à vous assurer que le mappage fonctionne correctement. Leur solution présente un risque non négligeable de créer des bugs étranges et difficiles à diagnostiquer.
De plus, vous n'allez pas conserver les ID existants des données avec leur solution. Cela pourrait bien briser tous les signets que les utilisateurs finaux ont, y compris les ID.
Enfin, leur solution est susceptible d'être enracinée . Ce n'est pas une bonne pratique pour toutes les raisons ci-dessus, mais s'ils l'obtiennent, il est probable qu'ils ne travailleront pas à s'en éloigner de sitôt car il est trop "facile" de continuer à utiliser les clés entières dans toutes les nouvelles code.
Une approche relativement standard pour introduire la synchronisation avec un système existant consiste à ajouter un nouveau ID unique . Ce nouvel ID est placé dans les données en plus des anciennes clés existantes. Les clés de substitution ne sont alors pas synchronisées.
Cela présente des avantages majeurs:
Il y a deux ennuis mineurs avec cette approche:
Ces deux problèmes sont gérables, cependant, et ce sont des compromis raisonnables à faire si le basculement de tout vers les GUID est actuellement trop cher.
Il convient également de noter que cette solution a activé exactement la requête qu'ils demandent d'être en mesure de faire.
C'est peut-être trop tard pour vous aider maintenant, mais je pense que ce sont de bonnes informations pour l'avenir.