2009-11-24 10 views
0

J'ai une base de données MS SQL 2008 qui stocke des données pour créer un graphe pondéré non orienté. Les données sont stockées dans des tables avec la structure suivante:Instruction SQL avec noms de tables dynamiques ou redesign?

[id1] [int] NOT NULL, 
[id2] [int] NOT NULL, 
[weight] [float] NOT NULL 

où [id1] et [ID2] représente les deux noeuds connectés et [poids] le poids de l'arête qui relie ces noeuds.

Il existe plusieurs algorithmes différents qui créent le graphique à partir de certaines données de base. Pour chaque algorithme, je veux stocker les données graphiques dans un tableau séparé. Ces tables ont toutes la même structure (comme montré ci-dessus) et utilisent un préfixe spécifié (similarityALB, similaritybyArticle, similaritybyCategory, ...) pour que je puisse les identifier comme des tables de graphes.

Le programme client peut sélectionner, quelle table (c'est-à-dire par quel algorithme le graphique est créé) à utiliser pour les opérations ultérieures.

L'accès aux données est effectué par des procédures stockées. Comme je l'ai tables différentes, je besoin d'utiliser un tablename variables .: par exemple

SELECT id1, id2, weight FROM @tableName 

Cela ne fonctionne pas parce que SQL ne supporte pas les noms de tables variables dans la déclaration. Je l'ai cherché sur le web et toutes les solutions à ce problème utilisez l'instruction dynamique EXEC SQL() par exemple:

EXEC('SELECT id1, id2, weight FROM ' + @tableName) 

Comme la plupart d'entre eux ont mentionné, cela fait la déclaration sujette à injection SQL, que je voudrais éviter. Une idée simple de refonte serait de mettre tous les différents graphiques dans un tableau et d'ajouter une colonne pour identifier les différents graphiques.

[graphId] [int] NOT NULL, 
[id1] [int] NOT NULL, 
[id2] [int] NOT NULL, 
[weight] [float] NOT NULL 

Mon problème avec cette solution est que les graphiques peuvent être très importantes en fonction de l'algorithme utilisé (jusqu'à 500 millions d'entrées). J'ai besoin d'indexer la table sur (id1, id2) et (id2, id1). Maintenant les mettre tous dans une grande table rendrait la table encore plus lourde (et demande plus lentement). L'ajout d'un nouveau graphique entraînerait de mauvaises performances, à cause des indices actifs. La suppression d'un graphique ne peut pas être fait par TRUNCATE plus, je besoin d'utiliser

DELETE * FROM myTable WHERE [email protected] 

qui fonctionne très mal avec de grandes tables et crée un fichier journal très grand (qui dépasse mon espace disque lorsque le graphique est assez grand) . Donc, je voudrais garder les tables indépendantes pour chaque graphique.

Des suggestions comment résoudre ce problème soit trouver un moyen de paramétrer le nom de table ou de re-concevoir la structure de la base de données tout en évitant les problèmes mentionnés ci-dessus?

Répondre

1

L'injection SQL peut facilement être évitée dans ce cas en comparant @tableName aux noms des tables existantes. Si ce n'est pas l'un d'entre eux, c'est une mauvaise entrée. (Référence xkcd obligatoire: sauf si vous avez une table appelée "bobby'; drop table students;")

Quoi qu'il en soit, en ce qui concerne vos problèmes de performance, avec les tables partitionnées (depuis SQLServer 2005), vous pouvez avoir les mêmes avantages que d'avoir plusieurs tables, mais sans les besoin de SQL dynamique.

+0

J'aime la table n Ame vérifier plus, pense que je vais aller de cette façon. J'ai jeté un coup d'oeil aux tables partitionnées - un problème serait que je ne peux pas utiliser truncate pour supprimer des graphes entiers (comme IronGoofy mentionné). De plus, j'examinerai comment utiliser sp_executesql, car il supporte la substitution de paramètres et les plans d'exécution (pour les réutiliser). – Aaginor

+0

Dans Oracle, vous pouvez tronquer une partition unique dans une table partitionnée (ALTER TABLE foo TRUNCATE PARTITION;). Dans SQL-Server, vous devez déplacer les données de la partition dans une nouvelle table, que vous pouvez ensuite supprimer ... bizarre ... –

0

Partioned Table peut être la réponse à votre problème.J'ai une autre idée, qui est « l'inverse »:

  • chaque graphique a son propre tableau (vous pouvez tronquer encore le tableau)
  • définir une vue (avec le structuré vous avez mentionné votre redéfini table) comme UNION ALL sur tous les tableaux-graphiques

Je n'ai aucune idée des performances d'un select sur cette vue et ainsi de suite, mais cela peut vous donner ce que vous cherchez. Je serais intéressé par les résultats si essayer cela ..

+0

Je suppose qu'il UNIONS les tables d'abord (et a donc des performances moins bonnes), mais je vais vérifier cela. – Aaginor

+0

Je l'ai vérifié: La première requête sur la vue a pris à peu près le temps nécessaire pour interroger toutes les tables. Tous les appels répétés à cette requête ont pris à peu près le même temps que nécessaire pour interroger sur une seule table. (J'ai fait la vue avec UNION sur 5 tables) Donc c'est seulement le premier appel, mais ce n'est pas encore bon, je pense – Aaginor

1

Peut-être que je ne comprenais pas tout, mais:

CREATE PROCEDURE dbo.GetMyData (
    @TableName AS varchar(50) 
    ) 
AS 
BEGIN 
    IF @TableName = 'Table_1' 
     BEGIN 
      SELECT id1 
        ,id2 
        ,[weight] 
      FROM dbo.Table_1 
     END 

    IF @TableName = 'Table_2' 
     BEGIN 
      SELECT id1 
        ,id2 
        ,[weight] 
      FROM dbo.Table_2 
     END 
END 

puis:

EXEC dbo.GetMyData @TableName = 'Table_1' 

Une autre technique implique en utilisant des synonymes dynamiquement, par exemple:

DECLARE @TableName varchar(50) 
SET @TableName = 'Table_1' 

-- drop synonym if it exists 
IF object_id('dbo.MyCurrentTable', 'SN') IS NOT NULL 
    DROP SYNONYM MyCurrentTable ; 

-- create synonym for the current table 
IF @TableName = 'Table_1' 
    CREATE SYNONYM dbo.MyCurrentTable FOR dbo.Table_1 ; 

IF @TableName = 'Table_2' 
    CREATE SYNONYM dbo.MyCurrentTable FOR dbo.Table_2 ; 

-- use synonym 
SELECT id1, id2, [weight] 
FROM dbo.MyCurrentTable 
+0

J'utilise déjà le IF (@ Table = 'mytable1') ... .) construire dans mes procédures stockées, mais je voulais m'en éloigner. Chaque fois que j'ajoute, renomme ou supprime une nouvelle table, j'ai besoin de mettre à jour TOUS mes SP. Mais ce synonyme-chose a l'air intéressant! Est-il possible de créer une procédure ou une fonction unique qui crée le synonyme, qui est ensuite appelé à partir de mes procs stockés, afin qu'ils puissent utiliser le synonyme? Ensuite, je n'aurais besoin de mettre à jour ONE proc/fonction lorsque j'ajouter/mettre à jour/supprimer une table! – Aaginor

+0

Oui synonyme est un objet db, c'est un nom alternatif pour un autre objet db. –

+0

J'ai implémenté la Synonym-Object-Setting-Stored-Procedure et ça a bien fonctionné ... jusqu'à ce que je commence à accéder aux SP en même temps. On dirait que j'ai besoin de définir un synonyme spécial pour chaque procédure-appel. – Aaginor

Questions connexes